From f4f8dd768c1001959f12f5bd463907f483210376 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Wed, 20 Sep 2017 16:36:52 -0700 Subject: including header files in code to get extern C effect --- src/core/lib/support/string.c | 1 + src/core/lib/support/string_posix.c | 1 + src/core/lib/support/string_windows.c | 1 + 3 files changed, 3 insertions(+) (limited to 'src/core/lib/support') diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c index 6b172df82f..d55863892f 100644 --- a/src/core/lib/support/string.c +++ b/src/core/lib/support/string.c @@ -27,6 +27,7 @@ #include #include #include +#include #include char *gpr_strdup(const char *src) { diff --git a/src/core/lib/support/string_posix.c b/src/core/lib/support/string_posix.c index e768faf739..92de21a6e1 100644 --- a/src/core/lib/support/string_posix.c +++ b/src/core/lib/support/string_posix.c @@ -25,6 +25,7 @@ #include #include +#include int gpr_asprintf(char **strp, const char *format, ...) { va_list args; diff --git a/src/core/lib/support/string_windows.c b/src/core/lib/support/string_windows.c index 50278d9559..bae524d7cb 100644 --- a/src/core/lib/support/string_windows.c +++ b/src/core/lib/support/string_windows.c @@ -27,6 +27,7 @@ #include #include +#include #include "src/core/lib/support/string.h" -- cgit v1.2.3 From ac0c2d9036814b56a3f8d99d6f8cbafeed671eca Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Wed, 20 Sep 2017 16:54:00 -0700 Subject: Adding cplusplus ifdefs for C linkage in header files --- src/core/lib/support/arena.h | 8 ++++++++ src/core/lib/support/backoff.h | 8 ++++++++ src/core/lib/support/mpscq.h | 8 ++++++++ src/core/lib/support/murmur_hash.h | 8 ++++++++ src/core/lib/support/stack_lockfree.h | 8 ++++++++ src/core/lib/support/string_windows.h | 8 ++++++++ src/core/lib/support/time_precise.h | 8 ++++++++ 7 files changed, 56 insertions(+) (limited to 'src/core/lib/support') diff --git a/src/core/lib/support/arena.h b/src/core/lib/support/arena.h index 47f0e4d16b..8a50786348 100644 --- a/src/core/lib/support/arena.h +++ b/src/core/lib/support/arena.h @@ -27,6 +27,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct gpr_arena gpr_arena; // Create an arena, with \a initial_size bytes in the first allocated buffer @@ -36,4 +40,8 @@ void *gpr_arena_alloc(gpr_arena *arena, size_t size); // Destroy an arena, returning the total number of bytes allocated size_t gpr_arena_destroy(gpr_arena *arena); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_ARENA_H */ diff --git a/src/core/lib/support/backoff.h b/src/core/lib/support/backoff.h index 6e0cc3a4b6..31ec28f666 100644 --- a/src/core/lib/support/backoff.h +++ b/src/core/lib/support/backoff.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { /// const: how long to wait after the first failure before retrying int64_t initial_connect_timeout; @@ -53,4 +57,8 @@ gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now); /// instead void gpr_backoff_reset(gpr_backoff *backoff); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_BACKOFF_H */ diff --git a/src/core/lib/support/mpscq.h b/src/core/lib/support/mpscq.h index daa51768f7..ca63a044bb 100644 --- a/src/core/lib/support/mpscq.h +++ b/src/core/lib/support/mpscq.h @@ -23,6 +23,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + // Multiple-producer single-consumer lock free queue, based upon the // implementation from Dmitry Vyukov here: // http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue @@ -50,4 +54,8 @@ gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q); // Pop a node; sets *empty to true if the queue is empty, or false if it is not gpr_mpscq_node *gpr_mpscq_pop_and_check_end(gpr_mpscq *q, bool *empty); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_MPSCQ_H */ diff --git a/src/core/lib/support/murmur_hash.h b/src/core/lib/support/murmur_hash.h index 7510b4d09c..a4c642e49f 100644 --- a/src/core/lib/support/murmur_hash.h +++ b/src/core/lib/support/murmur_hash.h @@ -23,7 +23,15 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* compute the hash of key (length len) */ uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_MURMUR_HASH_H */ diff --git a/src/core/lib/support/stack_lockfree.h b/src/core/lib/support/stack_lockfree.h index 6324211b72..706f63fbf6 100644 --- a/src/core/lib/support/stack_lockfree.h +++ b/src/core/lib/support/stack_lockfree.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct gpr_stack_lockfree gpr_stack_lockfree; /* This stack must specify the maximum number of entries to track. @@ -35,4 +39,8 @@ int gpr_stack_lockfree_push(gpr_stack_lockfree *, int entry); /* Returns -1 on empty or the actual entry number */ int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_STACK_LOCKFREE_H */ diff --git a/src/core/lib/support/string_windows.h b/src/core/lib/support/string_windows.h index 7c7f31e7aa..6771647581 100644 --- a/src/core/lib/support/string_windows.h +++ b/src/core/lib/support/string_windows.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + #ifdef GPR_WINDOWS /* These allocate new strings using gpr_malloc to convert from and to utf-8. */ @@ -29,4 +33,8 @@ LPSTR gpr_tchar_to_char(LPCTSTR input); #endif /* GPR_WINDOWS */ +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_STRING_WINDOWS_H */ diff --git a/src/core/lib/support/time_precise.h b/src/core/lib/support/time_precise.h index aa28d6d7c4..cb15cdf919 100644 --- a/src/core/lib/support/time_precise.h +++ b/src/core/lib/support/time_precise.h @@ -21,7 +21,15 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + void gpr_precise_clock_init(void); void gpr_precise_clock_now(gpr_timespec *clk); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SUPPORT_TIME_PRECISE_H */ -- cgit v1.2.3 From a7e6d65a4864422854117149a6e98ff80e492fa9 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Wed, 20 Sep 2017 18:56:37 -0700 Subject: Changes for C to C++. Adding extern C to header files for compatibility. --- src/core/ext/census/base_resources.h | 10 +++++++++- src/core/ext/census/census_interface.h | 10 +++++++++- src/core/ext/census/census_log.h | 10 +++++++++- src/core/ext/census/hash_table.h | 10 +++++++++- src/core/ext/census/intrusive_hash_map.h | 8 ++++++++ src/core/ext/census/mlog.h | 10 +++++++++- src/core/ext/census/resource.h | 10 +++++++++- src/core/ext/census/trace_context.h | 10 +++++++++- src/core/ext/census/trace_propagation.h | 10 +++++++++- src/core/ext/census/tracing.h | 10 +++++++++- src/core/ext/census/window_stats.h | 10 +++++++++- src/core/ext/filters/client_channel/client_channel.h | 10 +++++++++- src/core/ext/filters/client_channel/client_channel_factory.h | 10 +++++++++- src/core/ext/filters/client_channel/connector.h | 10 +++++++++- src/core/ext/filters/client_channel/http_connect_handshaker.h | 10 +++++++++- src/core/ext/filters/client_channel/http_proxy.h | 10 +++++++++- src/core/ext/filters/client_channel/lb_policy.h | 8 ++++++++ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h | 10 +++++++++- .../filters/client_channel/lb_policy/grpclb/grpclb_channel.h | 8 ++++++++ .../client_channel/lb_policy/grpclb/grpclb_client_stats.h | 8 ++++++++ src/core/ext/filters/client_channel/lb_policy_factory.h | 10 +++++++++- src/core/ext/filters/client_channel/lb_policy_registry.h | 10 +++++++++- src/core/ext/filters/client_channel/parse_address.h | 10 +++++++++- src/core/ext/filters/client_channel/proxy_mapper.h | 10 +++++++++- src/core/ext/filters/client_channel/proxy_mapper_registry.h | 10 +++++++++- src/core/ext/filters/client_channel/resolver.h | 8 ++++++++ .../client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h | 8 ++++++++ .../client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h | 8 ++++++++ .../ext/filters/client_channel/resolver/fake/fake_resolver.h | 8 ++++++++ src/core/ext/filters/client_channel/resolver_factory.h | 10 +++++++++- src/core/ext/filters/client_channel/resolver_registry.h | 10 +++++++++- src/core/ext/filters/client_channel/retry_throttle.h | 10 +++++++++- src/core/ext/filters/client_channel/subchannel.h | 8 ++++++++ src/core/ext/filters/client_channel/subchannel_index.h | 10 +++++++++- src/core/ext/filters/client_channel/uri_parser.h | 10 +++++++++- src/core/ext/filters/deadline/deadline_filter.h | 10 +++++++++- .../ext/filters/load_reporting/server_load_reporting_plugin.h | 8 ++++++++ src/core/ext/filters/workarounds/workaround_utils.h | 10 +++++++++- src/core/ext/transport/chttp2/alpn/alpn.h | 10 +++++++++- src/core/ext/transport/chttp2/server/chttp2_server.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/bin_decoder.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/bin_encoder.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/chttp2_transport.h | 8 ++++++++ src/core/ext/transport/chttp2/transport/frame.h | 8 ++++++++ src/core/ext/transport/chttp2/transport/frame_data.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/frame_goaway.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/frame_ping.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/frame_rst_stream.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/frame_settings.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/frame_window_update.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/hpack_encoder.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/hpack_parser.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/hpack_table.h | 8 ++++++++ src/core/ext/transport/chttp2/transport/http2_settings.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/incoming_metadata.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/internal.h | 8 ++++++++ src/core/ext/transport/chttp2/transport/stream_map.h | 10 +++++++++- src/core/ext/transport/chttp2/transport/varint.h | 8 ++++++++ src/core/ext/transport/cronet/transport/cronet_transport.h | 10 +++++++++- src/core/lib/channel/channel_args.h | 10 +++++++++- src/core/lib/channel/connected_channel.h | 10 +++++++++- src/core/lib/channel/handshaker.h | 10 +++++++++- src/core/lib/channel/handshaker_factory.h | 10 +++++++++- src/core/lib/channel/handshaker_registry.h | 10 +++++++++- src/core/lib/compression/algorithm_metadata.h | 10 +++++++++- src/core/lib/compression/message_compress.h | 10 +++++++++- src/core/lib/compression/stream_compression.h | 8 ++++++++ src/core/lib/debug/stats.h | 8 ++++++++ src/core/lib/debug/stats_data.h | 8 ++++++++ src/core/lib/debug/trace.h | 8 ++++++++ src/core/lib/http/format_request.h | 10 +++++++++- src/core/lib/http/httpcli.h | 10 +++++++++- src/core/lib/http/parser.h | 10 +++++++++- src/core/lib/iomgr/call_combiner.h | 8 ++++++++ src/core/lib/iomgr/combiner.h | 8 ++++++++ src/core/lib/iomgr/endpoint.h | 10 +++++++++- src/core/lib/iomgr/endpoint_pair.h | 10 +++++++++- src/core/lib/iomgr/error_internal.h | 10 +++++++++- src/core/lib/iomgr/ev_epoll1_linux.h | 10 +++++++++- src/core/lib/iomgr/ev_epollex_linux.h | 10 +++++++++- src/core/lib/iomgr/ev_epollsig_linux.h | 8 ++++++++ src/core/lib/iomgr/ev_poll_posix.h | 10 +++++++++- src/core/lib/iomgr/ev_posix.h | 10 +++++++++- src/core/lib/iomgr/exec_ctx.h | 8 ++++++++ src/core/lib/iomgr/executor.h | 10 +++++++++- src/core/lib/iomgr/gethostname.h | 10 +++++++++- src/core/lib/iomgr/iocp_windows.h | 10 +++++++++- src/core/lib/iomgr/iomgr.h | 10 +++++++++- src/core/lib/iomgr/iomgr_internal.h | 10 +++++++++- src/core/lib/iomgr/is_epollexclusive_available.h | 10 +++++++++- src/core/lib/iomgr/lockfree_event.h | 10 +++++++++- src/core/lib/iomgr/network_status_tracker.h | 10 +++++++++- src/core/lib/iomgr/polling_entity.h | 10 +++++++++- src/core/lib/iomgr/pollset.h | 8 ++++++++ src/core/lib/iomgr/pollset_set.h | 10 +++++++++- src/core/lib/iomgr/pollset_uv.h | 10 +++++++++- src/core/lib/iomgr/pollset_windows.h | 10 +++++++++- src/core/lib/iomgr/resolve_address.h | 10 +++++++++- src/core/lib/iomgr/resource_quota.h | 10 +++++++++- src/core/lib/iomgr/sockaddr_utils.h | 10 +++++++++- src/core/lib/iomgr/socket_utils.h | 10 +++++++++- src/core/lib/iomgr/socket_utils_posix.h | 10 +++++++++- src/core/lib/iomgr/socket_windows.h | 10 +++++++++- src/core/lib/iomgr/tcp_client.h | 10 +++++++++- src/core/lib/iomgr/tcp_client_posix.h | 10 +++++++++- src/core/lib/iomgr/tcp_posix.h | 10 +++++++++- src/core/lib/iomgr/tcp_server.h | 10 +++++++++- src/core/lib/iomgr/tcp_server_utils_posix.h | 10 +++++++++- src/core/lib/iomgr/tcp_uv.h | 10 +++++++++- src/core/lib/iomgr/tcp_windows.h | 10 +++++++++- src/core/lib/iomgr/time_averaged_stats.h | 10 +++++++++- src/core/lib/iomgr/timer.h | 8 ++++++++ src/core/lib/iomgr/timer_heap.h | 10 +++++++++- src/core/lib/iomgr/timer_manager.h | 10 +++++++++- src/core/lib/iomgr/udp_server.h | 10 +++++++++- src/core/lib/iomgr/unix_sockets_posix.h | 10 +++++++++- src/core/lib/iomgr/wakeup_fd_cv.h | 10 +++++++++- src/core/lib/iomgr/wakeup_fd_posix.h | 8 ++++++++ src/core/lib/json/json.h | 10 +++++++++- src/core/lib/json/json_reader.h | 10 +++++++++- src/core/lib/json/json_writer.h | 10 +++++++++- src/core/lib/profiling/basic_timers.c | 4 ++-- .../lib/security/credentials/composite/composite_credentials.h | 8 ++++++++ src/core/lib/security/credentials/credentials.h | 8 ++++++++ src/core/lib/security/credentials/fake/fake_credentials.h | 10 +++++++++- src/core/lib/security/credentials/jwt/json_token.h | 10 +++++++++- src/core/lib/security/credentials/jwt/jwt_credentials.h | 10 +++++++++- src/core/lib/security/credentials/jwt/jwt_verifier.h | 8 ++++++++ src/core/lib/security/credentials/oauth2/oauth2_credentials.h | 10 +++++++++- src/core/lib/security/transport/lb_targets_info.h | 10 +++++++++- src/core/lib/security/transport/secure_endpoint.h | 10 +++++++++- src/core/lib/security/transport/security_connector.h | 8 ++++++++ src/core/lib/security/transport/security_handshaker.h | 10 +++++++++- src/core/lib/security/transport/tsi_error.h | 10 +++++++++- src/core/lib/security/util/json_util.h | 10 +++++++++- src/core/lib/slice/b64.h | 10 +++++++++- src/core/lib/slice/percent_encoding.h | 10 +++++++++- src/core/lib/slice/slice_hash_table.h | 10 +++++++++- src/core/lib/slice/slice_internal.h | 10 +++++++++- src/core/lib/slice/slice_traits.h | 10 +++++++++- src/core/lib/support/time_precise.c | 2 ++ src/core/lib/surface/channel.h | 8 ++++++++ src/core/lib/surface/channel_stack_type.h | 10 +++++++++- src/core/lib/surface/completion_queue_factory.h | 10 +++++++++- src/core/lib/surface/event_string.h | 10 +++++++++- src/core/lib/surface/init.h | 10 +++++++++- src/core/lib/surface/server.h | 10 +++++++++- src/core/lib/surface/validate_metadata.h | 10 +++++++++- src/core/lib/transport/bdp_estimator.h | 10 +++++++++- src/core/lib/transport/byte_stream.h | 10 +++++++++- src/core/lib/transport/connectivity_state.h | 10 +++++++++- src/core/lib/transport/error_utils.h | 10 +++++++++- src/core/lib/transport/pid_controller.h | 10 +++++++++- src/core/lib/transport/service_config.h | 10 +++++++++- src/core/lib/transport/status_conversion.h | 10 +++++++++- src/core/lib/transport/timeout_encoding.h | 10 +++++++++- src/core/lib/transport/transport_impl.h | 10 +++++++++- src/core/tsi/gts_transport_security.h | 10 +++++++++- 158 files changed, 1377 insertions(+), 127 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/ext/census/base_resources.h b/src/core/ext/census/base_resources.h index 78a4d1fae5..4b1b988e3f 100644 --- a/src/core/ext/census/base_resources.h +++ b/src/core/ext/census/base_resources.h @@ -18,7 +18,15 @@ #ifndef GRPC_CORE_EXT_CENSUS_BASE_RESOURCES_H #define GRPC_CORE_EXT_CENSUS_BASE_RESOURCES_H +#ifdef __cplusplus +extern "C" { +#endif + /* Define all base resources. This should be called by census initialization. */ void define_base_resources(); -#endif /* GRPC_CORE_EXT_CENSUS_BASE_RESOURCES_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_BASE_RESOURCES_H */ \ No newline at end of file diff --git a/src/core/ext/census/census_interface.h b/src/core/ext/census/census_interface.h index a42b68ad65..12438e3c0a 100644 --- a/src/core/ext/census/census_interface.h +++ b/src/core/ext/census/census_interface.h @@ -24,6 +24,10 @@ /* Maximum length of an individual census trace annotation. */ #define CENSUS_MAX_ANNOTATION_LENGTH 200 +#ifdef __cplusplus +extern "C" { +#endif + /* Structure of a census op id. Define as structure because 64bit integer is not available on every platform for C89. */ typedef struct census_op_id { @@ -58,4 +62,8 @@ census_op_id census_tracing_start_op(void); /* Ends tracing. Calling this function will invalidate the input op_id. */ void census_tracing_end_op(census_op_id op_id); -#endif /* GRPC_CORE_EXT_CENSUS_CENSUS_INTERFACE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_CENSUS_INTERFACE_H */ \ No newline at end of file diff --git a/src/core/ext/census/census_log.h b/src/core/ext/census/census_log.h index 6b68b6bd37..cc9e008907 100644 --- a/src/core/ext/census/census_log.h +++ b/src/core/ext/census/census_log.h @@ -25,6 +25,10 @@ #define CENSUS_LOG_2_MAX_RECORD_SIZE 14 /* 2^14 = 16KB */ #define CENSUS_LOG_MAX_RECORD_SIZE (1 << CENSUS_LOG_2_MAX_RECORD_SIZE) +#ifdef __cplusplus +extern "C" { +#endif + /* Initialize the statistics logging subsystem with the given log size. A log size of 0 will result in the smallest possible log for the platform (approximately CENSUS_LOG_MAX_RECORD_SIZE * gpr_cpu_num_cores()). If @@ -73,4 +77,8 @@ size_t census_log_remaining_space(void); out-of-space. */ int census_log_out_of_space_count(void); -#endif /* GRPC_CORE_EXT_CENSUS_CENSUS_LOG_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_CENSUS_LOG_H */ \ No newline at end of file diff --git a/src/core/ext/census/hash_table.h b/src/core/ext/census/hash_table.h index 75770641c5..c22ba8df9d 100644 --- a/src/core/ext/census/hash_table.h +++ b/src/core/ext/census/hash_table.h @@ -23,6 +23,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* A chain based hash table with fixed number of buckets. Your probably shouldn't use this code directly. It is implemented for the use case in census trace store and stats store, where number of entries in @@ -113,4 +117,8 @@ typedef void (*census_ht_itr_cb)(census_ht_key key, const void *val_ptr, should not invalidate data entries. */ uint64_t census_ht_for_all(const census_ht *ht, census_ht_itr_cb); -#endif /* GRPC_CORE_EXT_CENSUS_HASH_TABLE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_HASH_TABLE_H */ \ No newline at end of file diff --git a/src/core/ext/census/intrusive_hash_map.h b/src/core/ext/census/intrusive_hash_map.h index f50de4fab4..2c7baa31fb 100644 --- a/src/core/ext/census/intrusive_hash_map.h +++ b/src/core/ext/census/intrusive_hash_map.h @@ -21,6 +21,10 @@ #include "src/core/ext/census/intrusive_hash_map_internal.h" +#ifdef __cplusplus +extern "C" { +#endif + /* 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 @@ -149,4 +153,8 @@ void intrusive_hash_map_clear(intrusive_hash_map *hash_map, void intrusive_hash_map_free(intrusive_hash_map *hash_map, void (*free_object)(void *)); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_CENSUS_INTRUSIVE_HASH_MAP_H */ diff --git a/src/core/ext/census/mlog.h b/src/core/ext/census/mlog.h index 6f3125944f..7b4d39272b 100644 --- a/src/core/ext/census/mlog.h +++ b/src/core/ext/census/mlog.h @@ -28,6 +28,10 @@ #define CENSUS_LOG_2_MAX_RECORD_SIZE 14 /* 2^14 = 16KB */ #define CENSUS_LOG_MAX_RECORD_SIZE (1 << CENSUS_LOG_2_MAX_RECORD_SIZE) +#ifdef __cplusplus +extern "C" { +#endif + /* Initialize the statistics logging subsystem with the given log size. A log size of 0 will result in the smallest possible log for the platform (approximately CENSUS_LOG_MAX_RECORD_SIZE * gpr_cpu_num_cores()). If @@ -77,4 +81,8 @@ size_t census_log_remaining_space(void); out-of-space. */ int64_t census_log_out_of_space_count(void); -#endif /* GRPC_CORE_EXT_CENSUS_MLOG_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_MLOG_H */ \ No newline at end of file diff --git a/src/core/ext/census/resource.h b/src/core/ext/census/resource.h index b8bda2c72e..5f7ac2ad27 100644 --- a/src/core/ext/census/resource.h +++ b/src/core/ext/census/resource.h @@ -23,6 +23,10 @@ #include #include "src/core/ext/census/gen/census.pb.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Internal representation of a resource. */ typedef struct { char *name; @@ -45,4 +49,8 @@ void shutdown_resources(void); from configuration files. */ int32_t define_resource(const resource *base); -#endif /* GRPC_CORE_EXT_CENSUS_RESOURCE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_RESOURCE_H */ \ No newline at end of file diff --git a/src/core/ext/census/trace_context.h b/src/core/ext/census/trace_context.h index a7233e6a2b..c707c63263 100644 --- a/src/core/ext/census/trace_context.h +++ b/src/core/ext/census/trace_context.h @@ -39,6 +39,10 @@ 1 byte for is_sampled (bool) */ #define TRACE_MAX_CONTEXT_SIZE 31 +#ifdef __cplusplus +extern "C" { +#endif + /* Encode a trace context (ctxt) into proto format to the buffer provided. The size of buffer must be at least TRACE_MAX_CONTEXT_SIZE. On success, returns the number of bytes successfully encoded into buffer. On failure, returns 0. */ @@ -53,4 +57,8 @@ of these do not exist. On success, returns true and false otherwise. */ bool decode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer, const size_t nbytes); -#endif /* GRPC_CORE_EXT_CENSUS_TRACE_CONTEXT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_TRACE_CONTEXT_H */ \ No newline at end of file diff --git a/src/core/ext/census/trace_propagation.h b/src/core/ext/census/trace_propagation.h index eecfcb7d01..3394e9e0fd 100644 --- a/src/core/ext/census/trace_propagation.h +++ b/src/core/ext/census/trace_propagation.h @@ -21,6 +21,10 @@ #include "src/core/ext/census/tracing.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Encoding and decoding functions for receiving and sending trace contexts over the wire. Only RPC libraries should be calling these functions. These functions return the number of bytes encoded/decoded @@ -45,4 +49,8 @@ size_t trace_span_context_to_http_format(const trace_span_context *ctxt, size_t http_format_to_trace_span_context(const char *buf, size_t buf_size, trace_span_context *ctxt); -#endif /* GRPC_CORE_EXT_CENSUS_TRACE_PROPAGATION_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_TRACE_PROPAGATION_H */ \ No newline at end of file diff --git a/src/core/ext/census/tracing.h b/src/core/ext/census/tracing.h index 038c9e2790..5fcbb1e1f7 100644 --- a/src/core/ext/census/tracing.h +++ b/src/core/ext/census/tracing.h @@ -25,6 +25,10 @@ #include "src/core/ext/census/trace_label.h" #include "src/core/ext/census/trace_status.h" +#ifdef __cplusplus +extern "C" { +#endif + /* This is the low level tracing API that other languages will interface with. This is not intended to be accessed by the end-user, therefore it has been designed with performance in mind rather than ease of use. */ @@ -106,4 +110,8 @@ free to ignore all further calls using the Span. EndSpanOptions can optionally be NULL. */ void trace_end_span(const trace_status *status, trace_span_context *span_ctxt); -#endif /* GRPC_CORE_EXT_CENSUS_TRACING_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_TRACING_H */ \ No newline at end of file diff --git a/src/core/ext/census/window_stats.h b/src/core/ext/census/window_stats.h index ebe3732008..3b1d197f76 100644 --- a/src/core/ext/census/window_stats.h +++ b/src/core/ext/census/window_stats.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Keep rolling sums of a user-defined statistic (containing a number of measurements) over a a number of time intervals ("windows"). For example, you can use a window_stats object to answer questions such as @@ -155,4 +159,8 @@ void census_window_stats_get_sums(const struct census_window_stats *wstats, assertion failure). This function is thread-compatible. */ void census_window_stats_destroy(struct census_window_stats *wstats); -#endif /* GRPC_CORE_EXT_CENSUS_WINDOW_STATS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_CENSUS_WINDOW_STATS_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/client_channel.h b/src/core/ext/filters/client_channel/client_channel.h index c99f0092e9..b32f378c6c 100644 --- a/src/core/ext/filters/client_channel/client_channel.h +++ b/src/core/ext/filters/client_channel/client_channel.h @@ -28,6 +28,10 @@ extern grpc_tracer_flag grpc_client_channel_trace; // Channel arg key for server URI string. #define GRPC_ARG_SERVER_URI "grpc.server_uri" +#ifdef __cplusplus +extern "C" { +#endif + /* A client channel is a channel that begins disconnected, and can connect to some endpoint on demand. If that endpoint disconnects, it will be connected to again later. @@ -52,4 +56,8 @@ void grpc_client_channel_watch_connectivity_state( grpc_subchannel_call *grpc_client_channel_get_subchannel_call( grpc_call_element *elem); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/client_channel_factory.h b/src/core/ext/filters/client_channel/client_channel_factory.h index ce6266c769..bad8b97042 100644 --- a/src/core/ext/filters/client_channel/client_channel_factory.h +++ b/src/core/ext/filters/client_channel/client_channel_factory.h @@ -27,6 +27,10 @@ // Channel arg key for client channel factory. #define GRPC_ARG_CLIENT_CHANNEL_FACTORY "grpc.client_channel_factory" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_client_channel_factory grpc_client_channel_factory; typedef struct grpc_client_channel_factory_vtable grpc_client_channel_factory_vtable; @@ -74,4 +78,8 @@ grpc_channel *grpc_client_channel_factory_create_channel( grpc_arg grpc_client_channel_factory_create_channel_arg( grpc_client_channel_factory *factory); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_FACTORY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/connector.h b/src/core/ext/filters/client_channel/connector.h index 7f3d4a1cc0..79ccb0d9bf 100644 --- a/src/core/ext/filters/client_channel/connector.h +++ b/src/core/ext/filters/client_channel/connector.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/resolve_address.h" #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_connector grpc_connector; typedef struct grpc_connector_vtable grpc_connector_vtable; @@ -70,4 +74,8 @@ void grpc_connector_connect(grpc_exec_ctx *exec_ctx, grpc_connector *connector, void grpc_connector_shutdown(grpc_exec_ctx *exec_ctx, grpc_connector *connector, grpc_error *why); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CONNECTOR_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.h b/src/core/ext/filters/client_channel/http_connect_handshaker.h index 928a23dc93..5042c61bec 100644 --- a/src/core/ext/filters/client_channel/http_connect_handshaker.h +++ b/src/core/ext/filters/client_channel/http_connect_handshaker.h @@ -28,7 +28,15 @@ /// seperated by colons. #define GRPC_ARG_HTTP_CONNECT_HEADERS "grpc.http_connect_headers" +#ifdef __cplusplus +extern "C" { +#endif + /// Registers handshaker factory. void grpc_http_connect_register_handshaker_factory(); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_CONNECT_HANDSHAKER_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/http_proxy.h b/src/core/ext/filters/client_channel/http_proxy.h index 34694931d0..65d52334af 100644 --- a/src/core/ext/filters/client_channel/http_proxy.h +++ b/src/core/ext/filters/client_channel/http_proxy.h @@ -19,6 +19,14 @@ #ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H #define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H +#ifdef __cplusplus +extern "C" { +#endif + void grpc_register_http_proxy_mapper(); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_HTTP_PROXY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/lb_policy.h b/src/core/ext/filters/client_channel/lb_policy.h index 645d51e138..010299c2f4 100644 --- a/src/core/ext/filters/client_channel/lb_policy.h +++ b/src/core/ext/filters/client_channel/lb_policy.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/transport/connectivity_state.h" +#ifdef __cplusplus +extern "C" { +#endif + /** A load balancing policy: specified by a vtable and a struct (which is expected to be extended to contain some parameters) */ typedef struct grpc_lb_policy grpc_lb_policy; @@ -204,4 +208,8 @@ void grpc_lb_policy_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, const grpc_lb_policy_args *lb_policy_args); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_H */ diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h index 63ad66c5e9..c67df609fc 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h @@ -21,9 +21,17 @@ #include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Returns a load balancing factory for the glb policy, which tries to connect * to a load balancing server to decide the next successfully connected * subchannel to pick. */ grpc_lb_policy_factory *grpc_glb_lb_factory_create(); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h index 6120bf53f7..e8599d1f51 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h @@ -23,6 +23,10 @@ #include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" #include "src/core/lib/slice/slice_hash_table.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Create the channel used for communicating with an LB service. * Note that an LB *service* may be comprised of several LB *servers*. * @@ -40,5 +44,9 @@ grpc_channel_args *grpc_lb_policy_grpclb_build_lb_channel_args( grpc_fake_resolver_response_generator *response_generator, const grpc_channel_args *args); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CHANNEL_H \ */ diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h index c51e2a431a..b38c076f38 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h @@ -23,6 +23,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_grpclb_client_stats grpc_grpclb_client_stats; typedef struct { @@ -61,5 +65,9 @@ void grpc_grpclb_client_stats_get_locked( void grpc_grpclb_dropped_call_counts_destroy( grpc_grpclb_dropped_call_counts* drop_entries); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_GRPCLB_GRPCLB_CLIENT_STATS_H \ */ diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.h b/src/core/ext/filters/client_channel/lb_policy_factory.h index cf0f8cb615..69bcba4232 100644 --- a/src/core/ext/filters/client_channel/lb_policy_factory.h +++ b/src/core/ext/filters/client_channel/lb_policy_factory.h @@ -29,6 +29,10 @@ // Channel arg key for grpc_lb_addresses. #define GRPC_ARG_LB_ADDRESSES "grpc.lb_addresses" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_lb_policy_factory grpc_lb_policy_factory; typedef struct grpc_lb_policy_factory_vtable grpc_lb_policy_factory_vtable; @@ -130,4 +134,8 @@ grpc_lb_policy *grpc_lb_policy_factory_create_lb_policy( grpc_exec_ctx *exec_ctx, grpc_lb_policy_factory *factory, grpc_lb_policy_args *args); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_FACTORY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.h b/src/core/ext/filters/client_channel/lb_policy_registry.h index f5995687cf..0867844c37 100644 --- a/src/core/ext/filters/client_channel/lb_policy_registry.h +++ b/src/core/ext/filters/client_channel/lb_policy_registry.h @@ -22,6 +22,10 @@ #include "src/core/ext/filters/client_channel/lb_policy_factory.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Initialize the registry and set \a default_factory as the factory to be * returned when no name is provided in a lookup */ void grpc_lb_policy_registry_init(void); @@ -37,4 +41,8 @@ void grpc_register_lb_policy(grpc_lb_policy_factory *factory); grpc_lb_policy *grpc_lb_policy_create(grpc_exec_ctx *exec_ctx, const char *name, grpc_lb_policy_args *args); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_LB_POLICY_REGISTRY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/parse_address.h b/src/core/ext/filters/client_channel/parse_address.h index c90a827da5..742df380b7 100644 --- a/src/core/ext/filters/client_channel/parse_address.h +++ b/src/core/ext/filters/client_channel/parse_address.h @@ -24,6 +24,10 @@ #include "src/core/ext/filters/client_channel/uri_parser.h" #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Populate \a resolved_addr from \a uri, whose path is expected to contain a * unix socket path. Returns true upon success. */ bool grpc_parse_unix(const grpc_uri *uri, grpc_resolved_address *resolved_addr); @@ -45,4 +49,8 @@ bool grpc_parse_ipv4_hostport(const char *hostport, grpc_resolved_address *addr, bool grpc_parse_ipv6_hostport(const char *hostport, grpc_resolved_address *addr, bool log_errors); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PARSE_ADDRESS_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/proxy_mapper.h b/src/core/ext/filters/client_channel/proxy_mapper.h index a13861ccaf..1325a9f1f6 100644 --- a/src/core/ext/filters/client_channel/proxy_mapper.h +++ b/src/core/ext/filters/client_channel/proxy_mapper.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_proxy_mapper grpc_proxy_mapper; typedef struct { @@ -71,4 +75,8 @@ bool grpc_proxy_mapper_map_address(grpc_exec_ctx* exec_ctx, void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.h b/src/core/ext/filters/client_channel/proxy_mapper_registry.h index 99e54d1a78..2d389f1c21 100644 --- a/src/core/ext/filters/client_channel/proxy_mapper_registry.h +++ b/src/core/ext/filters/client_channel/proxy_mapper_registry.h @@ -21,6 +21,10 @@ #include "src/core/ext/filters/client_channel/proxy_mapper.h" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_proxy_mapper_registry_init(); void grpc_proxy_mapper_registry_shutdown(); @@ -41,4 +45,8 @@ bool grpc_proxy_mappers_map_address(grpc_exec_ctx* exec_ctx, grpc_resolved_address** new_address, grpc_channel_args** new_args); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_PROXY_MAPPER_REGISTRY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/resolver.h b/src/core/ext/filters/client_channel/resolver.h index ae9c8f66fe..73fbbbbc3b 100644 --- a/src/core/ext/filters/client_channel/resolver.h +++ b/src/core/ext/filters/client_channel/resolver.h @@ -22,6 +22,10 @@ #include "src/core/ext/filters/client_channel/subchannel.h" #include "src/core/lib/iomgr/iomgr.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_resolver grpc_resolver; typedef struct grpc_resolver_vtable grpc_resolver_vtable; @@ -87,4 +91,8 @@ void grpc_resolver_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, grpc_channel_args **result, grpc_closure *on_complete); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_H */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h index 386012d2ed..3d4309f2fa 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h @@ -22,6 +22,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/pollset_set.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_ares_ev_driver grpc_ares_ev_driver; /* Start \a ev_driver. It will keep working until all IO on its ares_channel is @@ -49,5 +53,9 @@ void grpc_ares_ev_driver_destroy(grpc_ares_ev_driver *ev_driver); void grpc_ares_ev_driver_shutdown(grpc_exec_ctx *exec_ctx, grpc_ares_ev_driver *ev_driver); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_EV_DRIVER_H \ */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h index 108333047d..38fbea9aac 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_ares_request grpc_ares_request; /* Asynchronously resolve \a name. Use \a default_port if a port isn't @@ -65,5 +69,9 @@ grpc_error *grpc_ares_init(void); it has been called the same number of times as grpc_ares_init(). */ void grpc_ares_cleanup(void); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_DNS_C_ARES_GRPC_ARES_WRAPPER_H \ */ diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h index c084ef2a58..005f5f9417 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h @@ -24,6 +24,10 @@ #define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \ "grpc.fake_resolver.response_generator" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_resolver_fake_init(); // Instances of \a grpc_fake_resolver_response_generator are passed to the @@ -56,5 +60,9 @@ grpc_fake_resolver_response_generator_ref( void grpc_fake_resolver_response_generator_unref( grpc_fake_resolver_response_generator* generator); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FAKE_FAKE_RESOLVER_H \ */ diff --git a/src/core/ext/filters/client_channel/resolver_factory.h b/src/core/ext/filters/client_channel/resolver_factory.h index 6bd7929d4e..6e533e3248 100644 --- a/src/core/ext/filters/client_channel/resolver_factory.h +++ b/src/core/ext/filters/client_channel/resolver_factory.h @@ -24,6 +24,10 @@ #include "src/core/ext/filters/client_channel/uri_parser.h" #include "src/core/lib/iomgr/pollset_set.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_resolver_factory grpc_resolver_factory; typedef struct grpc_resolver_factory_vtable grpc_resolver_factory_vtable; @@ -67,4 +71,8 @@ grpc_resolver *grpc_resolver_factory_create_resolver( char *grpc_resolver_factory_get_default_authority( grpc_resolver_factory *factory, grpc_uri *uri); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_FACTORY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/resolver_registry.h b/src/core/ext/filters/client_channel/resolver_registry.h index 692490543d..eb08d887b1 100644 --- a/src/core/ext/filters/client_channel/resolver_registry.h +++ b/src/core/ext/filters/client_channel/resolver_registry.h @@ -22,6 +22,10 @@ #include "src/core/ext/filters/client_channel/resolver_factory.h" #include "src/core/lib/iomgr/pollset_set.h" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_resolver_registry_init(); void grpc_resolver_registry_shutdown(void); @@ -66,4 +70,8 @@ char *grpc_get_default_authority(grpc_exec_ctx *exec_ctx, const char *target); char *grpc_resolver_factory_add_default_prefix_if_needed( grpc_exec_ctx *exec_ctx, const char *target); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_REGISTRY_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/retry_throttle.h b/src/core/ext/filters/client_channel/retry_throttle.h index bf99297e98..3b849475b9 100644 --- a/src/core/ext/filters/client_channel/retry_throttle.h +++ b/src/core/ext/filters/client_channel/retry_throttle.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /// Tracks retry throttling data for an individual server name. typedef struct grpc_server_retry_throttle_data grpc_server_retry_throttle_data; @@ -47,4 +51,8 @@ void grpc_retry_throttle_map_shutdown(); grpc_server_retry_throttle_data* grpc_retry_throttle_map_get_data_for_server( const char* server_name, int max_milli_tokens, int milli_token_ratio); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_THROTTLE_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/subchannel.h b/src/core/ext/filters/client_channel/subchannel.h index 51d712f6a7..c67ff97b0b 100644 --- a/src/core/ext/filters/client_channel/subchannel.h +++ b/src/core/ext/filters/client_channel/subchannel.h @@ -26,6 +26,10 @@ #include "src/core/lib/transport/connectivity_state.h" #include "src/core/lib/transport/metadata.h" +#ifdef __cplusplus +extern "C" { +#endif + // Channel arg containing a grpc_resolved_address to connect to. #define GRPC_ARG_SUBCHANNEL_ADDRESS "grpc.subchannel_address" @@ -188,4 +192,8 @@ const char *grpc_get_subchannel_address_uri_arg(const grpc_channel_args *args); /// Caller is responsible for freeing the string. grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address *addr); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_H */ diff --git a/src/core/ext/filters/client_channel/subchannel_index.h b/src/core/ext/filters/client_channel/subchannel_index.h index 92e36d5283..09bac3592c 100644 --- a/src/core/ext/filters/client_channel/subchannel_index.h +++ b/src/core/ext/filters/client_channel/subchannel_index.h @@ -21,6 +21,10 @@ #include "src/core/ext/filters/client_channel/subchannel.h" +#ifdef __cplusplus +extern "C" { +#endif + /** \file Provides an index of active subchannels so that they can be shared amongst channels */ @@ -78,4 +82,8 @@ void grpc_subchannel_index_unref(void); * force_creation set. */ void grpc_subchannel_index_test_only_set_force_creation(bool force_creation); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_SUBCHANNEL_INDEX_H */ \ No newline at end of file diff --git a/src/core/ext/filters/client_channel/uri_parser.h b/src/core/ext/filters/client_channel/uri_parser.h index 05ca2e00e4..43e8ae64e0 100644 --- a/src/core/ext/filters/client_channel/uri_parser.h +++ b/src/core/ext/filters/client_channel/uri_parser.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { char *scheme; char *authority; @@ -47,4 +51,8 @@ const char *grpc_uri_get_query_arg(const grpc_uri *uri, const char *key); /** destroy a uri */ void grpc_uri_destroy(grpc_uri *uri); -#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_URI_PARSER_H */ \ No newline at end of file diff --git a/src/core/ext/filters/deadline/deadline_filter.h b/src/core/ext/filters/deadline/deadline_filter.h index 3eb102ad28..f4a1110ee6 100644 --- a/src/core/ext/filters/deadline/deadline_filter.h +++ b/src/core/ext/filters/deadline/deadline_filter.h @@ -20,6 +20,10 @@ #include "src/core/lib/channel/channel_stack.h" #include "src/core/lib/iomgr/timer.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum grpc_deadline_timer_state { GRPC_DEADLINE_STATE_INITIAL, GRPC_DEADLINE_STATE_PENDING, @@ -89,4 +93,8 @@ bool grpc_deadline_checking_enabled(const grpc_channel_args* args); extern const grpc_channel_filter grpc_client_deadline_filter; extern const grpc_channel_filter grpc_server_deadline_filter; -#endif /* GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_DEADLINE_DEADLINE_FILTER_H */ \ No newline at end of file diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h index 65a6d0900e..65e254eb53 100644 --- a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h +++ b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.h @@ -23,6 +23,10 @@ #include "src/core/lib/channel/channel_stack.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Identifiers for the invocation point of the users LR callback */ typedef enum grpc_load_reporting_source { GRPC_LR_POINT_UNKNOWN = 0, @@ -55,5 +59,9 @@ typedef struct grpc_load_reporting_call_data { /** Return a \a grpc_arg enabling load reporting */ grpc_arg grpc_load_reporting_enable_arg(); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_FILTERS_LOAD_REPORTING_SERVER_LOAD_REPORTING_PLUGIN_H \ */ diff --git a/src/core/ext/filters/workarounds/workaround_utils.h b/src/core/ext/filters/workarounds/workaround_utils.h index 2ad7a876d5..afd5291333 100644 --- a/src/core/ext/filters/workarounds/workaround_utils.h +++ b/src/core/ext/filters/workarounds/workaround_utils.h @@ -24,6 +24,10 @@ #define GRPC_WORKAROUND_PRIORITY_HIGH 10001 #define GRPC_WORKAROUND_PROIRITY_LOW 9999 +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_workaround_user_agent_md { bool workaround_active[GRPC_MAX_WORKAROUND_ID]; } grpc_workaround_user_agent_md; @@ -34,4 +38,8 @@ typedef bool (*user_agent_parser)(grpc_mdelem); void grpc_register_workaround(uint32_t id, user_agent_parser parser); -#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_FILTERS_WORKAROUNDS_WORKAROUND_UTILS_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/alpn/alpn.h b/src/core/ext/transport/chttp2/alpn/alpn.h index 379af4b24c..5842204c20 100644 --- a/src/core/ext/transport/chttp2/alpn/alpn.h +++ b/src/core/ext/transport/chttp2/alpn/alpn.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Retuns 1 if the version is supported, 0 otherwise. */ int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size); @@ -31,4 +35,8 @@ size_t grpc_chttp2_num_alpn_versions(void); * grpc_chttp2_num_alpn_versions()) */ const char *grpc_chttp2_get_alpn_version_index(size_t i); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_ALPN_ALPN_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.h b/src/core/ext/transport/chttp2/server/chttp2_server.h index ed968496f9..e1df28ed11 100644 --- a/src/core/ext/transport/chttp2/server/chttp2_server.h +++ b/src/core/ext/transport/chttp2/server/chttp2_server.h @@ -23,10 +23,18 @@ #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + /// Adds a port to \a server. Sets \a port_num to the port number. /// Takes ownership of \a args. grpc_error *grpc_chttp2_server_add_port(grpc_exec_ctx *exec_ctx, grpc_server *server, const char *addr, grpc_channel_args *args, int *port_num); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_SERVER_CHTTP2_SERVER_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.h b/src/core/ext/transport/chttp2/transport/bin_decoder.h index 047b33d587..f50e0a8ac4 100644 --- a/src/core/ext/transport/chttp2/transport/bin_decoder.h +++ b/src/core/ext/transport/chttp2/transport/bin_decoder.h @@ -22,6 +22,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + struct grpc_base64_decode_context { /* input/output: */ uint8_t *input_cur; @@ -49,4 +53,8 @@ grpc_slice grpc_chttp2_base64_decode_with_length(grpc_exec_ctx *exec_ctx, grpc_slice input, size_t output_length); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_DECODER_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.h b/src/core/ext/transport/chttp2/transport/bin_encoder.h index a8f36a345a..ae8219c5ce 100644 --- a/src/core/ext/transport/chttp2/transport/bin_encoder.h +++ b/src/core/ext/transport/chttp2/transport/bin_encoder.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* base64 encode a slice. Returns a new slice, does not take ownership of the input */ grpc_slice grpc_chttp2_base64_encode(grpc_slice input); @@ -36,4 +40,8 @@ grpc_slice grpc_chttp2_huffman_compress(grpc_slice input); return y; */ grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_BIN_ENCODER_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.h b/src/core/ext/transport/chttp2/transport/chttp2_transport.h index 55fb1a8343..321fca4c82 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.h +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + extern grpc_tracer_flag grpc_http_trace; extern grpc_tracer_flag grpc_flowctl_trace; extern grpc_tracer_flag grpc_trace_http2_stream_state; @@ -41,4 +45,8 @@ void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, grpc_transport *transport, grpc_slice_buffer *read_buffer); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_CHTTP2_TRANSPORT_H */ diff --git a/src/core/ext/transport/chttp2/transport/frame.h b/src/core/ext/transport/chttp2/transport/frame.h index dba4c004ec..e7debdad79 100644 --- a/src/core/ext/transport/chttp2/transport/frame.h +++ b/src/core/ext/transport/chttp2/transport/frame.h @@ -24,6 +24,10 @@ #include "src/core/lib/iomgr/error.h" +#ifdef __cplusplus +extern "C" { +#endif + /* defined in internal.h */ typedef struct grpc_chttp2_stream grpc_chttp2_stream; typedef struct grpc_chttp2_transport grpc_chttp2_transport; @@ -43,4 +47,8 @@ typedef struct grpc_chttp2_transport grpc_chttp2_transport; #define GRPC_CHTTP2_DATA_FLAG_PADDED 8 #define GRPC_CHTTP2_FLAG_HAS_PRIORITY 0x20 +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_H */ diff --git a/src/core/ext/transport/chttp2/transport/frame_data.h b/src/core/ext/transport/chttp2/transport/frame_data.h index 3f1c787847..2df99ccf98 100644 --- a/src/core/ext/transport/chttp2/transport/frame_data.h +++ b/src/core/ext/transport/chttp2/transport/frame_data.h @@ -28,6 +28,10 @@ #include "src/core/lib/transport/byte_stream.h" #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_CHTTP2_DATA_FH_0, GRPC_CHTTP2_DATA_FH_1, @@ -80,4 +84,8 @@ grpc_error *grpc_deframe_unprocessed_incoming_frames( grpc_slice_buffer *slices, grpc_slice *slice_out, grpc_byte_stream **stream_out); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_DATA_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.h b/src/core/ext/transport/chttp2/transport/frame_goaway.h index abc48f30c6..ce6f18b35c 100644 --- a/src/core/ext/transport/chttp2/transport/frame_goaway.h +++ b/src/core/ext/transport/chttp2/transport/frame_goaway.h @@ -25,6 +25,10 @@ #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_CHTTP2_GOAWAY_LSI0, GRPC_CHTTP2_GOAWAY_LSI1, @@ -60,4 +64,8 @@ void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code, grpc_slice debug_data, grpc_slice_buffer *slice_buffer); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_GOAWAY_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.h b/src/core/ext/transport/chttp2/transport/frame_ping.h index 5969ace9bd..91f16f050f 100644 --- a/src/core/ext/transport/chttp2/transport/frame_ping.h +++ b/src/core/ext/transport/chttp2/transport/frame_ping.h @@ -23,6 +23,10 @@ #include "src/core/ext/transport/chttp2/transport/frame.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint8_t byte; uint8_t is_ack; @@ -41,4 +45,8 @@ grpc_error *grpc_chttp2_ping_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, /* Test-only function for disabling ping ack */ void grpc_set_disable_ping_ack(bool disable_ping_ack); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_PING_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h index d088221b52..bdca064a91 100644 --- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.h +++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.h @@ -24,6 +24,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint8_t byte; uint8_t reason_bytes[4]; @@ -40,4 +44,8 @@ grpc_error *grpc_chttp2_rst_stream_parser_parse(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s, grpc_slice slice, int is_last); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_RST_STREAM_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.h b/src/core/ext/transport/chttp2/transport/frame_settings.h index 47479d675d..f0793f0e73 100644 --- a/src/core/ext/transport/chttp2/transport/frame_settings.h +++ b/src/core/ext/transport/chttp2/transport/frame_settings.h @@ -25,6 +25,10 @@ #include "src/core/ext/transport/chttp2/transport/http2_settings.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_CHTTP2_SPS_ID0, GRPC_CHTTP2_SPS_ID1, @@ -58,4 +62,8 @@ grpc_error *grpc_chttp2_settings_parser_parse(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s, grpc_slice slice, int is_last); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_SETTINGS_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.h b/src/core/ext/transport/chttp2/transport/frame_window_update.h index 698da4e351..29cf0cc740 100644 --- a/src/core/ext/transport/chttp2/transport/frame_window_update.h +++ b/src/core/ext/transport/chttp2/transport/frame_window_update.h @@ -24,6 +24,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint8_t byte; uint8_t is_connection_update; @@ -39,4 +43,8 @@ grpc_error *grpc_chttp2_window_update_parser_parse( grpc_exec_ctx *exec_ctx, void *parser, grpc_chttp2_transport *t, grpc_chttp2_stream *s, grpc_slice slice, int is_last); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_FRAME_WINDOW_UPDATE_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.h b/src/core/ext/transport/chttp2/transport/hpack_encoder.h index 271192f894..dc28b5566a 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.h +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.h @@ -34,6 +34,10 @@ /* maximum table size we'll actually use */ #define GRPC_CHTTP2_HPACKC_MAX_TABLE_SIZE (1024 * 1024) +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint32_t filter_elems_sum; uint32_t max_table_size; @@ -91,4 +95,8 @@ void grpc_chttp2_encode_header(grpc_exec_ctx *exec_ctx, const grpc_encode_header_options *options, grpc_slice_buffer *outbuf); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.h b/src/core/ext/transport/chttp2/transport/hpack_parser.h index 8fbc6a602b..6c36ebdf8d 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_parser.h +++ b/src/core/ext/transport/chttp2/transport/hpack_parser.h @@ -27,6 +27,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/transport/metadata.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_chttp2_hpack_parser grpc_chttp2_hpack_parser; typedef grpc_error *(*grpc_chttp2_hpack_parser_state)( @@ -111,4 +115,8 @@ grpc_error *grpc_chttp2_header_parser_parse(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s, grpc_slice slice, int is_last); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_PARSER_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.h b/src/core/ext/transport/chttp2/transport/hpack_table.h index 2cf8f68506..a3ce2730a8 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_table.h +++ b/src/core/ext/transport/chttp2/transport/hpack_table.h @@ -24,6 +24,10 @@ #include "src/core/lib/iomgr/error.h" #include "src/core/lib/transport/metadata.h" +#ifdef __cplusplus +extern "C" { +#endif + /* HPACK header table */ /* last index in the static table */ @@ -94,4 +98,8 @@ typedef struct { grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( const grpc_chttp2_hptbl *tbl, grpc_mdelem md); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_TABLE_H */ diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.h b/src/core/ext/transport/chttp2/transport/http2_settings.h index 706dfc3139..01e80b8d01 100644 --- a/src/core/ext/transport/chttp2/transport/http2_settings.h +++ b/src/core/ext/transport/chttp2/transport/http2_settings.h @@ -35,6 +35,10 @@ typedef enum { } grpc_chttp2_setting_id; #define GRPC_CHTTP2_NUM_SETTINGS 7 + +#ifdef __cplusplus +extern "C" { +#endif extern const uint16_t grpc_setting_id_to_wire_id[]; bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id *out); @@ -56,4 +60,8 @@ typedef struct { extern const grpc_chttp2_setting_parameters grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS]; -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HTTP2_SETTINGS_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.h b/src/core/ext/transport/chttp2/transport/incoming_metadata.h index a951d8764c..9ffcabd0b9 100644 --- a/src/core/ext/transport/chttp2/transport/incoming_metadata.h +++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.h @@ -21,6 +21,10 @@ #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { gpr_arena *arena; grpc_metadata_batch batch; @@ -45,4 +49,8 @@ grpc_error *grpc_chttp2_incoming_metadata_buffer_replace_or_add( void grpc_chttp2_incoming_metadata_buffer_set_deadline( grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INCOMING_METADATA_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/internal.h b/src/core/ext/transport/chttp2/transport/internal.h index 49022155aa..96af18f1d1 100644 --- a/src/core/ext/transport/chttp2/transport/internal.h +++ b/src/core/ext/transport/chttp2/transport/internal.h @@ -42,6 +42,10 @@ #include "src/core/lib/transport/pid_controller.h" #include "src/core/lib/transport/transport_impl.h" +#ifdef __cplusplus +extern "C" { +#endif + /* streams are kept in various linked lists depending on what things need to happen to them... this enum labels each list */ typedef enum { @@ -908,4 +912,8 @@ void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx, void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, bool is_client); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_INTERNAL_H */ diff --git a/src/core/ext/transport/chttp2/transport/stream_map.h b/src/core/ext/transport/chttp2/transport/stream_map.h index 30c50ba32e..364d37c33a 100644 --- a/src/core/ext/transport/chttp2/transport/stream_map.h +++ b/src/core/ext/transport/chttp2/transport/stream_map.h @@ -23,6 +23,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Data structure to map a uint32_t to a data object (represented by a void*) Represented as a sorted array of keys, and a corresponding array of values. @@ -65,4 +69,8 @@ void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, void *value), void *user_data); -#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_STREAM_MAP_H */ \ No newline at end of file diff --git a/src/core/ext/transport/chttp2/transport/varint.h b/src/core/ext/transport/chttp2/transport/varint.h index 5a2b670f06..d3a9d902c4 100644 --- a/src/core/ext/transport/chttp2/transport/varint.h +++ b/src/core/ext/transport/chttp2/transport/varint.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Helpers for hpack varint encoding */ /* length of a value that needs varint tail encoding (it's bigger than can be @@ -57,4 +61,8 @@ void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target, } \ } while (0) +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_VARINT_H */ diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.h b/src/core/ext/transport/cronet/transport/cronet_transport.h index 3bd4249b79..c473afa100 100644 --- a/src/core/ext/transport/cronet/transport/cronet_transport.h +++ b/src/core/ext/transport/cronet/transport/cronet_transport.h @@ -21,8 +21,16 @@ #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_transport *grpc_create_cronet_transport(void *engine, const char *target, const grpc_channel_args *args, void *reserved); -#endif /* GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_EXT_TRANSPORT_CRONET_TRANSPORT_CRONET_TRANSPORT_H */ \ No newline at end of file diff --git a/src/core/lib/channel/channel_args.h b/src/core/lib/channel/channel_args.h index 0599e189c3..2837174f49 100644 --- a/src/core/lib/channel/channel_args.h +++ b/src/core/lib/channel/channel_args.h @@ -23,6 +23,10 @@ #include #include "src/core/lib/iomgr/socket_mutator.h" +#ifdef __cplusplus +extern "C" { +#endif + // Channel args are intentionally immutable, to avoid the need for locking. /** Copy the arguments in \a src into a new instance */ @@ -149,4 +153,8 @@ grpc_arg grpc_channel_arg_integer_create(char *name, int value); grpc_arg grpc_channel_arg_pointer_create(char *name, void *value, const grpc_arg_pointer_vtable *vtable); -#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_CHANNEL_CHANNEL_ARGS_H */ \ No newline at end of file diff --git a/src/core/lib/channel/connected_channel.h b/src/core/lib/channel/connected_channel.h index 10c98cce54..b55a1089a0 100644 --- a/src/core/lib/channel/connected_channel.h +++ b/src/core/lib/channel/connected_channel.h @@ -21,6 +21,10 @@ #include "src/core/lib/channel/channel_stack_builder.h" +#ifdef __cplusplus +extern "C" { +#endif + extern const grpc_channel_filter grpc_connected_filter; bool grpc_add_connected_filter(grpc_exec_ctx *exec_ctx, @@ -30,4 +34,8 @@ bool grpc_add_connected_filter(grpc_exec_ctx *exec_ctx, /* Debug helper to dig the transport stream out of a call element */ grpc_stream *grpc_connected_channel_get_stream(grpc_call_element *elem); -#endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_CHANNEL_CONNECTED_CHANNEL_H */ \ No newline at end of file diff --git a/src/core/lib/channel/handshaker.h b/src/core/lib/channel/handshaker.h index eb9a59bd08..a857cde791 100644 --- a/src/core/lib/channel/handshaker.h +++ b/src/core/lib/channel/handshaker.h @@ -26,6 +26,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/tcp_server.h" +#ifdef __cplusplus +extern "C" { +#endif + /// Handshakers are used to perform initial handshakes on a connection /// before the client sends the initial request. Some examples of what /// a handshaker can be used for includes support for HTTP CONNECT on @@ -164,4 +168,8 @@ void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head, void grpc_handshake_manager_pending_list_shutdown_all( grpc_exec_ctx* exec_ctx, grpc_handshake_manager* head, grpc_error* why); -#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_H */ \ No newline at end of file diff --git a/src/core/lib/channel/handshaker_factory.h b/src/core/lib/channel/handshaker_factory.h index 6238e735dc..2a130de252 100644 --- a/src/core/lib/channel/handshaker_factory.h +++ b/src/core/lib/channel/handshaker_factory.h @@ -24,6 +24,10 @@ #include "src/core/lib/channel/handshaker.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + // A handshaker factory is used to create handshakers. typedef struct grpc_handshaker_factory grpc_handshaker_factory; @@ -48,4 +52,8 @@ void grpc_handshaker_factory_add_handshakers( void grpc_handshaker_factory_destroy( grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory); -#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_FACTORY_H */ \ No newline at end of file diff --git a/src/core/lib/channel/handshaker_registry.h b/src/core/lib/channel/handshaker_registry.h index a3b2ac1dc7..e96bf06b6a 100644 --- a/src/core/lib/channel/handshaker_registry.h +++ b/src/core/lib/channel/handshaker_registry.h @@ -24,6 +24,10 @@ #include "src/core/lib/channel/handshaker_factory.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { HANDSHAKER_CLIENT = 0, HANDSHAKER_SERVER, @@ -45,4 +49,8 @@ void grpc_handshakers_add(grpc_exec_ctx* exec_ctx, const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr); -#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_CHANNEL_HANDSHAKER_REGISTRY_H */ \ No newline at end of file diff --git a/src/core/lib/compression/algorithm_metadata.h b/src/core/lib/compression/algorithm_metadata.h index 08feafc1bb..3eb7088230 100644 --- a/src/core/lib/compression/algorithm_metadata.h +++ b/src/core/lib/compression/algorithm_metadata.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/transport/metadata.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Return compression algorithm based metadata value */ grpc_slice grpc_compression_algorithm_slice( grpc_compression_algorithm algorithm); @@ -49,4 +53,8 @@ grpc_compression_algorithm grpc_compression_algorithm_from_slice( grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice( grpc_slice str); -#endif /* GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_COMPRESSION_ALGORITHM_METADATA_H */ \ No newline at end of file diff --git a/src/core/lib/compression/message_compress.h b/src/core/lib/compression/message_compress.h index ca8ca37f8e..d2545a02c2 100644 --- a/src/core/lib/compression/message_compress.h +++ b/src/core/lib/compression/message_compress.h @@ -22,6 +22,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /* compress 'input' to 'output' using 'algorithm'. On success, appends compressed slices to output and returns 1. On failure, appends uncompressed slices to output and returns 0. */ @@ -36,4 +40,8 @@ int grpc_msg_decompress(grpc_exec_ctx* exec_ctx, grpc_compression_algorithm algorithm, grpc_slice_buffer* input, grpc_slice_buffer* output); -#endif /* GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_COMPRESSION_MESSAGE_COMPRESS_H */ \ No newline at end of file diff --git a/src/core/lib/compression/stream_compression.h b/src/core/lib/compression/stream_compression.h index 6d073280fa..901f9357ee 100644 --- a/src/core/lib/compression/stream_compression.h +++ b/src/core/lib/compression/stream_compression.h @@ -24,6 +24,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #include "src/core/lib/transport/static_metadata.h" typedef struct grpc_stream_compression_vtable grpc_stream_compression_vtable; @@ -111,4 +115,8 @@ void grpc_stream_compression_context_destroy( int grpc_stream_compression_method_parse( grpc_slice value, bool is_compress, grpc_stream_compression_method *method); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/core/lib/debug/stats.h b/src/core/lib/debug/stats.h index 09d190d488..fec1d651e6 100644 --- a/src/core/lib/debug/stats.h +++ b/src/core/lib/debug/stats.h @@ -23,6 +23,10 @@ #include "src/core/lib/debug/stats_data.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_stats_data { gpr_atm counters[GRPC_STATS_COUNTER_COUNT]; gpr_atm histograms[GRPC_STATS_HISTOGRAM_BUCKETS]; @@ -58,4 +62,8 @@ double grpc_stats_histo_percentile(const grpc_stats_data *data, size_t grpc_stats_histo_count(const grpc_stats_data *data, grpc_stats_histograms histogram); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h index 28dab00117..cf5bafbd04 100644 --- a/src/core/lib/debug/stats_data.h +++ b/src/core/lib/debug/stats_data.h @@ -24,6 +24,10 @@ #include #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED, GRPC_STATS_COUNTER_SERVER_CALLS_CREATED, @@ -467,4 +471,8 @@ extern const int *const grpc_stats_histo_bucket_boundaries[13]; extern void (*const grpc_stats_inc_histogram[13])(grpc_exec_ctx *exec_ctx, int x); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */ diff --git a/src/core/lib/debug/trace.h b/src/core/lib/debug/trace.h index 64f2e3fc33..558ba942bb 100644 --- a/src/core/lib/debug/trace.h +++ b/src/core/lib/debug/trace.h @@ -23,6 +23,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + #if defined(__has_feature) #if __has_feature(thread_sanitizer) #define GRPC_THREADSAFE_TRACER @@ -52,4 +56,8 @@ void grpc_register_tracer(grpc_tracer_flag *flag); void grpc_tracer_init(const char *env_var_name); void grpc_tracer_shutdown(void); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_DEBUG_TRACE_H */ diff --git a/src/core/lib/http/format_request.h b/src/core/lib/http/format_request.h index 12b42e42fa..a559aac660 100644 --- a/src/core/lib/http/format_request.h +++ b/src/core/lib/http/format_request.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/http/httpcli.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_slice grpc_httpcli_format_get_request(const grpc_httpcli_request *request); grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, const char *body_bytes, @@ -29,4 +33,8 @@ grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, grpc_slice grpc_httpcli_format_connect_request( const grpc_httpcli_request *request); -#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_HTTP_FORMAT_REQUEST_H */ \ No newline at end of file diff --git a/src/core/lib/http/httpcli.h b/src/core/lib/http/httpcli.h index 809618695e..630481da54 100644 --- a/src/core/lib/http/httpcli.h +++ b/src/core/lib/http/httpcli.h @@ -32,6 +32,10 @@ /* User agent this library reports */ #define GRPC_HTTPCLI_USER_AGENT "grpc-httpcli/0.0" +#ifdef __cplusplus +extern "C" { +#endif + /* Tracks in-progress http requests TODO(ctiller): allow caching and capturing multiple requests for the same content and combining them */ @@ -123,4 +127,8 @@ typedef int (*grpc_httpcli_post_override)( void grpc_httpcli_set_override(grpc_httpcli_get_override get, grpc_httpcli_post_override post); -#endif /* GRPC_CORE_LIB_HTTP_HTTPCLI_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_HTTP_HTTPCLI_H */ \ No newline at end of file diff --git a/src/core/lib/http/parser.h b/src/core/lib/http/parser.h index c8dced3901..5484948bea 100644 --- a/src/core/lib/http/parser.h +++ b/src/core/lib/http/parser.h @@ -27,6 +27,10 @@ /* Maximum length of a header string of the form 'Key: Value\r\n' */ #define GRPC_HTTP_PARSER_MAX_HEADER_LENGTH 4096 +#ifdef __cplusplus +extern "C" { +#endif + /* A single header to be passed in a request */ typedef struct grpc_http_header { char *key; @@ -109,4 +113,8 @@ void grpc_http_response_destroy(grpc_http_response *response); extern grpc_tracer_flag grpc_http1_trace; -#endif /* GRPC_CORE_LIB_HTTP_PARSER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_HTTP_PARSER_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/call_combiner.h b/src/core/lib/iomgr/call_combiner.h index 5cfb3f0c07..527f84fce0 100644 --- a/src/core/lib/iomgr/call_combiner.h +++ b/src/core/lib/iomgr/call_combiner.h @@ -27,6 +27,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/support/mpscq.h" +#ifdef __cplusplus +extern "C" { +#endif + // A simple, lock-free mechanism for serializing activity related to a // single call. This is similar to a combiner but is more lightweight. // @@ -118,4 +122,8 @@ void grpc_call_combiner_cancel(grpc_exec_ctx* exec_ctx, grpc_call_combiner* call_combiner, grpc_error* error); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_CALL_COMBINER_H */ diff --git a/src/core/lib/iomgr/combiner.h b/src/core/lib/iomgr/combiner.h index 8e0434369d..10e5fb480d 100644 --- a/src/core/lib/iomgr/combiner.h +++ b/src/core/lib/iomgr/combiner.h @@ -26,6 +26,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/support/mpscq.h" +#ifdef __cplusplus +extern "C" { +#endif + // Provides serialized access to some resource. // Each action queued on a combiner is executed serially in a borrowed thread. // The actual thread executing actions may change over time (but there will only @@ -63,4 +67,8 @@ bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx); extern grpc_tracer_flag grpc_combiner_trace; +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_COMBINER_H */ diff --git a/src/core/lib/iomgr/endpoint.h b/src/core/lib/iomgr/endpoint.h index 8f0523a981..16ff0ab733 100644 --- a/src/core/lib/iomgr/endpoint.h +++ b/src/core/lib/iomgr/endpoint.h @@ -26,6 +26,10 @@ #include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/resource_quota.h" +#ifdef __cplusplus +extern "C" { +#endif + /* An endpoint caps a streaming channel between two communicating processes. Examples may be: a tcp socket, , or some shared memory. */ @@ -95,4 +99,8 @@ struct grpc_endpoint { const grpc_endpoint_vtable *vtable; }; -#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/endpoint_pair.h b/src/core/lib/iomgr/endpoint_pair.h index b60e62fc3e..f8830022f4 100644 --- a/src/core/lib/iomgr/endpoint_pair.h +++ b/src/core/lib/iomgr/endpoint_pair.h @@ -21,6 +21,10 @@ #include "src/core/lib/iomgr/endpoint.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { grpc_endpoint *client; grpc_endpoint *server; @@ -29,4 +33,8 @@ typedef struct { grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, grpc_channel_args *args); -#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_ENDPOINT_PAIR_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/error_internal.h b/src/core/lib/iomgr/error_internal.h index 7507484557..f718e06d4e 100644 --- a/src/core/lib/iomgr/error_internal.h +++ b/src/core/lib/iomgr/error_internal.h @@ -24,6 +24,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_linked_error grpc_linked_error; struct grpc_linked_error { @@ -57,4 +61,8 @@ struct grpc_error { bool grpc_error_is_special(grpc_error *err); -#endif /* GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_ERROR_INTERNAL_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/ev_epoll1_linux.h b/src/core/lib/iomgr/ev_epoll1_linux.h index 0696e0df40..66fd826b49 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.h +++ b/src/core/lib/iomgr/ev_epoll1_linux.h @@ -22,8 +22,16 @@ #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/port.h" +#ifdef __cplusplus +extern "C" { +#endif + // a polling engine that utilizes a singleton epoll set and turnstile polling const grpc_event_engine_vtable *grpc_init_epoll1_linux(bool explicit_request); -#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLL1_LINUX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/ev_epollex_linux.h b/src/core/lib/iomgr/ev_epollex_linux.h index cff9b43c02..58cc5a24f8 100644 --- a/src/core/lib/iomgr/ev_epollex_linux.h +++ b/src/core/lib/iomgr/ev_epollex_linux.h @@ -22,7 +22,15 @@ #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/port.h" +#ifdef __cplusplus +extern "C" { +#endif + const grpc_event_engine_vtable *grpc_init_epollex_linux( bool explicitly_requested); -#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLLEX_LINUX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/ev_epollsig_linux.h b/src/core/lib/iomgr/ev_epollsig_linux.h index 88328682b3..c04ff27400 100644 --- a/src/core/lib/iomgr/ev_epollsig_linux.h +++ b/src/core/lib/iomgr/ev_epollsig_linux.h @@ -22,6 +22,10 @@ #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/port.h" +#ifdef __cplusplus +extern "C" { +#endif + const grpc_event_engine_vtable *grpc_init_epollsig_linux(bool explicit_request); #ifdef GRPC_LINUX_EPOLL @@ -30,4 +34,8 @@ void *grpc_pollset_get_polling_island(grpc_pollset *ps); bool grpc_are_polling_islands_equal(void *p, void *q); #endif /* defined(GRPC_LINUX_EPOLL) */ +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_EV_EPOLLSIG_LINUX_H */ diff --git a/src/core/lib/iomgr/ev_poll_posix.h b/src/core/lib/iomgr/ev_poll_posix.h index d444e60944..84b68155b5 100644 --- a/src/core/lib/iomgr/ev_poll_posix.h +++ b/src/core/lib/iomgr/ev_poll_posix.h @@ -21,7 +21,15 @@ #include "src/core/lib/iomgr/ev_posix.h" +#ifdef __cplusplus +extern "C" { +#endif + const grpc_event_engine_vtable *grpc_init_poll_posix(bool explicit_request); const grpc_event_engine_vtable *grpc_init_poll_cv_posix(bool explicit_request); -#endif /* GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_EV_POLL_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/ev_posix.h b/src/core/lib/iomgr/ev_posix.h index 1ff2ff1413..5ad1c13ee6 100644 --- a/src/core/lib/iomgr/ev_posix.h +++ b/src/core/lib/iomgr/ev_posix.h @@ -27,6 +27,10 @@ #include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/wakeup_fd_posix.h" +#ifdef __cplusplus +extern "C" { +#endif + extern grpc_tracer_flag grpc_polling_trace; /* Disabled by default */ typedef struct grpc_fd grpc_fd; @@ -158,4 +162,8 @@ extern grpc_poll_function_type grpc_poll_function; void grpc_set_event_engine_test_only(const grpc_event_engine_vtable *); const grpc_event_engine_vtable *grpc_get_event_engine_test_only(); -#endif /* GRPC_CORE_LIB_IOMGR_EV_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_EV_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/exec_ctx.h b/src/core/lib/iomgr/exec_ctx.h index c89792c8c4..a93728f0a6 100644 --- a/src/core/lib/iomgr/exec_ctx.h +++ b/src/core/lib/iomgr/exec_ctx.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/iomgr/closure.h" +#ifdef __cplusplus +extern "C" { +#endif + /* #define GRPC_EXECUTION_CONTEXT_SANITIZER 1 */ /** A workqueue represents a list of work to be executed asynchronously. @@ -106,4 +110,8 @@ void grpc_exec_ctx_global_init(void); void grpc_exec_ctx_global_init(void); void grpc_exec_ctx_global_shutdown(void); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_EXEC_CTX_H */ diff --git a/src/core/lib/iomgr/executor.h b/src/core/lib/iomgr/executor.h index 0412c02790..ab3fc901de 100644 --- a/src/core/lib/iomgr/executor.h +++ b/src/core/lib/iomgr/executor.h @@ -21,6 +21,10 @@ #include "src/core/lib/iomgr/closure.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_EXECUTOR_SHORT, GRPC_EXECUTOR_LONG @@ -45,4 +49,8 @@ bool grpc_executor_is_threaded(); grpc_executor_shutdown */ void grpc_executor_set_threading(grpc_exec_ctx *exec_ctx, bool enable); -#endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_EXECUTOR_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/gethostname.h b/src/core/lib/iomgr/gethostname.h index 9c6b9d8d42..4dcf50c591 100644 --- a/src/core/lib/iomgr/gethostname.h +++ b/src/core/lib/iomgr/gethostname.h @@ -19,8 +19,16 @@ #ifndef GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H #define GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H +#ifdef __cplusplus +extern "C" { +#endif + // Returns the hostname of the local machine. // Caller takes ownership of result. char *grpc_gethostname(); -#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/iocp_windows.h b/src/core/lib/iomgr/iocp_windows.h index 9c89e868c5..341c159501 100644 --- a/src/core/lib/iomgr/iocp_windows.h +++ b/src/core/lib/iomgr/iocp_windows.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/socket_windows.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_IOCP_WORK_WORK, GRPC_IOCP_WORK_TIMEOUT, @@ -37,4 +41,8 @@ void grpc_iocp_flush(void); void grpc_iocp_shutdown(void); void grpc_iocp_add_socket(grpc_winsocket *); -#endif /* GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_IOCP_WINDOWS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/iomgr.h b/src/core/lib/iomgr/iomgr.h index e3cd6ebe79..fea08496fe 100644 --- a/src/core/lib/iomgr/iomgr.h +++ b/src/core/lib/iomgr/iomgr.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/iomgr/port.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Initializes the iomgr. */ void grpc_iomgr_init(grpc_exec_ctx *exec_ctx); @@ -32,4 +36,8 @@ void grpc_iomgr_start(grpc_exec_ctx *exec_ctx); * exec_ctx. */ void grpc_iomgr_shutdown(grpc_exec_ctx *exec_ctx); -#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/iomgr_internal.h b/src/core/lib/iomgr/iomgr_internal.h index 836d82515d..005abbed13 100644 --- a/src/core/lib/iomgr/iomgr_internal.h +++ b/src/core/lib/iomgr/iomgr_internal.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/iomgr.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_iomgr_object { char *name; struct grpc_iomgr_object *next; @@ -40,4 +44,8 @@ void grpc_iomgr_platform_shutdown(void); bool grpc_iomgr_abort_on_leaks(void); -#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_IOMGR_INTERNAL_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/is_epollexclusive_available.h b/src/core/lib/iomgr/is_epollexclusive_available.h index 1d2e133a3b..5c3e483065 100644 --- a/src/core/lib/iomgr/is_epollexclusive_available.h +++ b/src/core/lib/iomgr/is_epollexclusive_available.h @@ -21,6 +21,14 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + bool grpc_is_epollexclusive_available(void); -#endif /* GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_IS_EPOLLEXCLUSIVE_AVAILABLE_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/lockfree_event.h b/src/core/lib/iomgr/lockfree_event.h index 6a14a0f3b2..925f004945 100644 --- a/src/core/lib/iomgr/lockfree_event.h +++ b/src/core/lib/iomgr/lockfree_event.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_lfev_init(gpr_atm *state); void grpc_lfev_destroy(gpr_atm *state); bool grpc_lfev_is_shutdown(gpr_atm *state); @@ -37,4 +41,8 @@ bool grpc_lfev_set_shutdown(grpc_exec_ctx *exec_ctx, gpr_atm *state, void grpc_lfev_set_ready(grpc_exec_ctx *exec_ctx, gpr_atm *state, const char *variable); -#endif /* GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_LOCKFREE_EVENT_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/network_status_tracker.h b/src/core/lib/iomgr/network_status_tracker.h index c0295c1f74..af50d51257 100644 --- a/src/core/lib/iomgr/network_status_tracker.h +++ b/src/core/lib/iomgr/network_status_tracker.h @@ -20,6 +20,10 @@ #define GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H #include "src/core/lib/iomgr/endpoint.h" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_network_status_init(void); void grpc_network_status_shutdown(void); @@ -27,4 +31,8 @@ void grpc_network_status_register_endpoint(grpc_endpoint *ep); void grpc_network_status_unregister_endpoint(grpc_endpoint *ep); void grpc_network_status_shutdown_all_endpoints(); -#endif /* GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_NETWORK_STATUS_TRACKER_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/polling_entity.h b/src/core/lib/iomgr/polling_entity.h index a161e1fea6..4a37acf212 100644 --- a/src/core/lib/iomgr/polling_entity.h +++ b/src/core/lib/iomgr/polling_entity.h @@ -22,6 +22,10 @@ #include "src/core/lib/iomgr/pollset.h" #include "src/core/lib/iomgr/pollset_set.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum grpc_pollset_tag { GRPC_POLLS_NONE, GRPC_POLLS_POLLSET, @@ -64,4 +68,8 @@ void grpc_polling_entity_add_to_pollset_set(grpc_exec_ctx *exec_ctx, void grpc_polling_entity_del_from_pollset_set(grpc_exec_ctx *exec_ctx, grpc_polling_entity *pollent, grpc_pollset_set *pss_dst); -#endif /* GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_POLLING_ENTITY_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/pollset.h b/src/core/lib/iomgr/pollset.h index a0f6b3a9d3..28d63949ea 100644 --- a/src/core/lib/iomgr/pollset.h +++ b/src/core/lib/iomgr/pollset.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + #ifndef NDEBUG extern grpc_tracer_flag grpc_trace_fd_refcount; #endif @@ -80,4 +84,8 @@ grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, grpc_pollset_worker *specific_worker) GRPC_MUST_USE_RESULT; +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_POLLSET_H */ diff --git a/src/core/lib/iomgr/pollset_set.h b/src/core/lib/iomgr/pollset_set.h index 29c0f03561..17df86542d 100644 --- a/src/core/lib/iomgr/pollset_set.h +++ b/src/core/lib/iomgr/pollset_set.h @@ -21,6 +21,10 @@ #include "src/core/lib/iomgr/pollset.h" +#ifdef __cplusplus +extern "C" { +#endif + /* A grpc_pollset_set is a set of pollsets that are interested in an action. Adding a pollset to a pollset_set automatically adds any fd's (etc) that have been registered with the set_set to that pollset. @@ -44,4 +48,8 @@ void grpc_pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, grpc_pollset_set *bag, grpc_pollset_set *item); -#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_SET_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/pollset_uv.h b/src/core/lib/iomgr/pollset_uv.h index 566c110ca6..d8f72ff867 100644 --- a/src/core/lib/iomgr/pollset_uv.h +++ b/src/core/lib/iomgr/pollset_uv.h @@ -19,9 +19,17 @@ #ifndef GRPC_CORE_LIB_IOMGR_POLLSET_UV_H #define GRPC_CORE_LIB_IOMGR_POLLSET_UV_H +#ifdef __cplusplus +extern "C" { +#endif + extern int grpc_pollset_work_run_loop; void grpc_pollset_global_init(void); void grpc_pollset_global_shutdown(void); -#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_UV_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/pollset_windows.h b/src/core/lib/iomgr/pollset_windows.h index 71878c3d30..7733d26471 100644 --- a/src/core/lib/iomgr/pollset_windows.h +++ b/src/core/lib/iomgr/pollset_windows.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/socket_windows.h" +#ifdef __cplusplus +extern "C" { +#endif + /* There isn't really any such thing as a pollset under Windows, due to the nature of the IO completion ports. A Windows "pollset" is merely a mutex used to synchronize with the IOCP, and workers are condition variables @@ -60,4 +64,8 @@ struct grpc_pollset { void grpc_pollset_global_init(void); void grpc_pollset_global_shutdown(void); -#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_POLLSET_WINDOWS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/resolve_address.h b/src/core/lib/iomgr/resolve_address.h index fe1dd78576..4a6df2cf26 100644 --- a/src/core/lib/iomgr/resolve_address.h +++ b/src/core/lib/iomgr/resolve_address.h @@ -25,6 +25,10 @@ #define GRPC_MAX_SOCKADDR_SIZE 128 +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { char addr[GRPC_MAX_SOCKADDR_SIZE]; size_t len; @@ -52,4 +56,8 @@ extern grpc_error *(*grpc_blocking_resolve_address)( const char *name, const char *default_port, grpc_resolved_addresses **addresses); -#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_RESOLVE_ADDRESS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/resource_quota.h b/src/core/lib/iomgr/resource_quota.h index d66f9ae774..3afb525434 100644 --- a/src/core/lib/iomgr/resource_quota.h +++ b/src/core/lib/iomgr/resource_quota.h @@ -24,6 +24,10 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + /** \file Tracks resource usage against a pool. The current implementation tracks only memory usage, but in the future @@ -150,4 +154,8 @@ grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx *exec_ctx, grpc_resource_user *resource_user, size_t size); -#endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_RESOURCE_QUOTA_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/sockaddr_utils.h b/src/core/lib/iomgr/sockaddr_utils.h index a589a19705..129bb54fc9 100644 --- a/src/core/lib/iomgr/sockaddr_utils.h +++ b/src/core/lib/iomgr/sockaddr_utils.h @@ -21,6 +21,10 @@ #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Returns true if addr is an IPv4-mapped IPv6 address within the ::ffff:0.0.0.0/96 range, or false otherwise. @@ -77,4 +81,8 @@ const char *grpc_sockaddr_get_uri_scheme(const grpc_resolved_address *addr); int grpc_sockaddr_get_family(const grpc_resolved_address *resolved_addr); -#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKADDR_UTILS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/socket_utils.h b/src/core/lib/iomgr/socket_utils.h index 03fe46e5e9..f319e931b6 100644 --- a/src/core/lib/iomgr/socket_utils.h +++ b/src/core/lib/iomgr/socket_utils.h @@ -21,7 +21,15 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* A wrapper for inet_ntop on POSIX systems and InetNtop on Windows systems */ const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size); -#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/socket_utils_posix.h b/src/core/lib/iomgr/socket_utils_posix.h index eef80b439e..623b83f08b 100644 --- a/src/core/lib/iomgr/socket_utils_posix.h +++ b/src/core/lib/iomgr/socket_utils_posix.h @@ -29,6 +29,10 @@ #include "src/core/lib/iomgr/socket_factory_posix.h" #include "src/core/lib/iomgr/socket_mutator.h" +#ifdef __cplusplus +extern "C" { +#endif + /* a wrapper for accept or accept4 */ int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, int cloexec); @@ -129,4 +133,8 @@ grpc_error *grpc_create_dualstack_socket_using_factory( grpc_socket_factory *factory, const grpc_resolved_address *addr, int type, int protocol, grpc_dualstack_mode *dsmode, int *newfd); -#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_UTILS_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/socket_windows.h b/src/core/lib/iomgr/socket_windows.h index 67dc4ca53e..a00a7615a3 100644 --- a/src/core/lib/iomgr/socket_windows.h +++ b/src/core/lib/iomgr/socket_windows.h @@ -28,6 +28,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr_internal.h" +#ifdef __cplusplus +extern "C" { +#endif + /* This holds the data for an outstanding read or write on a socket. The mutex to protect the concurrent access to that data is the one inside the winsocket wrapper. */ @@ -107,4 +111,8 @@ void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx, grpc_winsocket *winsocket, grpc_winsocket_callback_info *ci); -#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_SOCKET_WINDOWS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_client.h b/src/core/lib/iomgr/tcp_client.h index 6c9e51ae84..18cf6114f2 100644 --- a/src/core/lib/iomgr/tcp_client.h +++ b/src/core/lib/iomgr/tcp_client.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/pollset_set.h" #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Asynchronously connect to an address (specified as (addr, len)), and call cb with arg and the completed connection when done (or call cb with arg and NULL on failure). @@ -37,4 +41,8 @@ void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *on_connect, const grpc_resolved_address *addr, gpr_timespec deadline); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_client_posix.h b/src/core/lib/iomgr/tcp_client_posix.h index b5a3814799..0b9775504c 100644 --- a/src/core/lib/iomgr/tcp_client_posix.h +++ b/src/core/lib/iomgr/tcp_client_posix.h @@ -23,8 +23,16 @@ #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/tcp_client.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_endpoint *grpc_tcp_client_create_from_fd( grpc_exec_ctx *exec_ctx, grpc_fd *fd, const grpc_channel_args *channel_args, const char *addr_str); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_CLIENT_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_posix.h b/src/core/lib/iomgr/tcp_posix.h index 6831a4a57f..dda78b2f8e 100644 --- a/src/core/lib/iomgr/tcp_posix.h +++ b/src/core/lib/iomgr/tcp_posix.h @@ -33,6 +33,10 @@ #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/ev_posix.h" +#ifdef __cplusplus +extern "C" { +#endif + extern grpc_tracer_flag grpc_tcp_trace; /* Create a tcp endpoint given a file desciptor and a read slice size. @@ -53,4 +57,8 @@ int grpc_tcp_fd(grpc_endpoint *ep); void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, int *fd, grpc_closure *done); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_server.h b/src/core/lib/iomgr/tcp_server.h index 8a126b6dee..3f190ac285 100644 --- a/src/core/lib/iomgr/tcp_server.h +++ b/src/core/lib/iomgr/tcp_server.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Forward decl of grpc_tcp_server */ typedef struct grpc_tcp_server grpc_tcp_server; @@ -98,4 +102,8 @@ void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s); void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_server_utils_posix.h b/src/core/lib/iomgr/tcp_server_utils_posix.h index 85dea515d9..4bb0660f09 100644 --- a/src/core/lib/iomgr/tcp_server_utils_posix.h +++ b/src/core/lib/iomgr/tcp_server_utils_posix.h @@ -24,6 +24,10 @@ #include "src/core/lib/iomgr/socket_utils_posix.h" #include "src/core/lib/iomgr/tcp_server.h" +#ifdef __cplusplus +extern "C" { +#endif + /* one listening port */ typedef struct grpc_tcp_listener { int fd; @@ -117,4 +121,8 @@ grpc_error *grpc_tcp_server_prepare_socket(int fd, /* Ruturn true if the platform supports ifaddrs */ bool grpc_tcp_server_have_ifaddrs(void); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_SERVER_UTILS_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_uv.h b/src/core/lib/iomgr/tcp_uv.h index 0e67481d35..ba7db8a0f7 100644 --- a/src/core/lib/iomgr/tcp_uv.h +++ b/src/core/lib/iomgr/tcp_uv.h @@ -38,8 +38,16 @@ extern grpc_tracer_flag grpc_tcp_trace; #define GRPC_TCP_DEFAULT_READ_SLICE_SIZE 8192 +#ifdef __cplusplus +extern "C" { +#endif + grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, grpc_resource_quota *resource_quota, char *peer_string); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_UV_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_UV_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/tcp_windows.h b/src/core/lib/iomgr/tcp_windows.h index 864184ce84..1c935da2a2 100644 --- a/src/core/lib/iomgr/tcp_windows.h +++ b/src/core/lib/iomgr/tcp_windows.h @@ -32,6 +32,10 @@ #include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/socket_windows.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Create a tcp endpoint given a winsock handle. * Takes ownership of the handle. */ @@ -41,4 +45,8 @@ grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, grpc_error *grpc_tcp_prepare_socket(SOCKET sock); -#endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TCP_WINDOWS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/time_averaged_stats.h b/src/core/lib/iomgr/time_averaged_stats.h index 8745f7fa13..e255b58fee 100644 --- a/src/core/lib/iomgr/time_averaged_stats.h +++ b/src/core/lib/iomgr/time_averaged_stats.h @@ -19,6 +19,10 @@ #ifndef GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H #define GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H +#ifdef __cplusplus +extern "C" { +#endif + /* This tracks a time-decaying weighted average. It works by collecting batches of samples and then mixing their average into a time-decaying weighted mean. It is designed for batch operations where we do many adds @@ -70,4 +74,8 @@ void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats, value. */ double grpc_time_averaged_stats_update_average(grpc_time_averaged_stats* stats); -#endif /* GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TIME_AVERAGED_STATS_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/timer.h b/src/core/lib/iomgr/timer.h index ac392f87fe..466600d582 100644 --- a/src/core/lib/iomgr/timer.h +++ b/src/core/lib/iomgr/timer.h @@ -32,6 +32,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/iomgr/iomgr.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_timer grpc_timer; /* Initialize *timer. When expired or canceled, closure will be called with @@ -103,4 +107,8 @@ void grpc_timer_consume_kick(void); void grpc_kick_poller(void); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_TIMER_H */ diff --git a/src/core/lib/iomgr/timer_heap.h b/src/core/lib/iomgr/timer_heap.h index 0d64199ab9..f15e8a3abb 100644 --- a/src/core/lib/iomgr/timer_heap.h +++ b/src/core/lib/iomgr/timer_heap.h @@ -21,6 +21,10 @@ #include "src/core/lib/iomgr/timer.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { grpc_timer **timers; uint32_t timer_count; @@ -39,4 +43,8 @@ void grpc_timer_heap_pop(grpc_timer_heap *heap); int grpc_timer_heap_is_empty(grpc_timer_heap *heap); -#endif /* GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_HEAP_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/timer_manager.h b/src/core/lib/iomgr/timer_manager.h index 0ba502928a..d8a59a9477 100644 --- a/src/core/lib/iomgr/timer_manager.h +++ b/src/core/lib/iomgr/timer_manager.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Timer Manager tries to keep one thread waiting for the next timeout at all times */ @@ -34,4 +38,8 @@ void grpc_timer_manager_set_threading(bool enabled); * disabled */ void grpc_timer_manager_tick(void); -#endif /* GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_TIMER_MANAGER_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/udp_server.h b/src/core/lib/iomgr/udp_server.h index 881468ea2c..bcd8572260 100644 --- a/src/core/lib/iomgr/udp_server.h +++ b/src/core/lib/iomgr/udp_server.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/ev_posix.h" #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Forward decl of struct grpc_server */ /* This is not typedef'ed to avoid a typedef-redefinition error */ struct grpc_server; @@ -73,4 +77,8 @@ int grpc_udp_server_add_port(grpc_udp_server *s, void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *server, grpc_closure *on_done); -#endif /* GRPC_CORE_LIB_IOMGR_UDP_SERVER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_UDP_SERVER_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/unix_sockets_posix.h b/src/core/lib/iomgr/unix_sockets_posix.h index 25b64b3eec..b96131ae1c 100644 --- a/src/core/lib/iomgr/unix_sockets_posix.h +++ b/src/core/lib/iomgr/unix_sockets_posix.h @@ -25,6 +25,10 @@ #include "src/core/lib/iomgr/resolve_address.h" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_create_socketpair_if_unix(int sv[2]); grpc_error *grpc_resolve_unix_domain_address( @@ -38,4 +42,8 @@ void grpc_unlink_if_unix_domain_socket( char *grpc_sockaddr_to_uri_unix_if_possible( const grpc_resolved_address *resolved_addr); -#endif /* GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_UNIX_SOCKETS_POSIX_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/wakeup_fd_cv.h b/src/core/lib/iomgr/wakeup_fd_cv.h index dc170ad5b4..34bc42fd7f 100644 --- a/src/core/lib/iomgr/wakeup_fd_cv.h +++ b/src/core/lib/iomgr/wakeup_fd_cv.h @@ -40,6 +40,10 @@ #define GRPC_FD_TO_IDX(fd) (-(fd)-1) #define GRPC_IDX_TO_FD(idx) (-(idx)-1) +#ifdef __cplusplus +extern "C" { +#endif + typedef struct cv_node { gpr_cv* cv; struct cv_node* next; @@ -62,4 +66,8 @@ typedef struct cv_fd_table { grpc_poll_function_type poll; } cv_fd_table; -#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_CV_H */ \ No newline at end of file diff --git a/src/core/lib/iomgr/wakeup_fd_posix.h b/src/core/lib/iomgr/wakeup_fd_posix.h index a9584d0d48..ae7849f98c 100644 --- a/src/core/lib/iomgr/wakeup_fd_posix.h +++ b/src/core/lib/iomgr/wakeup_fd_posix.h @@ -49,6 +49,10 @@ #include "src/core/lib/iomgr/error.h" +#ifdef __cplusplus +extern "C" { +#endif + void grpc_wakeup_fd_global_init(void); void grpc_wakeup_fd_global_destroy(void); @@ -91,4 +95,8 @@ void grpc_wakeup_fd_destroy(grpc_wakeup_fd* fd_info); * wakeup_fd_nospecial.c if no such implementation exists. */ extern const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable; +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_IOMGR_WAKEUP_FD_POSIX_H */ diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h index bbd43025eb..81b7e0c9da 100644 --- a/src/core/lib/json/json.h +++ b/src/core/lib/json/json.h @@ -23,6 +23,10 @@ #include "src/core/lib/json/json_common.h" +#ifdef __cplusplus +extern "C" { +#endif + /* A tree-like structure to hold json values. The key and value pointers * are not owned by it. */ @@ -70,4 +74,8 @@ char* grpc_json_dump_to_string(grpc_json* json, int indent); grpc_json* grpc_json_create(grpc_json_type type); void grpc_json_destroy(grpc_json* json); -#endif /* GRPC_CORE_LIB_JSON_JSON_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_JSON_JSON_H */ \ No newline at end of file diff --git a/src/core/lib/json/json_reader.h b/src/core/lib/json/json_reader.h index 577fbbbaf6..ab2384f7a7 100644 --- a/src/core/lib/json/json_reader.h +++ b/src/core/lib/json/json_reader.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/json/json_common.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { GRPC_JSON_STATE_OBJECT_KEY_BEGIN, GRPC_JSON_STATE_OBJECT_KEY_STRING, @@ -142,4 +146,8 @@ void grpc_json_reader_init(grpc_json_reader *reader, */ int grpc_json_reader_is_complete(grpc_json_reader *reader); -#endif /* GRPC_CORE_LIB_JSON_JSON_READER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_JSON_JSON_READER_H */ \ No newline at end of file diff --git a/src/core/lib/json/json_writer.h b/src/core/lib/json/json_writer.h index 8779039d42..18bd2a80fe 100644 --- a/src/core/lib/json/json_writer.h +++ b/src/core/lib/json/json_writer.h @@ -35,6 +35,10 @@ #include "src/core/lib/json/json_common.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_json_writer_vtable { /* Adds a character to the output stream. */ void (*output_char)(void *userdata, char); @@ -79,4 +83,8 @@ void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, void grpc_json_writer_value_string(grpc_json_writer *writer, const char *string); -#endif /* GRPC_CORE_LIB_JSON_JSON_WRITER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_JSON_JSON_WRITER_H */ \ No newline at end of file diff --git a/src/core/lib/profiling/basic_timers.c b/src/core/lib/profiling/basic_timers.c index c7645b74f2..ab9d60481c 100644 --- a/src/core/lib/profiling/basic_timers.c +++ b/src/core/lib/profiling/basic_timers.c @@ -18,10 +18,10 @@ #include -#ifdef GRPC_BASIC_PROFILER - #include "src/core/lib/profiling/timers.h" +#ifdef GRPC_BASIC_PROFILER + #include #include #include diff --git a/src/core/lib/security/credentials/composite/composite_credentials.h b/src/core/lib/security/credentials/composite/composite_credentials.h index 3076afcb7e..6e9f9a8f6f 100644 --- a/src/core/lib/security/credentials/composite/composite_credentials.h +++ b/src/core/lib/security/credentials/composite/composite_credentials.h @@ -21,6 +21,10 @@ #include "src/core/lib/security/credentials/credentials.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { grpc_call_credentials **creds_array; size_t num_creds; @@ -53,5 +57,9 @@ typedef struct { grpc_call_credentials_array inner; } grpc_composite_call_credentials; +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_COMPOSITE_COMPOSITE_CREDENTIALS_H \ */ diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h index 04a54b0ca8..73e39ae039 100644 --- a/src/core/lib/security/credentials/credentials.h +++ b/src/core/lib/security/credentials/credentials.h @@ -29,6 +29,10 @@ #include "src/core/lib/iomgr/polling_entity.h" #include "src/core/lib/security/transport/security_connector.h" +#ifdef __cplusplus +extern "C" { +#endif + struct grpc_http_response; /* --- Constants. --- */ @@ -252,4 +256,8 @@ grpc_credentials_metadata_request *grpc_credentials_metadata_request_create( void grpc_credentials_metadata_request_destroy( grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *r); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_CREDENTIALS_H */ diff --git a/src/core/lib/security/credentials/fake/fake_credentials.h b/src/core/lib/security/credentials/fake/fake_credentials.h index aa0f3b6e20..64f6f439f0 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.h +++ b/src/core/lib/security/credentials/fake/fake_credentials.h @@ -21,6 +21,10 @@ #include "src/core/lib/security/credentials/credentials.h" +#ifdef __cplusplus +extern "C" { +#endif + /* -- Fake transport security credentials. -- */ /* Creates a fake transport security credentials object for testing. */ @@ -56,4 +60,8 @@ typedef struct { bool is_async; } grpc_md_only_test_credentials; -#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H */ \ No newline at end of file diff --git a/src/core/lib/security/credentials/jwt/json_token.h b/src/core/lib/security/credentials/jwt/json_token.h index e50790ef2e..486fd981af 100644 --- a/src/core/lib/security/credentials/jwt/json_token.h +++ b/src/core/lib/security/credentials/jwt/json_token.h @@ -28,6 +28,10 @@ #define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token" +#ifdef __cplusplus +extern "C" { +#endif + /* --- auth_json_key parsing. --- */ typedef struct { @@ -70,4 +74,8 @@ typedef char *(*grpc_jwt_encode_and_sign_override)( void grpc_jwt_encode_and_sign_set_override( grpc_jwt_encode_and_sign_override func); -#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */ \ No newline at end of file diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h index 07f4022669..c09485fd55 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.h +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h @@ -22,6 +22,10 @@ #include "src/core/lib/security/credentials/credentials.h" #include "src/core/lib/security/credentials/jwt/json_token.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { grpc_call_credentials base; @@ -45,4 +49,8 @@ grpc_service_account_jwt_access_credentials_create_from_auth_json_key( grpc_exec_ctx *exec_ctx, grpc_auth_json_key key, gpr_timespec token_lifetime); -#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_CREDENTIALS_H */ \ No newline at end of file diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.h b/src/core/lib/security/credentials/jwt/jwt_verifier.h index 8fac452d4e..0603811627 100644 --- a/src/core/lib/security/credentials/jwt/jwt_verifier.h +++ b/src/core/lib/security/credentials/jwt/jwt_verifier.h @@ -32,6 +32,10 @@ #define GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX \ "www.googleapis.com/robot/v1/metadata/x509" +#ifdef __cplusplus +extern "C" { +#endif + /* --- grpc_jwt_verifier_status. --- */ typedef enum { @@ -122,4 +126,8 @@ grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims, const char *audience); const char *grpc_jwt_issuer_email_domain(const char *issuer); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JWT_VERIFIER_H */ diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h index d9ad6691b8..c8a9333417 100644 --- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h +++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h @@ -22,6 +22,10 @@ #include "src/core/lib/json/json.h" #include "src/core/lib/security/credentials/credentials.h" +#ifdef __cplusplus +extern "C" { +#endif + // auth_refresh_token parsing. typedef struct { const char *type; @@ -102,4 +106,8 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( grpc_exec_ctx *exec_ctx, const struct grpc_http_response *response, grpc_mdelem *token_md, gpr_timespec *token_lifetime); -#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */ \ No newline at end of file diff --git a/src/core/lib/security/transport/lb_targets_info.h b/src/core/lib/security/transport/lb_targets_info.h index c3d685df5f..705d33b0ab 100644 --- a/src/core/lib/security/transport/lb_targets_info.h +++ b/src/core/lib/security/transport/lb_targets_info.h @@ -21,6 +21,10 @@ #include "src/core/lib/slice/slice_hash_table.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Return a channel argument containing \a targets_info. */ grpc_arg grpc_lb_targets_info_create_channel_arg( grpc_slice_hash_table *targets_info); @@ -29,4 +33,8 @@ grpc_arg grpc_lb_targets_info_create_channel_arg( grpc_slice_hash_table *grpc_lb_targets_info_find_in_args( const grpc_channel_args *args); -#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_LB_TARGETS_INFO_H */ \ No newline at end of file diff --git a/src/core/lib/security/transport/secure_endpoint.h b/src/core/lib/security/transport/secure_endpoint.h index 3323a6ff42..832cc1c0ce 100644 --- a/src/core/lib/security/transport/secure_endpoint.h +++ b/src/core/lib/security/transport/secure_endpoint.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/iomgr/endpoint.h" +#ifdef __cplusplus +extern "C" { +#endif + struct tsi_frame_protector; struct tsi_zero_copy_grpc_protector; @@ -36,4 +40,8 @@ grpc_endpoint *grpc_secure_endpoint_create( grpc_endpoint *to_wrap, grpc_slice *leftover_slices, size_t leftover_nslices); -#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURE_ENDPOINT_H */ \ No newline at end of file diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h index 4f9b63ad20..4d87cd0c80 100644 --- a/src/core/lib/security/transport/security_connector.h +++ b/src/core/lib/security/transport/security_connector.h @@ -29,6 +29,10 @@ #include "src/core/tsi/ssl_transport_security.h" #include "src/core/tsi/transport_security_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + #ifndef NDEBUG extern grpc_tracer_flag grpc_trace_security_connector_refcount; #endif @@ -245,4 +249,8 @@ tsi_peer tsi_shallow_peer_from_ssl_auth_context( const grpc_auth_context *auth_context); void tsi_shallow_peer_destruct(tsi_peer *peer); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_CONNECTOR_H */ diff --git a/src/core/lib/security/transport/security_handshaker.h b/src/core/lib/security/transport/security_handshaker.h index 95bf127fc6..345065f26c 100644 --- a/src/core/lib/security/transport/security_handshaker.h +++ b/src/core/lib/security/transport/security_handshaker.h @@ -23,6 +23,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" #include "src/core/lib/security/transport/security_connector.h" +#ifdef __cplusplus +extern "C" { +#endif + /// Creates a security handshaker using \a handshaker. grpc_handshaker *grpc_security_handshaker_create( grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, @@ -31,4 +35,8 @@ grpc_handshaker *grpc_security_handshaker_create( /// Registers security handshaker factories. void grpc_security_register_handshaker_factories(); -#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_SECURITY_HANDSHAKER_H */ \ No newline at end of file diff --git a/src/core/lib/security/transport/tsi_error.h b/src/core/lib/security/transport/tsi_error.h index 87a63a8a7c..4c78b06603 100644 --- a/src/core/lib/security/transport/tsi_error.h +++ b/src/core/lib/security/transport/tsi_error.h @@ -22,6 +22,14 @@ #include "src/core/lib/iomgr/error.h" #include "src/core/tsi/transport_security_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_error *grpc_set_tsi_error_result(grpc_error *error, tsi_result result); -#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_TRANSPORT_TSI_ERROR_H */ \ No newline at end of file diff --git a/src/core/lib/security/util/json_util.h b/src/core/lib/security/util/json_util.h index 5ea831e27e..43a2f6b9d1 100644 --- a/src/core/lib/security/util/json_util.h +++ b/src/core/lib/security/util/json_util.h @@ -28,6 +28,10 @@ #define GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT "service_account" #define GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER "authorized_user" +#ifdef __cplusplus +extern "C" { +#endif + // Gets a child property from a json node. const char *grpc_json_get_string_property(const grpc_json *json, const char *prop_name); @@ -37,4 +41,8 @@ const char *grpc_json_get_string_property(const grpc_json *json, bool grpc_copy_json_string_property(const grpc_json *json, const char *prop_name, char **copied_value); -#endif /* GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SECURITY_UTIL_JSON_UTIL_H */ \ No newline at end of file diff --git a/src/core/lib/slice/b64.h b/src/core/lib/slice/b64.h index 3fd15febe5..c01da56575 100644 --- a/src/core/lib/slice/b64.h +++ b/src/core/lib/slice/b64.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Encodes data using base64. It is the caller's responsability to free the returned char * using gpr_free. Returns NULL on NULL input. TODO(makdharma) : change the flags to bool from int */ @@ -47,4 +51,8 @@ grpc_slice grpc_base64_decode(grpc_exec_ctx *exec_ctx, const char *b64, grpc_slice grpc_base64_decode_with_len(grpc_exec_ctx *exec_ctx, const char *b64, size_t b64_len, int url_safe); -#endif /* GRPC_CORE_LIB_SLICE_B64_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SLICE_B64_H */ \ No newline at end of file diff --git a/src/core/lib/slice/percent_encoding.h b/src/core/lib/slice/percent_encoding.h index faae26a683..e6f85120c3 100644 --- a/src/core/lib/slice/percent_encoding.h +++ b/src/core/lib/slice/percent_encoding.h @@ -30,6 +30,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* URL percent encoding spec bitfield (usabel as 'unreserved_bytes' in grpc_percent_encode_slice, grpc_strict_percent_decode_slice). Flags [A-Za-z0-9-_.~] as unreserved bytes for the percent encoding routines @@ -60,4 +64,8 @@ bool grpc_strict_percent_decode_slice(grpc_slice slice_in, This cannot fail. */ grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in); -#endif /* GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SLICE_PERCENT_ENCODING_H */ \ No newline at end of file diff --git a/src/core/lib/slice/slice_hash_table.h b/src/core/lib/slice/slice_hash_table.h index 339078fef5..3c3f0e61f3 100644 --- a/src/core/lib/slice/slice_hash_table.h +++ b/src/core/lib/slice/slice_hash_table.h @@ -19,6 +19,10 @@ #include "src/core/lib/transport/metadata.h" +#ifdef __cplusplus +extern "C" { +#endif + /** Hash table implementation. * * This implementation uses open addressing @@ -67,4 +71,8 @@ void *grpc_slice_hash_table_get(const grpc_slice_hash_table *table, int grpc_slice_hash_table_cmp(const grpc_slice_hash_table *a, const grpc_slice_hash_table *b); -#endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_HASH_TABLE_H */ \ No newline at end of file diff --git a/src/core/lib/slice/slice_internal.h b/src/core/lib/slice/slice_internal.h index 6df0b4b50b..8591185c53 100644 --- a/src/core/lib/slice/slice_internal.h +++ b/src/core/lib/slice/slice_internal.h @@ -24,6 +24,10 @@ #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_slice grpc_slice_ref_internal(grpc_slice slice); void grpc_slice_unref_internal(grpc_exec_ctx *exec_ctx, grpc_slice slice); void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx *exec_ctx, @@ -46,4 +50,8 @@ grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice, uint32_t grpc_static_slice_hash(grpc_slice s); int grpc_static_slice_eq(grpc_slice a, grpc_slice b); -#endif /* GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_INTERNAL_H */ \ No newline at end of file diff --git a/src/core/lib/slice/slice_traits.h b/src/core/lib/slice/slice_traits.h index 4b898bdcd4..1eda17cf00 100644 --- a/src/core/lib/slice/slice_traits.h +++ b/src/core/lib/slice/slice_traits.h @@ -22,8 +22,16 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + bool grpc_slice_is_legal_header(grpc_slice s); bool grpc_slice_is_legal_nonbin_header(grpc_slice s); bool grpc_slice_is_bin_suffixed(grpc_slice s); -#endif /* GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SLICE_SLICE_TRAITS_H */ \ No newline at end of file diff --git a/src/core/lib/support/time_precise.c b/src/core/lib/support/time_precise.c index 6ce19e53cc..05ef7c59bc 100644 --- a/src/core/lib/support/time_precise.c +++ b/src/core/lib/support/time_precise.c @@ -20,6 +20,8 @@ #include #include +#include "src/core/lib/support/time_precise.h" + #ifdef GRPC_TIMERS_RDTSC #if defined(__i386__) static void gpr_get_cycle_counter(int64_t int *clk) { diff --git a/src/core/lib/surface/channel.h b/src/core/lib/surface/channel.h index 528bb868e2..427422b565 100644 --- a/src/core/lib/surface/channel.h +++ b/src/core/lib/surface/channel.h @@ -23,6 +23,10 @@ #include "src/core/lib/channel/channel_stack_builder.h" #include "src/core/lib/surface/channel_stack_type.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target, const grpc_channel_args *args, grpc_channel_stack_type channel_stack_type, @@ -81,4 +85,8 @@ void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, grpc_compression_options grpc_channel_compression_options( const grpc_channel *channel); +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_H */ diff --git a/src/core/lib/surface/channel_stack_type.h b/src/core/lib/surface/channel_stack_type.h index 3f0e14ffc0..903b90a071 100644 --- a/src/core/lib/surface/channel_stack_type.h +++ b/src/core/lib/surface/channel_stack_type.h @@ -21,6 +21,10 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { // normal top-half client channel with load-balancing, connection management GRPC_CLIENT_CHANNEL, @@ -42,4 +46,8 @@ bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type); const char *grpc_channel_stack_type_string(grpc_channel_stack_type type); -#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SURFACE_CHANNEL_STACK_TYPE_H */ \ No newline at end of file diff --git a/src/core/lib/surface/completion_queue_factory.h b/src/core/lib/surface/completion_queue_factory.h index 89be8f8216..cb0af6f0fb 100644 --- a/src/core/lib/surface/completion_queue_factory.h +++ b/src/core/lib/surface/completion_queue_factory.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/surface/completion_queue.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_completion_queue_factory_vtable { grpc_completion_queue* (*create)(const grpc_completion_queue_factory*, const grpc_completion_queue_attributes*); @@ -33,4 +37,8 @@ struct grpc_completion_queue_factory { grpc_completion_queue_factory_vtable* vtable; }; -#endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SURFACE_COMPLETION_QUEUE_FACTORY_H */ \ No newline at end of file diff --git a/src/core/lib/surface/event_string.h b/src/core/lib/surface/event_string.h index f00efca7f3..127609c404 100644 --- a/src/core/lib/surface/event_string.h +++ b/src/core/lib/surface/event_string.h @@ -21,7 +21,15 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + /* Returns a string describing an event. Must be later freed with gpr_free() */ char *grpc_event_string(grpc_event *ev); -#endif /* GRPC_CORE_LIB_SURFACE_EVENT_STRING_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SURFACE_EVENT_STRING_H */ \ No newline at end of file diff --git a/src/core/lib/surface/init.h b/src/core/lib/surface/init.h index 9353208332..b2f48576e5 100644 --- a/src/core/lib/surface/init.h +++ b/src/core/lib/surface/init.h @@ -19,9 +19,17 @@ #ifndef GRPC_CORE_LIB_SURFACE_INIT_H #define GRPC_CORE_LIB_SURFACE_INIT_H +#ifdef __cplusplus +extern "C" { +#endif + void grpc_register_security_filters(void); void grpc_security_pre_init(void); void grpc_security_init(void); int grpc_is_initialized(void); -#endif /* GRPC_CORE_LIB_SURFACE_INIT_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SURFACE_INIT_H */ \ No newline at end of file diff --git a/src/core/lib/surface/server.h b/src/core/lib/surface/server.h index dd5639d97a..1114715833 100644 --- a/src/core/lib/surface/server.h +++ b/src/core/lib/surface/server.h @@ -24,6 +24,10 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + extern const grpc_channel_filter grpc_server_top_filter; /** Lightweight tracing of server channel state */ @@ -54,4 +58,8 @@ int grpc_server_has_open_connections(grpc_server *server); void grpc_server_get_pollsets(grpc_server *server, grpc_pollset ***pollsets, size_t *pollset_count); -#endif /* GRPC_CORE_LIB_SURFACE_SERVER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SURFACE_SERVER_H */ \ No newline at end of file diff --git a/src/core/lib/surface/validate_metadata.h b/src/core/lib/surface/validate_metadata.h index de869d89b4..aa02419d9f 100644 --- a/src/core/lib/surface/validate_metadata.h +++ b/src/core/lib/surface/validate_metadata.h @@ -22,7 +22,15 @@ #include #include "src/core/lib/iomgr/error.h" +#ifdef __cplusplus +extern "C" { +#endif + grpc_error *grpc_validate_header_key_is_legal(grpc_slice slice); grpc_error *grpc_validate_header_nonbin_value_is_legal(grpc_slice slice); -#endif /* GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_SURFACE_VALIDATE_METADATA_H */ \ No newline at end of file diff --git a/src/core/lib/transport/bdp_estimator.h b/src/core/lib/transport/bdp_estimator.h index 1ef0dc99dd..21c27ec6af 100644 --- a/src/core/lib/transport/bdp_estimator.h +++ b/src/core/lib/transport/bdp_estimator.h @@ -27,6 +27,10 @@ #define GRPC_BDP_SAMPLES 16 #define GRPC_BDP_MIN_SAMPLES_FOR_ESTIMATE 3 +#ifdef __cplusplus +extern "C" { +#endif + extern grpc_tracer_flag grpc_bdp_estimator_trace; typedef enum { @@ -66,4 +70,8 @@ void grpc_bdp_estimator_start_ping(grpc_bdp_estimator *estimator); // Completes a previously started ping void grpc_bdp_estimator_complete_ping(grpc_bdp_estimator *estimator); -#endif /* GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_BDP_ESTIMATOR_H */ \ No newline at end of file diff --git a/src/core/lib/transport/byte_stream.h b/src/core/lib/transport/byte_stream.h index be2a35213e..d3e04df5c0 100644 --- a/src/core/lib/transport/byte_stream.h +++ b/src/core/lib/transport/byte_stream.h @@ -28,6 +28,10 @@ /** Mask of all valid internal flags. */ #define GRPC_WRITE_INTERNAL_USED_MASK (GRPC_WRITE_INTERNAL_COMPRESS) +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_byte_stream grpc_byte_stream; typedef struct { @@ -135,4 +139,8 @@ void grpc_caching_byte_stream_init(grpc_caching_byte_stream *stream, // Resets the byte stream to the start of the underlying stream. void grpc_caching_byte_stream_reset(grpc_caching_byte_stream *stream); -#endif /* GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_BYTE_STREAM_H */ \ No newline at end of file diff --git a/src/core/lib/transport/connectivity_state.h b/src/core/lib/transport/connectivity_state.h index 2fece6cc21..1796a540a7 100644 --- a/src/core/lib/transport/connectivity_state.h +++ b/src/core/lib/transport/connectivity_state.h @@ -23,6 +23,10 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/exec_ctx.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_connectivity_state_watcher { /** we keep watchers in a linked list */ struct grpc_connectivity_state_watcher *next; @@ -84,4 +88,8 @@ bool grpc_connectivity_state_notify_on_state_change( grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, grpc_connectivity_state *current, grpc_closure *notify); -#endif /* GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_CONNECTIVITY_STATE_H */ \ No newline at end of file diff --git a/src/core/lib/transport/error_utils.h b/src/core/lib/transport/error_utils.h index e530884215..18ff54839c 100644 --- a/src/core/lib/transport/error_utils.h +++ b/src/core/lib/transport/error_utils.h @@ -22,6 +22,10 @@ #include "src/core/lib/iomgr/error.h" #include "src/core/lib/transport/http2_errors.h" +#ifdef __cplusplus +extern "C" { +#endif + /// A utility function to get the status code and message to be returned /// to the application. If not set in the top-level message, looks /// through child errors until it finds the first one with these attributes. @@ -38,4 +42,8 @@ void grpc_error_get_status(grpc_error *error, gpr_timespec deadline, /// GRPC_ERROR_CANCELLED bool grpc_error_has_clear_grpc_status(grpc_error *error); -#endif /* GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_ERROR_UTILS_H */ \ No newline at end of file diff --git a/src/core/lib/transport/pid_controller.h b/src/core/lib/transport/pid_controller.h index 9352b2643f..17feabfd39 100644 --- a/src/core/lib/transport/pid_controller.h +++ b/src/core/lib/transport/pid_controller.h @@ -19,6 +19,10 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H #define GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H +#ifdef __cplusplus +extern "C" { +#endif + /* \file Simple PID controller. Implements a proportional-integral-derivative controller. Used when we want to iteratively control a variable to converge some other @@ -59,4 +63,8 @@ double grpc_pid_controller_update(grpc_pid_controller *pid_controller, /** Returns the last control value calculated */ double grpc_pid_controller_last(grpc_pid_controller *pid_controller); -#endif /* GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_PID_CONTROLLER_H */ \ No newline at end of file diff --git a/src/core/lib/transport/service_config.h b/src/core/lib/transport/service_config.h index 84110abc36..c485f52472 100644 --- a/src/core/lib/transport/service_config.h +++ b/src/core/lib/transport/service_config.h @@ -22,6 +22,10 @@ #include "src/core/lib/json/json.h" #include "src/core/lib/slice/slice_hash_table.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_service_config grpc_service_config; grpc_service_config* grpc_service_config_create(const char* json_string); @@ -59,4 +63,8 @@ void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx, const grpc_slice_hash_table* table, grpc_slice path); -#endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */ \ No newline at end of file diff --git a/src/core/lib/transport/status_conversion.h b/src/core/lib/transport/status_conversion.h index e93f3dfd9b..b257998e4d 100644 --- a/src/core/lib/transport/status_conversion.h +++ b/src/core/lib/transport/status_conversion.h @@ -22,6 +22,10 @@ #include #include "src/core/lib/transport/http2_errors.h" +#ifdef __cplusplus +extern "C" { +#endif + /* Conversion of grpc status codes to http2 error codes (for RST_STREAM) */ grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status); grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error, @@ -31,4 +35,8 @@ grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error, grpc_status_code grpc_http2_status_to_grpc_status(int status); int grpc_status_to_http2_status(grpc_status_code status); -#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_STATUS_CONVERSION_H */ \ No newline at end of file diff --git a/src/core/lib/transport/timeout_encoding.h b/src/core/lib/transport/timeout_encoding.h index 7ff35c4083..1f4e206f8a 100644 --- a/src/core/lib/transport/timeout_encoding.h +++ b/src/core/lib/transport/timeout_encoding.h @@ -26,9 +26,17 @@ #define GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE (GPR_LTOA_MIN_BUFSIZE + 1) +#ifdef __cplusplus +extern "C" { +#endif + /* Encode/decode timeouts to the GRPC over HTTP/2 format; encoding may round up arbitrarily */ void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer); int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout); -#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_TIMEOUT_ENCODING_H */ \ No newline at end of file diff --git a/src/core/lib/transport/transport_impl.h b/src/core/lib/transport/transport_impl.h index bbae69c223..41d34d3954 100644 --- a/src/core/lib/transport/transport_impl.h +++ b/src/core/lib/transport/transport_impl.h @@ -21,6 +21,10 @@ #include "src/core/lib/transport/transport.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct grpc_transport_vtable { /* Memory required for a single stream element - this is allocated by upper layers and initialized by the transport */ @@ -69,4 +73,8 @@ struct grpc_transport { const grpc_transport_vtable *vtable; }; -#endif /* GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_LIB_TRANSPORT_TRANSPORT_IMPL_H */ \ No newline at end of file diff --git a/src/core/tsi/gts_transport_security.h b/src/core/tsi/gts_transport_security.h index 538e1030bc..b988c3f861 100644 --- a/src/core/tsi/gts_transport_security.h +++ b/src/core/tsi/gts_transport_security.h @@ -23,6 +23,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct gts_shared_resource { gpr_thd_id thread_id; grpc_channel *channel; @@ -34,4 +38,8 @@ typedef struct gts_shared_resource { * TSI handshakes. */ gts_shared_resource *gts_get_shared_resource(void); -#endif /* GRPC_CORE_TSI_GTS_TRANSPORT_SECURITY_H */ +#ifdef __cplusplus +} +#endif + +#endif /* GRPC_CORE_TSI_GTS_TRANSPORT_SECURITY_H */ \ No newline at end of file -- cgit v1.2.3 From 1c9b584a153ff592c92b6cf6baa620d8602a37cd Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Thu, 21 Sep 2017 15:49:55 -0700 Subject: Changes for C to C++. Adding extern C to header files for compatibility. --- CMakeLists.txt | 6 +- Makefile | 10 +-- binding.gyp | 2 +- config.m4 | 2 +- config.w32 | 2 +- gRPC-Core.podspec | 2 +- grpc.gemspec | 2 +- grpc.gyp | 4 +- package.xml | 2 +- .../client_channel/resolver/fake/fake_resolver.h | 6 +- src/core/ext/filters/http/http_filters_plugin.c | 4 +- src/core/lib/debug/stats_data.h | 8 -- src/core/lib/debug/trace.c | 1 + src/core/lib/iomgr/gethostname.h | 10 +-- src/core/lib/iomgr/network_status_tracker.c | 2 +- src/core/lib/iomgr/wakeup_fd_cv.h | 2 +- src/core/lib/iomgr/wakeup_fd_pipe.c | 2 +- src/core/lib/iomgr/wakeup_fd_pipe.h | 2 +- src/core/lib/security/credentials/jwt/json_token.c | 1 + src/core/lib/support/log.c | 2 +- src/core/lib/support/log_linux.c | 2 +- src/core/lib/support/log_posix.c | 2 +- src/core/lib/support/log_windows.c | 2 +- src/core/lib/surface/call.c | 1 + src/core/lib/surface/version.c | 26 ------ src/core/lib/surface/version.cc | 26 ++++++ .../plugin_registry/grpc_cronet_plugin_registry.c | 47 ----------- .../plugin_registry/grpc_cronet_plugin_registry.cc | 47 +++++++++++ src/core/plugin_registry/grpc_plugin_registry.c | 95 ---------------------- src/core/plugin_registry/grpc_plugin_registry.cc | 95 ++++++++++++++++++++++ .../grpc_unsecure_plugin_registry.c | 91 --------------------- .../grpc_unsecure_plugin_registry.cc | 91 +++++++++++++++++++++ src/python/grpcio/grpc_core_dependencies.py | 2 +- templates/src/core/lib/surface/version.c.template | 28 ------- templates/src/core/lib/surface/version.cc.template | 28 +++++++ templates/src/core/plugin_registry.template | 6 +- tools/buildgen/plugins/expand_filegroups.py | 2 +- tools/doxygen/Doxyfile.core.internal | 2 +- 38 files changed, 326 insertions(+), 339 deletions(-) delete mode 100644 src/core/lib/surface/version.c create mode 100644 src/core/lib/surface/version.cc delete mode 100644 src/core/plugin_registry/grpc_cronet_plugin_registry.c create mode 100644 src/core/plugin_registry/grpc_cronet_plugin_registry.cc delete mode 100644 src/core/plugin_registry/grpc_plugin_registry.c create mode 100644 src/core/plugin_registry/grpc_plugin_registry.cc delete mode 100644 src/core/plugin_registry/grpc_unsecure_plugin_registry.c create mode 100644 src/core/plugin_registry/grpc_unsecure_plugin_registry.cc delete mode 100644 templates/src/core/lib/surface/version.c.template create mode 100644 templates/src/core/lib/surface/version.cc.template (limited to 'src/core/lib/support') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e448c92b6..fe838383f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1214,7 +1214,7 @@ add_library(grpc src/core/ext/filters/message_size/message_size_filter.c src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c src/core/ext/filters/workarounds/workaround_utils.c - src/core/plugin_registry/grpc_plugin_registry.c + src/core/plugin_registry/grpc_plugin_registry.cc ) if(WIN32 AND MSVC) @@ -1523,7 +1523,7 @@ add_library(grpc_cronet src/core/ext/transport/chttp2/client/chttp2_connector.c src/core/ext/filters/load_reporting/server_load_reporting_filter.c src/core/ext/filters/load_reporting/server_load_reporting_plugin.c - src/core/plugin_registry/grpc_cronet_plugin_registry.c + src/core/plugin_registry/grpc_cronet_plugin_registry.cc ) if(WIN32 AND MSVC) @@ -2364,7 +2364,7 @@ add_library(grpc_unsecure src/core/ext/filters/message_size/message_size_filter.c src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c src/core/ext/filters/workarounds/workaround_utils.c - src/core/plugin_registry/grpc_unsecure_plugin_registry.c + src/core/plugin_registry/grpc_unsecure_plugin_registry.cc ) if(WIN32 AND MSVC) diff --git a/Makefile b/Makefile index 517ddfd90e..7f657ef289 100644 --- a/Makefile +++ b/Makefile @@ -3205,7 +3205,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/message_size/message_size_filter.c \ src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ src/core/ext/filters/workarounds/workaround_utils.c \ - src/core/plugin_registry/grpc_plugin_registry.c \ + src/core/plugin_registry/grpc_plugin_registry.cc \ PUBLIC_HEADERS_C += \ include/grpc/impl/codegen/byte_buffer.h \ @@ -3514,7 +3514,7 @@ LIBGRPC_CRONET_SRC = \ src/core/ext/transport/chttp2/client/chttp2_connector.c \ src/core/ext/filters/load_reporting/server_load_reporting_filter.c \ src/core/ext/filters/load_reporting/server_load_reporting_plugin.c \ - src/core/plugin_registry/grpc_cronet_plugin_registry.c \ + src/core/plugin_registry/grpc_cronet_plugin_registry.cc \ PUBLIC_HEADERS_C += \ include/grpc/impl/codegen/byte_buffer.h \ @@ -4323,7 +4323,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/message_size/message_size_filter.c \ src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ src/core/ext/filters/workarounds/workaround_utils.c \ - src/core/plugin_registry/grpc_unsecure_plugin_registry.c \ + src/core/plugin_registry/grpc_unsecure_plugin_registry.cc \ PUBLIC_HEADERS_C += \ include/grpc/impl/codegen/byte_buffer.h \ @@ -20188,8 +20188,8 @@ src/core/lib/security/transport/server_auth_filter.c: $(OPENSSL_DEP) src/core/lib/security/transport/tsi_error.c: $(OPENSSL_DEP) src/core/lib/security/util/json_util.c: $(OPENSSL_DEP) src/core/lib/surface/init_secure.c: $(OPENSSL_DEP) -src/core/plugin_registry/grpc_cronet_plugin_registry.c: $(OPENSSL_DEP) -src/core/plugin_registry/grpc_plugin_registry.c: $(OPENSSL_DEP) +src/core/plugin_registry/grpc_cronet_plugin_registry.cc: $(OPENSSL_DEP) +src/core/plugin_registry/grpc_plugin_registry.cc: $(OPENSSL_DEP) src/core/tsi/fake_transport_security.c: $(OPENSSL_DEP) src/core/tsi/gts_transport_security.c: $(OPENSSL_DEP) src/core/tsi/ssl_transport_security.c: $(OPENSSL_DEP) diff --git a/binding.gyp b/binding.gyp index b4f9038210..476e6fd3c4 100644 --- a/binding.gyp +++ b/binding.gyp @@ -916,7 +916,7 @@ 'src/core/ext/filters/message_size/message_size_filter.c', 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', 'src/core/ext/filters/workarounds/workaround_utils.c', - 'src/core/plugin_registry/grpc_plugin_registry.c', + 'src/core/plugin_registry/grpc_plugin_registry.cc', ], 'conditions': [ ['OS == "mac"', { diff --git a/config.m4 b/config.m4 index 72f8a1e710..edd4c64f26 100644 --- a/config.m4 +++ b/config.m4 @@ -345,7 +345,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/message_size/message_size_filter.c \ src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ src/core/ext/filters/workarounds/workaround_utils.c \ - src/core/plugin_registry/grpc_plugin_registry.c \ + src/core/plugin_registry/grpc_plugin_registry.cc \ src/boringssl/err_data.c \ third_party/boringssl/crypto/aes/aes.c \ third_party/boringssl/crypto/aes/key_wrap.c \ diff --git a/config.w32 b/config.w32 index 5347baedc2..7247cfafdb 100644 --- a/config.w32 +++ b/config.w32 @@ -322,7 +322,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\message_size\\message_size_filter.c " + "src\\core\\ext\\filters\\workarounds\\workaround_cronet_compression_filter.c " + "src\\core\\ext\\filters\\workarounds\\workaround_utils.c " + - "src\\core\\plugin_registry\\grpc_plugin_registry.c " + + "src\\core\\plugin_registry\\grpc_plugin_registry.cc " + "src\\boringssl\\err_data.c " + "third_party\\boringssl\\crypto\\aes\\aes.c " + "third_party\\boringssl\\crypto\\aes\\key_wrap.c " + diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index c76b9a3f4b..487d35606e 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -727,7 +727,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/message_size/message_size_filter.c', 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', 'src/core/ext/filters/workarounds/workaround_utils.c', - 'src/core/plugin_registry/grpc_plugin_registry.c' + 'src/core/plugin_registry/grpc_plugin_registry.cc' ss.private_header_files = 'src/core/lib/profiling/timers.h', 'src/core/lib/support/arena.h', diff --git a/grpc.gemspec b/grpc.gemspec index acf71cab78..93c0cb54fd 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -667,7 +667,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/message_size/message_size_filter.c ) s.files += %w( src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c ) s.files += %w( src/core/ext/filters/workarounds/workaround_utils.c ) - s.files += %w( src/core/plugin_registry/grpc_plugin_registry.c ) + s.files += %w( src/core/plugin_registry/grpc_plugin_registry.cc ) s.files += %w( third_party/boringssl/crypto/aes/internal.h ) s.files += %w( third_party/boringssl/crypto/asn1/asn1_locl.h ) s.files += %w( third_party/boringssl/crypto/bio/internal.h ) diff --git a/grpc.gyp b/grpc.gyp index 6331b76f47..9055dfe901 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -482,7 +482,7 @@ 'src/core/ext/filters/message_size/message_size_filter.c', 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', 'src/core/ext/filters/workarounds/workaround_utils.c', - 'src/core/plugin_registry/grpc_plugin_registry.c', + 'src/core/plugin_registry/grpc_plugin_registry.cc', ], }, { @@ -1148,7 +1148,7 @@ 'src/core/ext/filters/message_size/message_size_filter.c', 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', 'src/core/ext/filters/workarounds/workaround_utils.c', - 'src/core/plugin_registry/grpc_unsecure_plugin_registry.c', + 'src/core/plugin_registry/grpc_unsecure_plugin_registry.cc', ], }, { diff --git a/package.xml b/package.xml index 0eea122b24..39d238e378 100644 --- a/package.xml +++ b/package.xml @@ -679,7 +679,7 @@ - + diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h index 005f5f9417..95c3bafed8 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h @@ -21,13 +21,13 @@ #include "src/core/ext/filters/client_channel/uri_parser.h" #include "src/core/lib/channel/channel_args.h" -#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \ - "grpc.fake_resolver.response_generator" - #ifdef __cplusplus extern "C" { #endif +#define GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR \ + "grpc.fake_resolver.response_generator" + void grpc_resolver_fake_init(); // Instances of \a grpc_fake_resolver_response_generator are passed to the diff --git a/src/core/ext/filters/http/http_filters_plugin.c b/src/core/ext/filters/http/http_filters_plugin.c index 88bd2250f9..8f5b856317 100644 --- a/src/core/ext/filters/http/http_filters_plugin.c +++ b/src/core/ext/filters/http/http_filters_plugin.c @@ -64,7 +64,7 @@ static bool maybe_add_required_filter(grpc_exec_ctx *exec_ctx, : true; } -void grpc_http_filters_init(void) { +extern "C" void grpc_http_filters_init(void) { grpc_register_tracer(&grpc_compression_trace); grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, @@ -86,4 +86,4 @@ void grpc_http_filters_init(void) { maybe_add_required_filter, (void *)&grpc_http_server_filter); } -void grpc_http_filters_shutdown(void) {} +extern "C" void grpc_http_filters_shutdown(void) {} diff --git a/src/core/lib/debug/stats_data.h b/src/core/lib/debug/stats_data.h index cf5bafbd04..28dab00117 100644 --- a/src/core/lib/debug/stats_data.h +++ b/src/core/lib/debug/stats_data.h @@ -24,10 +24,6 @@ #include #include "src/core/lib/iomgr/exec_ctx.h" -#ifdef __cplusplus -extern "C" { -#endif - typedef enum { GRPC_STATS_COUNTER_CLIENT_CALLS_CREATED, GRPC_STATS_COUNTER_SERVER_CALLS_CREATED, @@ -471,8 +467,4 @@ extern const int *const grpc_stats_histo_bucket_boundaries[13]; extern void (*const grpc_stats_inc_histogram[13])(grpc_exec_ctx *exec_ctx, int x); -#ifdef __cplusplus -} -#endif - #endif /* GRPC_CORE_LIB_DEBUG_STATS_DATA_H */ diff --git a/src/core/lib/debug/trace.c b/src/core/lib/debug/trace.c index 7cb2789a19..21b0d8c3a6 100644 --- a/src/core/lib/debug/trace.c +++ b/src/core/lib/debug/trace.c @@ -20,6 +20,7 @@ #include +#include #include #include #include "src/core/lib/support/env.h" diff --git a/src/core/lib/iomgr/gethostname.h b/src/core/lib/iomgr/gethostname.h index 4dcf50c591..9c6b9d8d42 100644 --- a/src/core/lib/iomgr/gethostname.h +++ b/src/core/lib/iomgr/gethostname.h @@ -19,16 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H #define GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H -#ifdef __cplusplus -extern "C" { -#endif - // Returns the hostname of the local machine. // Caller takes ownership of result. char *grpc_gethostname(); -#ifdef __cplusplus -} -#endif - -#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */ \ No newline at end of file +#endif /* GRPC_CORE_LIB_IOMGR_GETHOSTNAME_H */ diff --git a/src/core/lib/iomgr/network_status_tracker.c b/src/core/lib/iomgr/network_status_tracker.c index fe5659160c..57a7faa9f1 100644 --- a/src/core/lib/iomgr/network_status_tracker.c +++ b/src/core/lib/iomgr/network_status_tracker.c @@ -16,8 +16,8 @@ * */ -#include "src/core/lib/iomgr/endpoint.h" #include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/endpoint.h" void grpc_network_status_shutdown(void) {} diff --git a/src/core/lib/iomgr/wakeup_fd_cv.h b/src/core/lib/iomgr/wakeup_fd_cv.h index 8f57de8152..dcd7bdb560 100644 --- a/src/core/lib/iomgr/wakeup_fd_cv.h +++ b/src/core/lib/iomgr/wakeup_fd_cv.h @@ -66,7 +66,7 @@ typedef struct cv_fd_table { grpc_poll_function_type poll; } cv_fd_table; -extern grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable; +extern const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable; #ifdef __cplusplus } diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.c b/src/core/lib/iomgr/wakeup_fd_pipe.c index c9c390b587..05d69dc9cc 100644 --- a/src/core/lib/iomgr/wakeup_fd_pipe.c +++ b/src/core/lib/iomgr/wakeup_fd_pipe.c @@ -20,8 +20,8 @@ #ifdef GRPC_POSIX_WAKEUP_FD -#include "src/core/lib/iomgr/wakeup_fd_posix.h" #include "src/core/lib/iomgr/wakeup_fd_pipe.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" #include #include diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.h b/src/core/lib/iomgr/wakeup_fd_pipe.h index c4c95ab358..9bbb5e2ff7 100644 --- a/src/core/lib/iomgr/wakeup_fd_pipe.h +++ b/src/core/lib/iomgr/wakeup_fd_pipe.h @@ -25,7 +25,7 @@ extern "C" { #endif -extern grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable; +extern const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable; #ifdef __cplusplus } diff --git a/src/core/lib/security/credentials/jwt/json_token.c b/src/core/lib/security/credentials/jwt/json_token.c index e41667c22f..1f5cc7059d 100644 --- a/src/core/lib/security/credentials/jwt/json_token.c +++ b/src/core/lib/security/credentials/jwt/json_token.c @@ -20,6 +20,7 @@ #include +#include #include #include #include diff --git a/src/core/lib/support/log.c b/src/core/lib/support/log.c index fadb4d9a2c..69f92e001c 100644 --- a/src/core/lib/support/log.c +++ b/src/core/lib/support/log.c @@ -27,7 +27,7 @@ #include #include -extern void gpr_default_log(gpr_log_func_args *args); +extern "C" void gpr_default_log(gpr_log_func_args *args); static gpr_atm g_log_func = (gpr_atm)gpr_default_log; static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET; diff --git a/src/core/lib/support/log_linux.c b/src/core/lib/support/log_linux.c index 7755018693..0914acedf4 100644 --- a/src/core/lib/support/log_linux.c +++ b/src/core/lib/support/log_linux.c @@ -56,7 +56,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, free(message); } -void gpr_default_log(gpr_log_func_args *args) { +extern "C" void gpr_default_log(gpr_log_func_args *args) { const char *final_slash; char *prefix; const char *display_file; diff --git a/src/core/lib/support/log_posix.c b/src/core/lib/support/log_posix.c index 8b376fce41..855e8e7107 100644 --- a/src/core/lib/support/log_posix.c +++ b/src/core/lib/support/log_posix.c @@ -57,7 +57,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, gpr_free(allocated); } -void gpr_default_log(gpr_log_func_args *args) { +extern "C" void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; diff --git a/src/core/lib/support/log_windows.c b/src/core/lib/support/log_windows.c index 0fdab79ae6..b71dacd80a 100644 --- a/src/core/lib/support/log_windows.c +++ b/src/core/lib/support/log_windows.c @@ -65,7 +65,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, } /* Simple starter implementation */ -void gpr_default_log(gpr_log_func_args *args) { +extern "C" void gpr_default_log(gpr_log_func_args *args) { char *final_slash; const char *display_file; char time_buffer[64]; diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c index 03f47553a1..74e55d5741 100644 --- a/src/core/lib/surface/call.c +++ b/src/core/lib/surface/call.c @@ -41,6 +41,7 @@ #include "src/core/lib/support/string.h" #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/call_test_only.h" #include "src/core/lib/surface/channel.h" #include "src/core/lib/surface/completion_queue.h" #include "src/core/lib/surface/validate_metadata.h" diff --git a/src/core/lib/surface/version.c b/src/core/lib/surface/version.c deleted file mode 100644 index fd6ea4daa9..0000000000 --- a/src/core/lib/surface/version.c +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* This file is autogenerated from: - templates/src/core/surface/version.c.template */ - -#include - -const char *grpc_version_string(void) { return "5.0.0-dev"; } - -const char *grpc_g_stands_for(void) { return "gambit"; } diff --git a/src/core/lib/surface/version.cc b/src/core/lib/surface/version.cc new file mode 100644 index 0000000000..fd6ea4daa9 --- /dev/null +++ b/src/core/lib/surface/version.cc @@ -0,0 +1,26 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This file is autogenerated from: + templates/src/core/surface/version.c.template */ + +#include + +const char *grpc_version_string(void) { return "5.0.0-dev"; } + +const char *grpc_g_stands_for(void) { return "gambit"; } diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.c b/src/core/plugin_registry/grpc_cronet_plugin_registry.c deleted file mode 100644 index 1c09f54ad9..0000000000 --- a/src/core/plugin_registry/grpc_cronet_plugin_registry.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -extern void grpc_http_filters_init(void); -extern void grpc_http_filters_shutdown(void); -extern void grpc_chttp2_plugin_init(void); -extern void grpc_chttp2_plugin_shutdown(void); -extern void grpc_deadline_filter_init(void); -extern void grpc_deadline_filter_shutdown(void); -extern void grpc_client_channel_init(void); -extern void grpc_client_channel_shutdown(void); -extern void grpc_tsi_gts_init(void); -extern void grpc_tsi_gts_shutdown(void); -extern void grpc_server_load_reporting_plugin_init(void); -extern void grpc_server_load_reporting_plugin_shutdown(void); - -void grpc_register_built_in_plugins(void) { - grpc_register_plugin(grpc_http_filters_init, - grpc_http_filters_shutdown); - grpc_register_plugin(grpc_chttp2_plugin_init, - grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_deadline_filter_init, - grpc_deadline_filter_shutdown); - grpc_register_plugin(grpc_client_channel_init, - grpc_client_channel_shutdown); - grpc_register_plugin(grpc_tsi_gts_init, - grpc_tsi_gts_shutdown); - grpc_register_plugin(grpc_server_load_reporting_plugin_init, - grpc_server_load_reporting_plugin_shutdown); -} diff --git a/src/core/plugin_registry/grpc_cronet_plugin_registry.cc b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc new file mode 100644 index 0000000000..e0422f6750 --- /dev/null +++ b/src/core/plugin_registry/grpc_cronet_plugin_registry.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +extern "C" void grpc_http_filters_init(void); +extern "C" void grpc_http_filters_shutdown(void); +extern "C" void grpc_chttp2_plugin_init(void); +extern "C" void grpc_chttp2_plugin_shutdown(void); +extern "C" void grpc_deadline_filter_init(void); +extern "C" void grpc_deadline_filter_shutdown(void); +extern "C" void grpc_client_channel_init(void); +extern "C" void grpc_client_channel_shutdown(void); +extern "C" void grpc_tsi_gts_init(void); +extern "C" void grpc_tsi_gts_shutdown(void); +extern "C" void grpc_server_load_reporting_plugin_init(void); +extern "C" void grpc_server_load_reporting_plugin_shutdown(void); + +void grpc_register_built_in_plugins(void) { + grpc_register_plugin(grpc_http_filters_init, + grpc_http_filters_shutdown); + grpc_register_plugin(grpc_chttp2_plugin_init, + grpc_chttp2_plugin_shutdown); + grpc_register_plugin(grpc_deadline_filter_init, + grpc_deadline_filter_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); + grpc_register_plugin(grpc_tsi_gts_init, + grpc_tsi_gts_shutdown); + grpc_register_plugin(grpc_server_load_reporting_plugin_init, + grpc_server_load_reporting_plugin_shutdown); +} diff --git a/src/core/plugin_registry/grpc_plugin_registry.c b/src/core/plugin_registry/grpc_plugin_registry.c deleted file mode 100644 index 9cacf3d306..0000000000 --- a/src/core/plugin_registry/grpc_plugin_registry.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -extern void grpc_http_filters_init(void); -extern void grpc_http_filters_shutdown(void); -extern void grpc_chttp2_plugin_init(void); -extern void grpc_chttp2_plugin_shutdown(void); -extern void grpc_tsi_gts_init(void); -extern void grpc_tsi_gts_shutdown(void); -extern void grpc_deadline_filter_init(void); -extern void grpc_deadline_filter_shutdown(void); -extern void grpc_client_channel_init(void); -extern void grpc_client_channel_shutdown(void); -extern void grpc_inproc_plugin_init(void); -extern void grpc_inproc_plugin_shutdown(void); -extern void grpc_resolver_fake_init(void); -extern void grpc_resolver_fake_shutdown(void); -extern void grpc_lb_policy_grpclb_init(void); -extern void grpc_lb_policy_grpclb_shutdown(void); -extern void grpc_lb_policy_pick_first_init(void); -extern void grpc_lb_policy_pick_first_shutdown(void); -extern void grpc_lb_policy_round_robin_init(void); -extern void grpc_lb_policy_round_robin_shutdown(void); -extern void grpc_resolver_dns_ares_init(void); -extern void grpc_resolver_dns_ares_shutdown(void); -extern void grpc_resolver_dns_native_init(void); -extern void grpc_resolver_dns_native_shutdown(void); -extern void grpc_resolver_sockaddr_init(void); -extern void grpc_resolver_sockaddr_shutdown(void); -extern void grpc_server_load_reporting_plugin_init(void); -extern void grpc_server_load_reporting_plugin_shutdown(void); -extern void census_grpc_plugin_init(void); -extern void census_grpc_plugin_shutdown(void); -extern void grpc_max_age_filter_init(void); -extern void grpc_max_age_filter_shutdown(void); -extern void grpc_message_size_filter_init(void); -extern void grpc_message_size_filter_shutdown(void); -extern void grpc_workaround_cronet_compression_filter_init(void); -extern void grpc_workaround_cronet_compression_filter_shutdown(void); - -void grpc_register_built_in_plugins(void) { - grpc_register_plugin(grpc_http_filters_init, - grpc_http_filters_shutdown); - grpc_register_plugin(grpc_chttp2_plugin_init, - grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_tsi_gts_init, - grpc_tsi_gts_shutdown); - grpc_register_plugin(grpc_deadline_filter_init, - grpc_deadline_filter_shutdown); - grpc_register_plugin(grpc_client_channel_init, - grpc_client_channel_shutdown); - grpc_register_plugin(grpc_inproc_plugin_init, - grpc_inproc_plugin_shutdown); - grpc_register_plugin(grpc_resolver_fake_init, - grpc_resolver_fake_shutdown); - grpc_register_plugin(grpc_lb_policy_grpclb_init, - grpc_lb_policy_grpclb_shutdown); - grpc_register_plugin(grpc_lb_policy_pick_first_init, - grpc_lb_policy_pick_first_shutdown); - grpc_register_plugin(grpc_lb_policy_round_robin_init, - grpc_lb_policy_round_robin_shutdown); - grpc_register_plugin(grpc_resolver_dns_ares_init, - grpc_resolver_dns_ares_shutdown); - grpc_register_plugin(grpc_resolver_dns_native_init, - grpc_resolver_dns_native_shutdown); - grpc_register_plugin(grpc_resolver_sockaddr_init, - grpc_resolver_sockaddr_shutdown); - grpc_register_plugin(grpc_server_load_reporting_plugin_init, - grpc_server_load_reporting_plugin_shutdown); - grpc_register_plugin(census_grpc_plugin_init, - census_grpc_plugin_shutdown); - grpc_register_plugin(grpc_max_age_filter_init, - grpc_max_age_filter_shutdown); - grpc_register_plugin(grpc_message_size_filter_init, - grpc_message_size_filter_shutdown); - grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, - grpc_workaround_cronet_compression_filter_shutdown); -} diff --git a/src/core/plugin_registry/grpc_plugin_registry.cc b/src/core/plugin_registry/grpc_plugin_registry.cc new file mode 100644 index 0000000000..2d332e2b91 --- /dev/null +++ b/src/core/plugin_registry/grpc_plugin_registry.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +extern "C" void grpc_http_filters_init(void); +extern "C" void grpc_http_filters_shutdown(void); +extern "C" void grpc_chttp2_plugin_init(void); +extern "C" void grpc_chttp2_plugin_shutdown(void); +extern "C" void grpc_tsi_gts_init(void); +extern "C" void grpc_tsi_gts_shutdown(void); +extern "C" void grpc_deadline_filter_init(void); +extern "C" void grpc_deadline_filter_shutdown(void); +extern "C" void grpc_client_channel_init(void); +extern "C" void grpc_client_channel_shutdown(void); +extern "C" void grpc_inproc_plugin_init(void); +extern "C" void grpc_inproc_plugin_shutdown(void); +extern "C" void grpc_resolver_fake_init(void); +extern "C" void grpc_resolver_fake_shutdown(void); +extern "C" void grpc_lb_policy_grpclb_init(void); +extern "C" void grpc_lb_policy_grpclb_shutdown(void); +extern "C" void grpc_lb_policy_pick_first_init(void); +extern "C" void grpc_lb_policy_pick_first_shutdown(void); +extern "C" void grpc_lb_policy_round_robin_init(void); +extern "C" void grpc_lb_policy_round_robin_shutdown(void); +extern "C" void grpc_resolver_dns_ares_init(void); +extern "C" void grpc_resolver_dns_ares_shutdown(void); +extern "C" void grpc_resolver_dns_native_init(void); +extern "C" void grpc_resolver_dns_native_shutdown(void); +extern "C" void grpc_resolver_sockaddr_init(void); +extern "C" void grpc_resolver_sockaddr_shutdown(void); +extern "C" void grpc_server_load_reporting_plugin_init(void); +extern "C" void grpc_server_load_reporting_plugin_shutdown(void); +extern "C" void census_grpc_plugin_init(void); +extern "C" void census_grpc_plugin_shutdown(void); +extern "C" void grpc_max_age_filter_init(void); +extern "C" void grpc_max_age_filter_shutdown(void); +extern "C" void grpc_message_size_filter_init(void); +extern "C" void grpc_message_size_filter_shutdown(void); +extern "C" void grpc_workaround_cronet_compression_filter_init(void); +extern "C" void grpc_workaround_cronet_compression_filter_shutdown(void); + +void grpc_register_built_in_plugins(void) { + grpc_register_plugin(grpc_http_filters_init, + grpc_http_filters_shutdown); + grpc_register_plugin(grpc_chttp2_plugin_init, + grpc_chttp2_plugin_shutdown); + grpc_register_plugin(grpc_tsi_gts_init, + grpc_tsi_gts_shutdown); + grpc_register_plugin(grpc_deadline_filter_init, + grpc_deadline_filter_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); + grpc_register_plugin(grpc_inproc_plugin_init, + grpc_inproc_plugin_shutdown); + grpc_register_plugin(grpc_resolver_fake_init, + grpc_resolver_fake_shutdown); + grpc_register_plugin(grpc_lb_policy_grpclb_init, + grpc_lb_policy_grpclb_shutdown); + grpc_register_plugin(grpc_lb_policy_pick_first_init, + grpc_lb_policy_pick_first_shutdown); + grpc_register_plugin(grpc_lb_policy_round_robin_init, + grpc_lb_policy_round_robin_shutdown); + grpc_register_plugin(grpc_resolver_dns_ares_init, + grpc_resolver_dns_ares_shutdown); + grpc_register_plugin(grpc_resolver_dns_native_init, + grpc_resolver_dns_native_shutdown); + grpc_register_plugin(grpc_resolver_sockaddr_init, + grpc_resolver_sockaddr_shutdown); + grpc_register_plugin(grpc_server_load_reporting_plugin_init, + grpc_server_load_reporting_plugin_shutdown); + grpc_register_plugin(census_grpc_plugin_init, + census_grpc_plugin_shutdown); + grpc_register_plugin(grpc_max_age_filter_init, + grpc_max_age_filter_shutdown); + grpc_register_plugin(grpc_message_size_filter_init, + grpc_message_size_filter_shutdown); + grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, + grpc_workaround_cronet_compression_filter_shutdown); +} diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c b/src/core/plugin_registry/grpc_unsecure_plugin_registry.c deleted file mode 100644 index 7b90d796d5..0000000000 --- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -extern void grpc_http_filters_init(void); -extern void grpc_http_filters_shutdown(void); -extern void grpc_chttp2_plugin_init(void); -extern void grpc_chttp2_plugin_shutdown(void); -extern void grpc_deadline_filter_init(void); -extern void grpc_deadline_filter_shutdown(void); -extern void grpc_client_channel_init(void); -extern void grpc_client_channel_shutdown(void); -extern void grpc_inproc_plugin_init(void); -extern void grpc_inproc_plugin_shutdown(void); -extern void grpc_resolver_dns_ares_init(void); -extern void grpc_resolver_dns_ares_shutdown(void); -extern void grpc_resolver_dns_native_init(void); -extern void grpc_resolver_dns_native_shutdown(void); -extern void grpc_resolver_sockaddr_init(void); -extern void grpc_resolver_sockaddr_shutdown(void); -extern void grpc_resolver_fake_init(void); -extern void grpc_resolver_fake_shutdown(void); -extern void grpc_server_load_reporting_plugin_init(void); -extern void grpc_server_load_reporting_plugin_shutdown(void); -extern void grpc_lb_policy_grpclb_init(void); -extern void grpc_lb_policy_grpclb_shutdown(void); -extern void grpc_lb_policy_pick_first_init(void); -extern void grpc_lb_policy_pick_first_shutdown(void); -extern void grpc_lb_policy_round_robin_init(void); -extern void grpc_lb_policy_round_robin_shutdown(void); -extern void census_grpc_plugin_init(void); -extern void census_grpc_plugin_shutdown(void); -extern void grpc_max_age_filter_init(void); -extern void grpc_max_age_filter_shutdown(void); -extern void grpc_message_size_filter_init(void); -extern void grpc_message_size_filter_shutdown(void); -extern void grpc_workaround_cronet_compression_filter_init(void); -extern void grpc_workaround_cronet_compression_filter_shutdown(void); - -void grpc_register_built_in_plugins(void) { - grpc_register_plugin(grpc_http_filters_init, - grpc_http_filters_shutdown); - grpc_register_plugin(grpc_chttp2_plugin_init, - grpc_chttp2_plugin_shutdown); - grpc_register_plugin(grpc_deadline_filter_init, - grpc_deadline_filter_shutdown); - grpc_register_plugin(grpc_client_channel_init, - grpc_client_channel_shutdown); - grpc_register_plugin(grpc_inproc_plugin_init, - grpc_inproc_plugin_shutdown); - grpc_register_plugin(grpc_resolver_dns_ares_init, - grpc_resolver_dns_ares_shutdown); - grpc_register_plugin(grpc_resolver_dns_native_init, - grpc_resolver_dns_native_shutdown); - grpc_register_plugin(grpc_resolver_sockaddr_init, - grpc_resolver_sockaddr_shutdown); - grpc_register_plugin(grpc_resolver_fake_init, - grpc_resolver_fake_shutdown); - grpc_register_plugin(grpc_server_load_reporting_plugin_init, - grpc_server_load_reporting_plugin_shutdown); - grpc_register_plugin(grpc_lb_policy_grpclb_init, - grpc_lb_policy_grpclb_shutdown); - grpc_register_plugin(grpc_lb_policy_pick_first_init, - grpc_lb_policy_pick_first_shutdown); - grpc_register_plugin(grpc_lb_policy_round_robin_init, - grpc_lb_policy_round_robin_shutdown); - grpc_register_plugin(census_grpc_plugin_init, - census_grpc_plugin_shutdown); - grpc_register_plugin(grpc_max_age_filter_init, - grpc_max_age_filter_shutdown); - grpc_register_plugin(grpc_message_size_filter_init, - grpc_message_size_filter_shutdown); - grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, - grpc_workaround_cronet_compression_filter_shutdown); -} diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc new file mode 100644 index 0000000000..78218582f0 --- /dev/null +++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc @@ -0,0 +1,91 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +extern "C" void grpc_http_filters_init(void); +extern "C" void grpc_http_filters_shutdown(void); +extern "C" void grpc_chttp2_plugin_init(void); +extern "C" void grpc_chttp2_plugin_shutdown(void); +extern "C" void grpc_deadline_filter_init(void); +extern "C" void grpc_deadline_filter_shutdown(void); +extern "C" void grpc_client_channel_init(void); +extern "C" void grpc_client_channel_shutdown(void); +extern "C" void grpc_inproc_plugin_init(void); +extern "C" void grpc_inproc_plugin_shutdown(void); +extern "C" void grpc_resolver_dns_ares_init(void); +extern "C" void grpc_resolver_dns_ares_shutdown(void); +extern "C" void grpc_resolver_dns_native_init(void); +extern "C" void grpc_resolver_dns_native_shutdown(void); +extern "C" void grpc_resolver_sockaddr_init(void); +extern "C" void grpc_resolver_sockaddr_shutdown(void); +extern "C" void grpc_resolver_fake_init(void); +extern "C" void grpc_resolver_fake_shutdown(void); +extern "C" void grpc_server_load_reporting_plugin_init(void); +extern "C" void grpc_server_load_reporting_plugin_shutdown(void); +extern "C" void grpc_lb_policy_grpclb_init(void); +extern "C" void grpc_lb_policy_grpclb_shutdown(void); +extern "C" void grpc_lb_policy_pick_first_init(void); +extern "C" void grpc_lb_policy_pick_first_shutdown(void); +extern "C" void grpc_lb_policy_round_robin_init(void); +extern "C" void grpc_lb_policy_round_robin_shutdown(void); +extern "C" void census_grpc_plugin_init(void); +extern "C" void census_grpc_plugin_shutdown(void); +extern "C" void grpc_max_age_filter_init(void); +extern "C" void grpc_max_age_filter_shutdown(void); +extern "C" void grpc_message_size_filter_init(void); +extern "C" void grpc_message_size_filter_shutdown(void); +extern "C" void grpc_workaround_cronet_compression_filter_init(void); +extern "C" void grpc_workaround_cronet_compression_filter_shutdown(void); + +void grpc_register_built_in_plugins(void) { + grpc_register_plugin(grpc_http_filters_init, + grpc_http_filters_shutdown); + grpc_register_plugin(grpc_chttp2_plugin_init, + grpc_chttp2_plugin_shutdown); + grpc_register_plugin(grpc_deadline_filter_init, + grpc_deadline_filter_shutdown); + grpc_register_plugin(grpc_client_channel_init, + grpc_client_channel_shutdown); + grpc_register_plugin(grpc_inproc_plugin_init, + grpc_inproc_plugin_shutdown); + grpc_register_plugin(grpc_resolver_dns_ares_init, + grpc_resolver_dns_ares_shutdown); + grpc_register_plugin(grpc_resolver_dns_native_init, + grpc_resolver_dns_native_shutdown); + grpc_register_plugin(grpc_resolver_sockaddr_init, + grpc_resolver_sockaddr_shutdown); + grpc_register_plugin(grpc_resolver_fake_init, + grpc_resolver_fake_shutdown); + grpc_register_plugin(grpc_server_load_reporting_plugin_init, + grpc_server_load_reporting_plugin_shutdown); + grpc_register_plugin(grpc_lb_policy_grpclb_init, + grpc_lb_policy_grpclb_shutdown); + grpc_register_plugin(grpc_lb_policy_pick_first_init, + grpc_lb_policy_pick_first_shutdown); + grpc_register_plugin(grpc_lb_policy_round_robin_init, + grpc_lb_policy_round_robin_shutdown); + grpc_register_plugin(census_grpc_plugin_init, + census_grpc_plugin_shutdown); + grpc_register_plugin(grpc_max_age_filter_init, + grpc_max_age_filter_shutdown); + grpc_register_plugin(grpc_message_size_filter_init, + grpc_message_size_filter_shutdown); + grpc_register_plugin(grpc_workaround_cronet_compression_filter_init, + grpc_workaround_cronet_compression_filter_shutdown); +} diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 7b684f2a58..29e210042d 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -321,7 +321,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/message_size/message_size_filter.c', 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', 'src/core/ext/filters/workarounds/workaround_utils.c', - 'src/core/plugin_registry/grpc_plugin_registry.c', + 'src/core/plugin_registry/grpc_plugin_registry.cc', 'src/boringssl/err_data.c', 'third_party/boringssl/crypto/aes/aes.c', 'third_party/boringssl/crypto/aes/key_wrap.c', diff --git a/templates/src/core/lib/surface/version.c.template b/templates/src/core/lib/surface/version.c.template deleted file mode 100644 index d2efa565e5..0000000000 --- a/templates/src/core/lib/surface/version.c.template +++ /dev/null @@ -1,28 +0,0 @@ -%YAML 1.2 ---- | - /* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - - /* This file is autogenerated from: - templates/src/core/surface/version.c.template */ - - #include - - const char *grpc_version_string(void) { return "${settings.core_version}"; } - - const char *grpc_g_stands_for(void) { return "${settings.g_stands_for}"; } diff --git a/templates/src/core/lib/surface/version.cc.template b/templates/src/core/lib/surface/version.cc.template new file mode 100644 index 0000000000..d2efa565e5 --- /dev/null +++ b/templates/src/core/lib/surface/version.cc.template @@ -0,0 +1,28 @@ +%YAML 1.2 +--- | + /* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + + /* This file is autogenerated from: + templates/src/core/surface/version.c.template */ + + #include + + const char *grpc_version_string(void) { return "${settings.core_version}"; } + + const char *grpc_g_stands_for(void) { return "${settings.g_stands_for}"; } diff --git a/templates/src/core/plugin_registry.template b/templates/src/core/plugin_registry.template index cf0f4f523e..8d7617129f 100644 --- a/templates/src/core/plugin_registry.template +++ b/templates/src/core/plugin_registry.template @@ -2,7 +2,7 @@ --- foreach: libs cond: selected.get('generate_plugin_registry', False) -output_name: ${selected.name}_plugin_registry.c +output_name: ${selected.name}_plugin_registry.cc template: | /* * @@ -25,8 +25,8 @@ template: | #include %for plugin in selected.plugins: - extern void ${plugin}_init(void); - extern void ${plugin}_shutdown(void); + extern "C" void ${plugin}_init(void); + extern "C" void ${plugin}_shutdown(void); %endfor void grpc_register_built_in_plugins(void) { diff --git a/tools/buildgen/plugins/expand_filegroups.py b/tools/buildgen/plugins/expand_filegroups.py index dc073d72b8..669704063e 100755 --- a/tools/buildgen/plugins/expand_filegroups.py +++ b/tools/buildgen/plugins/expand_filegroups.py @@ -146,7 +146,7 @@ def mako_plugin(dictionary): lib[lst] = vals lib['plugins'] = plugins if lib.get('generate_plugin_registry', False): - lib['src'].append('src/core/plugin_registry/%s_plugin_registry.c' % + lib['src'].append('src/core/plugin_registry/%s_plugin_registry.cc' % lib['name']) for lst in FILEGROUP_LISTS: lib[lst] = uniquify(lib.get(lst, [])) diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 33cafacdde..205cb2971e 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1417,7 +1417,7 @@ src/core/lib/transport/transport.c \ src/core/lib/transport/transport.h \ src/core/lib/transport/transport_impl.h \ src/core/lib/transport/transport_op_string.c \ -src/core/plugin_registry/grpc_plugin_registry.c \ +src/core/plugin_registry/grpc_plugin_registry.cc \ src/core/tsi/README.md \ src/core/tsi/fake_transport_security.c \ src/core/tsi/fake_transport_security.h \ -- cgit v1.2.3 From 83062842c3601faeddcae8f901c515e3c78f3661 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Thu, 21 Sep 2017 18:56:08 -0700 Subject: Changes for C to C++. Adding extern C to header files for compatibility. Also converting to .cc --- CMakeLists.txt | 2652 ++++++++-------- Makefile | 2724 ++++++++-------- binding.gyp | 602 ++-- build.yaml | 656 ++-- config.m4 | 602 ++-- config.w32 | 602 ++-- gRPC-Core.podspec | 602 ++-- grpc.gemspec | 602 ++-- grpc.gyp | 1822 ++++++----- package.xml | 602 ++-- src/core/ext/census/base_resources.c | 56 - src/core/ext/census/base_resources.cc | 56 + src/core/ext/census/census_init.c | 33 - src/core/ext/census/census_init.cc | 33 + src/core/ext/census/census_log.c | 588 ---- src/core/ext/census/census_log.cc | 588 ++++ src/core/ext/census/census_rpc_stats.c | 238 -- src/core/ext/census/census_rpc_stats.cc | 238 ++ src/core/ext/census/census_tracing.c | 226 -- src/core/ext/census/census_tracing.cc | 226 ++ src/core/ext/census/context.c | 496 --- src/core/ext/census/context.cc | 496 +++ src/core/ext/census/gen/census.pb.c | 161 - src/core/ext/census/gen/census.pb.cc | 161 + src/core/ext/census/gen/trace_context.pb.c | 39 - src/core/ext/census/gen/trace_context.pb.cc | 39 + src/core/ext/census/grpc_context.c | 38 - src/core/ext/census/grpc_context.cc | 38 + src/core/ext/census/grpc_filter.c | 196 -- src/core/ext/census/grpc_filter.cc | 196 ++ src/core/ext/census/grpc_plugin.c | 70 - src/core/ext/census/grpc_plugin.cc | 70 + src/core/ext/census/hash_table.c | 288 -- src/core/ext/census/hash_table.cc | 288 ++ src/core/ext/census/initialize.c | 51 - src/core/ext/census/initialize.cc | 51 + src/core/ext/census/intrusive_hash_map.c | 305 -- src/core/ext/census/intrusive_hash_map.cc | 305 ++ src/core/ext/census/mlog.c | 586 ---- src/core/ext/census/mlog.cc | 586 ++++ src/core/ext/census/operation.c | 48 - src/core/ext/census/operation.cc | 48 + src/core/ext/census/placeholders.c | 49 - src/core/ext/census/placeholders.cc | 49 + src/core/ext/census/resource.c | 303 -- src/core/ext/census/resource.cc | 303 ++ src/core/ext/census/trace_context.c | 71 - src/core/ext/census/trace_context.cc | 71 + src/core/ext/census/tracing.c | 55 - src/core/ext/census/tracing.cc | 55 + src/core/ext/census/window_stats.c | 301 -- src/core/ext/census/window_stats.cc | 301 ++ .../filters/client_channel/channel_connectivity.c | 249 -- .../filters/client_channel/channel_connectivity.cc | 249 ++ .../ext/filters/client_channel/client_channel.c | 1657 ---------- .../ext/filters/client_channel/client_channel.cc | 1657 ++++++++++ .../client_channel/client_channel_factory.c | 68 - .../client_channel/client_channel_factory.cc | 68 + .../filters/client_channel/client_channel_plugin.c | 94 - .../client_channel/client_channel_plugin.cc | 94 + src/core/ext/filters/client_channel/connector.c | 40 - src/core/ext/filters/client_channel/connector.cc | 40 + .../client_channel/http_connect_handshaker.c | 376 --- .../client_channel/http_connect_handshaker.cc | 376 +++ src/core/ext/filters/client_channel/http_proxy.c | 195 -- src/core/ext/filters/client_channel/http_proxy.cc | 195 ++ src/core/ext/filters/client_channel/lb_policy.c | 164 - src/core/ext/filters/client_channel/lb_policy.cc | 164 + .../grpclb/client_load_reporting_filter.c | 137 - .../grpclb/client_load_reporting_filter.cc | 137 + .../client_channel/lb_policy/grpclb/grpclb.c | 2021 ------------ .../client_channel/lb_policy/grpclb/grpclb.cc | 2021 ++++++++++++ .../lb_policy/grpclb/grpclb_channel.c | 71 - .../lb_policy/grpclb/grpclb_channel.cc | 71 + .../lb_policy/grpclb/grpclb_channel_secure.c | 99 - .../lb_policy/grpclb/grpclb_channel_secure.cc | 99 + .../lb_policy/grpclb/grpclb_client_stats.c | 151 - .../lb_policy/grpclb/grpclb_client_stats.cc | 151 + .../lb_policy/grpclb/load_balancer_api.c | 314 -- .../lb_policy/grpclb/load_balancer_api.cc | 314 ++ .../grpclb/proto/grpc/lb/v1/load_balancer.pb.c | 103 - .../grpclb/proto/grpc/lb/v1/load_balancer.pb.cc | 103 + .../lb_policy/pick_first/pick_first.c | 714 ----- .../lb_policy/pick_first/pick_first.cc | 714 +++++ .../lb_policy/round_robin/round_robin.c | 924 ------ .../lb_policy/round_robin/round_robin.cc | 924 ++++++ .../ext/filters/client_channel/lb_policy_factory.c | 169 - .../filters/client_channel/lb_policy_factory.cc | 169 + .../filters/client_channel/lb_policy_registry.c | 70 - .../filters/client_channel/lb_policy_registry.cc | 70 + .../ext/filters/client_channel/parse_address.c | 186 -- .../ext/filters/client_channel/parse_address.cc | 186 ++ src/core/ext/filters/client_channel/proxy_mapper.c | 48 - .../ext/filters/client_channel/proxy_mapper.cc | 48 + .../filters/client_channel/proxy_mapper_registry.c | 124 - .../client_channel/proxy_mapper_registry.cc | 124 + src/core/ext/filters/client_channel/resolver.c | 83 - src/core/ext/filters/client_channel/resolver.cc | 83 + .../resolver/dns/c_ares/dns_resolver_ares.c | 458 --- .../resolver/dns/c_ares/dns_resolver_ares.cc | 458 +++ .../dns/c_ares/grpc_ares_ev_driver_posix.c | 356 --- .../dns/c_ares/grpc_ares_ev_driver_posix.cc | 356 +++ .../resolver/dns/c_ares/grpc_ares_wrapper.c | 550 ---- .../resolver/dns/c_ares/grpc_ares_wrapper.cc | 550 ++++ .../dns/c_ares/grpc_ares_wrapper_fallback.c | 60 - .../dns/c_ares/grpc_ares_wrapper_fallback.cc | 60 + .../resolver/dns/native/dns_resolver.c | 310 -- .../resolver/dns/native/dns_resolver.cc | 310 ++ .../client_channel/resolver/fake/fake_resolver.c | 265 -- .../client_channel/resolver/fake/fake_resolver.cc | 265 ++ .../resolver/sockaddr/sockaddr_resolver.c | 222 -- .../resolver/sockaddr/sockaddr_resolver.cc | 222 ++ .../ext/filters/client_channel/resolver_factory.c | 41 - .../ext/filters/client_channel/resolver_factory.cc | 41 + .../ext/filters/client_channel/resolver_registry.c | 159 - .../filters/client_channel/resolver_registry.cc | 159 + .../ext/filters/client_channel/retry_throttle.c | 202 -- .../ext/filters/client_channel/retry_throttle.cc | 202 ++ src/core/ext/filters/client_channel/subchannel.c | 816 ----- src/core/ext/filters/client_channel/subchannel.cc | 816 +++++ .../ext/filters/client_channel/subchannel_index.c | 251 -- .../ext/filters/client_channel/subchannel_index.cc | 251 ++ src/core/ext/filters/client_channel/uri_parser.c | 315 -- src/core/ext/filters/client_channel/uri_parser.cc | 315 ++ src/core/ext/filters/deadline/deadline_filter.c | 397 --- src/core/ext/filters/deadline/deadline_filter.cc | 397 +++ .../ext/filters/http/client/http_client_filter.c | 570 ---- .../ext/filters/http/client/http_client_filter.cc | 570 ++++ src/core/ext/filters/http/http_filters_plugin.c | 89 - src/core/ext/filters/http/http_filters_plugin.cc | 89 + .../message_compress/message_compress_filter.c | 547 ---- .../message_compress/message_compress_filter.cc | 547 ++++ .../ext/filters/http/server/http_server_filter.c | 440 --- .../ext/filters/http/server/http_server_filter.cc | 440 +++ .../load_reporting/server_load_reporting_filter.c | 227 -- .../load_reporting/server_load_reporting_filter.cc | 227 ++ .../load_reporting/server_load_reporting_plugin.c | 70 - .../load_reporting/server_load_reporting_plugin.cc | 70 + src/core/ext/filters/max_age/max_age_filter.c | 423 --- src/core/ext/filters/max_age/max_age_filter.cc | 423 +++ .../ext/filters/message_size/message_size_filter.c | 303 -- .../filters/message_size/message_size_filter.cc | 303 ++ .../workaround_cronet_compression_filter.c | 207 -- .../workaround_cronet_compression_filter.cc | 207 ++ .../ext/filters/workarounds/workaround_utils.c | 51 - .../ext/filters/workarounds/workaround_utils.cc | 51 + src/core/ext/transport/chttp2/alpn/alpn.c | 41 - src/core/ext/transport/chttp2/alpn/alpn.cc | 41 + .../ext/transport/chttp2/client/chttp2_connector.c | 206 -- .../transport/chttp2/client/chttp2_connector.cc | 206 ++ .../chttp2/client/insecure/channel_create.c | 104 - .../chttp2/client/insecure/channel_create.cc | 104 + .../chttp2/client/insecure/channel_create_posix.c | 78 - .../chttp2/client/insecure/channel_create_posix.cc | 78 + .../chttp2/client/secure/secure_channel_create.c | 224 -- .../chttp2/client/secure/secure_channel_create.cc | 224 ++ .../ext/transport/chttp2/server/chttp2_server.c | 292 -- .../ext/transport/chttp2/server/chttp2_server.cc | 292 ++ .../chttp2/server/insecure/server_chttp2.c | 44 - .../chttp2/server/insecure/server_chttp2.cc | 44 + .../chttp2/server/insecure/server_chttp2_posix.c | 75 - .../chttp2/server/insecure/server_chttp2_posix.cc | 75 + .../chttp2/server/secure/server_secure_chttp2.c | 88 - .../chttp2/server/secure/server_secure_chttp2.cc | 88 + .../ext/transport/chttp2/transport/bin_decoder.c | 222 -- .../ext/transport/chttp2/transport/bin_decoder.cc | 222 ++ .../ext/transport/chttp2/transport/bin_encoder.c | 226 -- .../ext/transport/chttp2/transport/bin_encoder.cc | 226 ++ .../ext/transport/chttp2/transport/chttp2_plugin.c | 32 - .../transport/chttp2/transport/chttp2_plugin.cc | 32 + .../transport/chttp2/transport/chttp2_transport.c | 3252 -------------------- .../transport/chttp2/transport/chttp2_transport.cc | 3252 ++++++++++++++++++++ .../ext/transport/chttp2/transport/flow_control.c | 502 --- .../ext/transport/chttp2/transport/flow_control.cc | 502 +++ .../ext/transport/chttp2/transport/frame_data.c | 318 -- .../ext/transport/chttp2/transport/frame_data.cc | 318 ++ .../ext/transport/chttp2/transport/frame_goaway.c | 183 -- .../ext/transport/chttp2/transport/frame_goaway.cc | 183 ++ .../ext/transport/chttp2/transport/frame_ping.c | 131 - .../ext/transport/chttp2/transport/frame_ping.cc | 131 + .../transport/chttp2/transport/frame_rst_stream.c | 110 - .../transport/chttp2/transport/frame_rst_stream.cc | 110 + .../transport/chttp2/transport/frame_settings.c | 227 -- .../transport/chttp2/transport/frame_settings.cc | 227 ++ .../chttp2/transport/frame_window_update.c | 122 - .../chttp2/transport/frame_window_update.cc | 122 + .../ext/transport/chttp2/transport/hpack_encoder.c | 669 ---- .../transport/chttp2/transport/hpack_encoder.cc | 669 ++++ .../ext/transport/chttp2/transport/hpack_parser.c | 1761 ----------- .../ext/transport/chttp2/transport/hpack_parser.cc | 1761 +++++++++++ .../ext/transport/chttp2/transport/hpack_table.c | 369 --- .../ext/transport/chttp2/transport/hpack_table.cc | 369 +++ .../transport/chttp2/transport/http2_settings.c | 60 - .../transport/chttp2/transport/http2_settings.cc | 60 + src/core/ext/transport/chttp2/transport/huffsyms.c | 90 - .../ext/transport/chttp2/transport/huffsyms.cc | 90 + .../transport/chttp2/transport/incoming_metadata.c | 74 - .../chttp2/transport/incoming_metadata.cc | 74 + src/core/ext/transport/chttp2/transport/parsing.c | 766 ----- src/core/ext/transport/chttp2/transport/parsing.cc | 766 +++++ .../ext/transport/chttp2/transport/stream_lists.c | 212 -- .../ext/transport/chttp2/transport/stream_lists.cc | 212 ++ .../ext/transport/chttp2/transport/stream_map.c | 163 - .../ext/transport/chttp2/transport/stream_map.cc | 163 + src/core/ext/transport/chttp2/transport/varint.c | 54 - src/core/ext/transport/chttp2/transport/varint.cc | 54 + src/core/ext/transport/chttp2/transport/writing.c | 534 ---- src/core/ext/transport/chttp2/transport/writing.cc | 534 ++++ .../cronet/client/secure/cronet_channel_create.c | 53 - .../cronet/client/secure/cronet_channel_create.cc | 53 + .../transport/cronet/transport/cronet_api_dummy.c | 80 - .../transport/cronet/transport/cronet_api_dummy.cc | 80 + .../transport/cronet/transport/cronet_transport.c | 1495 --------- .../transport/cronet/transport/cronet_transport.cc | 1495 +++++++++ src/core/ext/transport/inproc/inproc_plugin.c | 29 - src/core/ext/transport/inproc/inproc_plugin.cc | 31 + src/core/ext/transport/inproc/inproc_transport.c | 1299 -------- src/core/ext/transport/inproc/inproc_transport.cc | 1299 ++++++++ src/core/lib/channel/channel_args.c | 501 --- src/core/lib/channel/channel_args.cc | 501 +++ src/core/lib/channel/channel_stack.c | 262 -- src/core/lib/channel/channel_stack.cc | 262 ++ src/core/lib/channel/channel_stack_builder.c | 312 -- src/core/lib/channel/channel_stack_builder.cc | 312 ++ src/core/lib/channel/connected_channel.c | 246 -- src/core/lib/channel/connected_channel.cc | 246 ++ src/core/lib/channel/handshaker.c | 268 -- src/core/lib/channel/handshaker.cc | 268 ++ src/core/lib/channel/handshaker_factory.c | 39 - src/core/lib/channel/handshaker_factory.cc | 39 + src/core/lib/channel/handshaker_registry.c | 98 - src/core/lib/channel/handshaker_registry.cc | 98 + src/core/lib/compression/compression.c | 283 -- src/core/lib/compression/compression.cc | 283 ++ src/core/lib/compression/message_compress.c | 189 -- src/core/lib/compression/message_compress.cc | 189 ++ src/core/lib/compression/stream_compression.c | 77 - src/core/lib/compression/stream_compression.cc | 77 + src/core/lib/debug/stats.c | 174 -- src/core/lib/debug/stats.cc | 174 ++ src/core/lib/debug/stats_data.c | 687 ----- src/core/lib/debug/stats_data.cc | 687 +++++ src/core/lib/debug/trace.c | 147 - src/core/lib/debug/trace.cc | 147 + src/core/lib/http/format_request.c | 120 - src/core/lib/http/format_request.cc | 120 + src/core/lib/http/httpcli.c | 321 -- src/core/lib/http/httpcli.cc | 321 ++ src/core/lib/http/httpcli_security_connector.c | 186 -- src/core/lib/http/httpcli_security_connector.cc | 186 ++ src/core/lib/http/parser.c | 365 --- src/core/lib/http/parser.cc | 365 +++ src/core/lib/iomgr/call_combiner.c | 202 -- src/core/lib/iomgr/call_combiner.cc | 202 ++ src/core/lib/iomgr/closure.c | 219 -- src/core/lib/iomgr/closure.cc | 219 ++ src/core/lib/iomgr/combiner.c | 370 --- src/core/lib/iomgr/combiner.cc | 370 +++ src/core/lib/iomgr/endpoint.c | 59 - src/core/lib/iomgr/endpoint.cc | 59 + src/core/lib/iomgr/endpoint_pair_posix.c | 72 - src/core/lib/iomgr/endpoint_pair_posix.cc | 72 + src/core/lib/iomgr/endpoint_pair_uv.c | 38 - src/core/lib/iomgr/endpoint_pair_uv.cc | 38 + src/core/lib/iomgr/endpoint_pair_windows.c | 86 - src/core/lib/iomgr/endpoint_pair_windows.cc | 86 + src/core/lib/iomgr/error.c | 801 ----- src/core/lib/iomgr/error.cc | 801 +++++ src/core/lib/iomgr/ev_epoll1_linux.c | 1267 -------- src/core/lib/iomgr/ev_epoll1_linux.cc | 1267 ++++++++ src/core/lib/iomgr/ev_epollex_linux.c | 1461 --------- src/core/lib/iomgr/ev_epollex_linux.cc | 1461 +++++++++ src/core/lib/iomgr/ev_epollsig_linux.c | 1769 ----------- src/core/lib/iomgr/ev_epollsig_linux.cc | 1769 +++++++++++ src/core/lib/iomgr/ev_poll_posix.c | 1746 ----------- src/core/lib/iomgr/ev_poll_posix.cc | 1746 +++++++++++ src/core/lib/iomgr/ev_posix.c | 266 -- src/core/lib/iomgr/ev_posix.cc | 266 ++ src/core/lib/iomgr/ev_windows.c | 28 - src/core/lib/iomgr/ev_windows.cc | 28 + src/core/lib/iomgr/exec_ctx.c | 113 - src/core/lib/iomgr/exec_ctx.cc | 113 + src/core/lib/iomgr/executor.c | 301 -- src/core/lib/iomgr/executor.cc | 301 ++ src/core/lib/iomgr/gethostname_fallback.c | 27 - src/core/lib/iomgr/gethostname_fallback.cc | 27 + src/core/lib/iomgr/gethostname_host_name_max.c | 37 - src/core/lib/iomgr/gethostname_host_name_max.cc | 37 + src/core/lib/iomgr/gethostname_sysconf.c | 37 - src/core/lib/iomgr/gethostname_sysconf.cc | 37 + src/core/lib/iomgr/iocp_windows.c | 155 - src/core/lib/iomgr/iocp_windows.cc | 155 + src/core/lib/iomgr/iomgr.c | 170 - src/core/lib/iomgr/iomgr.cc | 170 + src/core/lib/iomgr/iomgr_posix.c | 41 - src/core/lib/iomgr/iomgr_posix.cc | 41 + src/core/lib/iomgr/iomgr_uv.c | 42 - src/core/lib/iomgr/iomgr_uv.cc | 42 + src/core/lib/iomgr/iomgr_windows.c | 61 - src/core/lib/iomgr/iomgr_windows.cc | 61 + src/core/lib/iomgr/is_epollexclusive_available.c | 101 - src/core/lib/iomgr/is_epollexclusive_available.cc | 101 + src/core/lib/iomgr/load_file.c | 78 - src/core/lib/iomgr/load_file.cc | 78 + src/core/lib/iomgr/lockfree_event.c | 241 -- src/core/lib/iomgr/lockfree_event.cc | 241 ++ src/core/lib/iomgr/network_status_tracker.c | 34 - src/core/lib/iomgr/network_status_tracker.cc | 34 + src/core/lib/iomgr/polling_entity.c | 89 - src/core/lib/iomgr/polling_entity.cc | 89 + src/core/lib/iomgr/pollset_set_uv.c | 48 - src/core/lib/iomgr/pollset_set_uv.cc | 48 + src/core/lib/iomgr/pollset_set_windows.c | 49 - src/core/lib/iomgr/pollset_set_windows.cc | 49 + src/core/lib/iomgr/pollset_uv.c | 155 - src/core/lib/iomgr/pollset_uv.cc | 155 + src/core/lib/iomgr/pollset_windows.c | 222 -- src/core/lib/iomgr/pollset_windows.cc | 222 ++ src/core/lib/iomgr/resolve_address_posix.c | 193 -- src/core/lib/iomgr/resolve_address_posix.cc | 193 ++ src/core/lib/iomgr/resolve_address_uv.c | 280 -- src/core/lib/iomgr/resolve_address_uv.cc | 280 ++ src/core/lib/iomgr/resolve_address_windows.c | 175 -- src/core/lib/iomgr/resolve_address_windows.cc | 175 ++ src/core/lib/iomgr/resource_quota.c | 868 ------ src/core/lib/iomgr/resource_quota.cc | 868 ++++++ src/core/lib/iomgr/sockaddr_utils.c | 262 -- src/core/lib/iomgr/sockaddr_utils.cc | 262 ++ src/core/lib/iomgr/socket_factory_posix.c | 92 - src/core/lib/iomgr/socket_factory_posix.cc | 92 + src/core/lib/iomgr/socket_mutator.c | 81 - src/core/lib/iomgr/socket_mutator.cc | 81 + src/core/lib/iomgr/socket_utils_common_posix.c | 315 -- src/core/lib/iomgr/socket_utils_common_posix.cc | 315 ++ src/core/lib/iomgr/socket_utils_linux.c | 42 - src/core/lib/iomgr/socket_utils_linux.cc | 42 + src/core/lib/iomgr/socket_utils_posix.c | 58 - src/core/lib/iomgr/socket_utils_posix.cc | 58 + src/core/lib/iomgr/socket_utils_uv.c | 34 - src/core/lib/iomgr/socket_utils_uv.cc | 34 + src/core/lib/iomgr/socket_utils_windows.c | 33 - src/core/lib/iomgr/socket_utils_windows.cc | 33 + src/core/lib/iomgr/socket_windows.c | 152 - src/core/lib/iomgr/socket_windows.cc | 152 + src/core/lib/iomgr/tcp_client_posix.c | 356 --- src/core/lib/iomgr/tcp_client_posix.cc | 356 +++ src/core/lib/iomgr/tcp_client_uv.c | 183 -- src/core/lib/iomgr/tcp_client_uv.cc | 183 ++ src/core/lib/iomgr/tcp_client_windows.c | 245 -- src/core/lib/iomgr/tcp_client_windows.cc | 245 ++ src/core/lib/iomgr/tcp_posix.c | 819 ----- src/core/lib/iomgr/tcp_posix.cc | 819 +++++ src/core/lib/iomgr/tcp_server_posix.c | 565 ---- src/core/lib/iomgr/tcp_server_posix.cc | 565 ++++ src/core/lib/iomgr/tcp_server_utils_posix_common.c | 206 -- .../lib/iomgr/tcp_server_utils_posix_common.cc | 206 ++ .../lib/iomgr/tcp_server_utils_posix_ifaddrs.c | 181 -- .../lib/iomgr/tcp_server_utils_posix_ifaddrs.cc | 181 ++ .../lib/iomgr/tcp_server_utils_posix_noifaddrs.c | 34 - .../lib/iomgr/tcp_server_utils_posix_noifaddrs.cc | 34 + src/core/lib/iomgr/tcp_server_uv.c | 454 --- src/core/lib/iomgr/tcp_server_uv.cc | 454 +++ src/core/lib/iomgr/tcp_server_windows.c | 543 ---- src/core/lib/iomgr/tcp_server_windows.cc | 543 ++++ src/core/lib/iomgr/tcp_uv.c | 381 --- src/core/lib/iomgr/tcp_uv.cc | 381 +++ src/core/lib/iomgr/tcp_windows.c | 448 --- src/core/lib/iomgr/tcp_windows.cc | 448 +++ src/core/lib/iomgr/time_averaged_stats.c | 62 - src/core/lib/iomgr/time_averaged_stats.cc | 62 + src/core/lib/iomgr/timer_generic.c | 705 ----- src/core/lib/iomgr/timer_generic.cc | 705 +++++ src/core/lib/iomgr/timer_heap.c | 137 - src/core/lib/iomgr/timer_heap.cc | 137 + src/core/lib/iomgr/timer_manager.c | 354 --- src/core/lib/iomgr/timer_manager.cc | 354 +++ src/core/lib/iomgr/timer_uv.c | 101 - src/core/lib/iomgr/timer_uv.cc | 101 + src/core/lib/iomgr/udp_server.c | 549 ---- src/core/lib/iomgr/udp_server.cc | 549 ++++ src/core/lib/iomgr/unix_sockets_posix.c | 95 - src/core/lib/iomgr/unix_sockets_posix.cc | 95 + src/core/lib/iomgr/unix_sockets_posix_noop.c | 47 - src/core/lib/iomgr/unix_sockets_posix_noop.cc | 47 + src/core/lib/iomgr/wakeup_fd_cv.c | 104 - src/core/lib/iomgr/wakeup_fd_cv.cc | 104 + src/core/lib/iomgr/wakeup_fd_eventfd.c | 82 - src/core/lib/iomgr/wakeup_fd_eventfd.cc | 82 + src/core/lib/iomgr/wakeup_fd_nospecial.c | 36 - src/core/lib/iomgr/wakeup_fd_nospecial.cc | 36 + src/core/lib/iomgr/wakeup_fd_pipe.c | 98 - src/core/lib/iomgr/wakeup_fd_pipe.cc | 98 + src/core/lib/iomgr/wakeup_fd_posix.c | 85 - src/core/lib/iomgr/wakeup_fd_posix.cc | 85 + src/core/lib/json/json.c | 48 - src/core/lib/json/json.cc | 48 + src/core/lib/json/json_reader.c | 661 ---- src/core/lib/json/json_reader.cc | 661 ++++ src/core/lib/json/json_string.c | 364 --- src/core/lib/json/json_string.cc | 364 +++ src/core/lib/json/json_writer.c | 243 -- src/core/lib/json/json_writer.cc | 243 ++ src/core/lib/profiling/basic_timers.c | 283 -- src/core/lib/profiling/basic_timers.cc | 283 ++ src/core/lib/profiling/stap_timers.c | 50 - src/core/lib/profiling/stap_timers.cc | 50 + src/core/lib/security/context/security_context.c | 346 --- src/core/lib/security/context/security_context.cc | 346 +++ .../credentials/composite/composite_credentials.c | 267 -- .../credentials/composite/composite_credentials.cc | 267 ++ src/core/lib/security/credentials/credentials.c | 289 -- src/core/lib/security/credentials/credentials.cc | 289 ++ .../security/credentials/credentials_metadata.c | 60 - .../security/credentials/credentials_metadata.cc | 60 + .../security/credentials/fake/fake_credentials.c | 144 - .../security/credentials/fake/fake_credentials.cc | 144 + .../google_default/credentials_generic.c | 39 - .../google_default/credentials_generic.cc | 39 + .../google_default/google_default_credentials.c | 323 -- .../google_default/google_default_credentials.cc | 324 ++ .../lib/security/credentials/iam/iam_credentials.c | 86 - .../security/credentials/iam/iam_credentials.cc | 86 + src/core/lib/security/credentials/jwt/json_token.c | 307 -- .../lib/security/credentials/jwt/json_token.cc | 307 ++ .../lib/security/credentials/jwt/jwt_credentials.c | 193 -- .../security/credentials/jwt/jwt_credentials.cc | 193 ++ .../lib/security/credentials/jwt/jwt_verifier.c | 939 ------ .../lib/security/credentials/jwt/jwt_verifier.cc | 939 ++++++ .../credentials/oauth2/oauth2_credentials.c | 539 ---- .../credentials/oauth2/oauth2_credentials.cc | 539 ++++ .../credentials/plugin/plugin_credentials.c | 272 -- .../credentials/plugin/plugin_credentials.cc | 272 ++ .../lib/security/credentials/ssl/ssl_credentials.c | 195 -- .../security/credentials/ssl/ssl_credentials.cc | 195 ++ .../lib/security/transport/client_auth_filter.c | 432 --- .../lib/security/transport/client_auth_filter.cc | 432 +++ src/core/lib/security/transport/lb_targets_info.c | 57 - src/core/lib/security/transport/lb_targets_info.cc | 57 + src/core/lib/security/transport/secure_endpoint.c | 433 --- src/core/lib/security/transport/secure_endpoint.cc | 433 +++ .../lib/security/transport/security_connector.c | 921 ------ .../lib/security/transport/security_connector.cc | 921 ++++++ .../lib/security/transport/security_handshaker.c | 539 ---- .../lib/security/transport/security_handshaker.cc | 539 ++++ .../lib/security/transport/server_auth_filter.c | 274 -- .../lib/security/transport/server_auth_filter.cc | 274 ++ src/core/lib/security/transport/tsi_error.c | 27 - src/core/lib/security/transport/tsi_error.cc | 27 + src/core/lib/security/util/json_util.c | 46 - src/core/lib/security/util/json_util.cc | 46 + src/core/lib/slice/b64.c | 236 -- src/core/lib/slice/b64.cc | 236 ++ src/core/lib/slice/percent_encoding.c | 167 - src/core/lib/slice/percent_encoding.cc | 167 + src/core/lib/slice/slice.c | 486 --- src/core/lib/slice/slice.cc | 486 +++ src/core/lib/slice/slice_buffer.c | 360 --- src/core/lib/slice/slice_buffer.cc | 360 +++ src/core/lib/slice/slice_hash_table.c | 146 - src/core/lib/slice/slice_hash_table.cc | 146 + src/core/lib/slice/slice_intern.c | 334 -- src/core/lib/slice/slice_intern.cc | 334 ++ src/core/lib/slice/slice_string_helpers.c | 80 - src/core/lib/slice/slice_string_helpers.cc | 80 + src/core/lib/support/alloc.c | 102 - src/core/lib/support/alloc.cc | 102 + src/core/lib/support/arena.c | 83 - src/core/lib/support/arena.cc | 83 + src/core/lib/support/atm.c | 32 - src/core/lib/support/atm.cc | 32 + src/core/lib/support/avl.c | 299 -- src/core/lib/support/avl.cc | 299 ++ src/core/lib/support/backoff.c | 72 - src/core/lib/support/backoff.cc | 72 + src/core/lib/support/cmdline.c | 330 -- src/core/lib/support/cmdline.cc | 330 ++ src/core/lib/support/cpu_iphone.c | 34 - src/core/lib/support/cpu_iphone.cc | 34 + src/core/lib/support/cpu_linux.c | 68 - src/core/lib/support/cpu_linux.cc | 68 + src/core/lib/support/cpu_posix.c | 57 - src/core/lib/support/cpu_posix.cc | 57 + src/core/lib/support/cpu_windows.c | 32 - src/core/lib/support/cpu_windows.cc | 32 + src/core/lib/support/env_linux.c | 82 - src/core/lib/support/env_linux.cc | 82 + src/core/lib/support/env_posix.c | 47 - src/core/lib/support/env_posix.cc | 47 + src/core/lib/support/env_windows.c | 69 - src/core/lib/support/env_windows.cc | 69 + src/core/lib/support/histogram.c | 228 -- src/core/lib/support/histogram.cc | 228 ++ src/core/lib/support/host_port.c | 95 - src/core/lib/support/host_port.cc | 95 + src/core/lib/support/log.c | 94 - src/core/lib/support/log.cc | 94 + src/core/lib/support/log_android.c | 72 - src/core/lib/support/log_android.cc | 72 + src/core/lib/support/log_linux.c | 92 - src/core/lib/support/log_linux.cc | 92 + src/core/lib/support/log_posix.c | 91 - src/core/lib/support/log_posix.cc | 91 + src/core/lib/support/log_windows.c | 97 - src/core/lib/support/log_windows.cc | 97 + src/core/lib/support/mpscq.c | 79 - src/core/lib/support/mpscq.cc | 79 + src/core/lib/support/murmur_hash.c | 81 - src/core/lib/support/murmur_hash.cc | 81 + src/core/lib/support/stack_lockfree.c | 137 - src/core/lib/support/stack_lockfree.cc | 137 + src/core/lib/support/string.c | 315 -- src/core/lib/support/string.cc | 315 ++ src/core/lib/support/string_posix.c | 72 - src/core/lib/support/string_posix.cc | 72 + src/core/lib/support/string_util_windows.c | 79 - src/core/lib/support/string_util_windows.cc | 79 + src/core/lib/support/string_windows.c | 69 - src/core/lib/support/string_windows.cc | 69 + src/core/lib/support/subprocess_posix.c | 99 - src/core/lib/support/subprocess_posix.cc | 99 + src/core/lib/support/subprocess_windows.c | 126 - src/core/lib/support/subprocess_windows.cc | 126 + src/core/lib/support/sync.c | 122 - src/core/lib/support/sync.cc | 122 + src/core/lib/support/sync_posix.c | 98 - src/core/lib/support/sync_posix.cc | 98 + src/core/lib/support/sync_windows.c | 118 - src/core/lib/support/sync_windows.cc | 118 + src/core/lib/support/thd.c | 49 - src/core/lib/support/thd.cc | 49 + src/core/lib/support/thd_posix.c | 80 - src/core/lib/support/thd_posix.cc | 80 + src/core/lib/support/thd_windows.c | 102 - src/core/lib/support/thd_windows.cc | 102 + src/core/lib/support/time.c | 247 -- src/core/lib/support/time.cc | 247 ++ src/core/lib/support/time_posix.c | 169 - src/core/lib/support/time_posix.cc | 169 + src/core/lib/support/time_precise.c | 76 - src/core/lib/support/time_precise.cc | 76 + src/core/lib/support/time_windows.c | 101 - src/core/lib/support/time_windows.cc | 101 + src/core/lib/support/tls_pthread.c | 30 - src/core/lib/support/tls_pthread.cc | 30 + src/core/lib/support/tmpfile_msys.c | 58 - src/core/lib/support/tmpfile_msys.cc | 58 + src/core/lib/support/tmpfile_posix.c | 70 - src/core/lib/support/tmpfile_posix.cc | 70 + src/core/lib/support/tmpfile_windows.c | 69 - src/core/lib/support/tmpfile_windows.cc | 69 + src/core/lib/support/wrap_memcpy.c | 40 - src/core/lib/support/wrap_memcpy.cc | 40 + src/core/lib/surface/alarm.c | 139 - src/core/lib/surface/alarm.cc | 139 + src/core/lib/surface/api_trace.c | 22 - src/core/lib/surface/api_trace.cc | 22 + src/core/lib/surface/byte_buffer.c | 90 - src/core/lib/surface/byte_buffer.cc | 90 + src/core/lib/surface/byte_buffer_reader.c | 125 - src/core/lib/surface/byte_buffer_reader.cc | 125 + src/core/lib/surface/call.c | 2160 ------------- src/core/lib/surface/call.cc | 2160 +++++++++++++ src/core/lib/surface/call_details.c | 41 - src/core/lib/surface/call_details.cc | 41 + src/core/lib/surface/call_log_batch.c | 116 - src/core/lib/surface/call_log_batch.cc | 116 + src/core/lib/surface/channel.c | 454 --- src/core/lib/surface/channel.cc | 454 +++ src/core/lib/surface/channel_init.c | 108 - src/core/lib/surface/channel_init.cc | 108 + src/core/lib/surface/channel_ping.c | 65 - src/core/lib/surface/channel_ping.cc | 65 + src/core/lib/surface/channel_stack_type.c | 57 - src/core/lib/surface/channel_stack_type.cc | 57 + src/core/lib/surface/completion_queue.c | 1249 -------- src/core/lib/surface/completion_queue.cc | 1249 ++++++++ src/core/lib/surface/completion_queue_factory.c | 77 - src/core/lib/surface/completion_queue_factory.cc | 77 + src/core/lib/surface/event_string.c | 66 - src/core/lib/surface/event_string.cc | 66 + src/core/lib/surface/init.c | 209 -- src/core/lib/surface/init.cc | 209 ++ src/core/lib/surface/init_secure.c | 91 - src/core/lib/surface/init_secure.cc | 91 + src/core/lib/surface/init_unsecure.c | 25 - src/core/lib/surface/init_unsecure.cc | 25 + src/core/lib/surface/metadata_array.c | 34 - src/core/lib/surface/metadata_array.cc | 34 + src/core/lib/surface/server.c | 1554 ---------- src/core/lib/surface/server.cc | 1554 ++++++++++ src/core/lib/surface/validate_metadata.c | 94 - src/core/lib/surface/validate_metadata.cc | 94 + src/core/lib/transport/bdp_estimator.c | 110 - src/core/lib/transport/bdp_estimator.cc | 110 + src/core/lib/transport/byte_stream.c | 187 -- src/core/lib/transport/byte_stream.cc | 187 ++ src/core/lib/transport/connectivity_state.c | 206 -- src/core/lib/transport/connectivity_state.cc | 206 ++ src/core/lib/transport/error_utils.c | 109 - src/core/lib/transport/error_utils.cc | 109 + src/core/lib/transport/metadata.c | 534 ---- src/core/lib/transport/metadata.cc | 534 ++++ src/core/lib/transport/metadata_batch.c | 315 -- src/core/lib/transport/metadata_batch.cc | 315 ++ src/core/lib/transport/pid_controller.c | 63 - src/core/lib/transport/pid_controller.cc | 63 + src/core/lib/transport/service_config.c | 246 -- src/core/lib/transport/service_config.cc | 246 ++ src/core/lib/transport/static_metadata.c | 582 ---- src/core/lib/transport/static_metadata.cc | 582 ++++ src/core/lib/transport/status_conversion.c | 98 - src/core/lib/transport/status_conversion.cc | 98 + src/core/lib/transport/timeout_encoding.c | 175 -- src/core/lib/transport/timeout_encoding.cc | 175 ++ src/core/lib/transport/transport.c | 289 -- src/core/lib/transport/transport.cc | 289 ++ src/core/lib/transport/transport_op_string.c | 206 -- src/core/lib/transport/transport_op_string.cc | 206 ++ src/core/tsi/fake_transport_security.c | 770 ----- src/core/tsi/fake_transport_security.cc | 770 +++++ src/core/tsi/gts_transport_security.c | 40 - src/core/tsi/gts_transport_security.cc | 40 + src/core/tsi/ssl_transport_security.c | 1604 ---------- src/core/tsi/ssl_transport_security.cc | 1604 ++++++++++ src/core/tsi/transport_security.c | 318 -- src/core/tsi/transport_security.cc | 318 ++ src/core/tsi/transport_security_adapter.c | 226 -- src/core/tsi/transport_security_adapter.cc | 226 ++ src/core/tsi/transport_security_grpc.c | 71 - src/core/tsi/transport_security_grpc.cc | 71 + src/python/grpcio/grpc_core_dependencies.py | 602 ++-- tools/doxygen/Doxyfile.core.internal | 602 ++-- tools/run_tests/generated/sources_and_headers.json | 656 ++-- 633 files changed, 92197 insertions(+), 92246 deletions(-) delete mode 100644 src/core/ext/census/base_resources.c create mode 100644 src/core/ext/census/base_resources.cc delete mode 100644 src/core/ext/census/census_init.c create mode 100644 src/core/ext/census/census_init.cc delete mode 100644 src/core/ext/census/census_log.c create mode 100644 src/core/ext/census/census_log.cc delete mode 100644 src/core/ext/census/census_rpc_stats.c create mode 100644 src/core/ext/census/census_rpc_stats.cc delete mode 100644 src/core/ext/census/census_tracing.c create mode 100644 src/core/ext/census/census_tracing.cc delete mode 100644 src/core/ext/census/context.c create mode 100644 src/core/ext/census/context.cc delete mode 100644 src/core/ext/census/gen/census.pb.c create mode 100644 src/core/ext/census/gen/census.pb.cc delete mode 100644 src/core/ext/census/gen/trace_context.pb.c create mode 100644 src/core/ext/census/gen/trace_context.pb.cc delete mode 100644 src/core/ext/census/grpc_context.c create mode 100644 src/core/ext/census/grpc_context.cc delete mode 100644 src/core/ext/census/grpc_filter.c create mode 100644 src/core/ext/census/grpc_filter.cc delete mode 100644 src/core/ext/census/grpc_plugin.c create mode 100644 src/core/ext/census/grpc_plugin.cc delete mode 100644 src/core/ext/census/hash_table.c create mode 100644 src/core/ext/census/hash_table.cc delete mode 100644 src/core/ext/census/initialize.c create mode 100644 src/core/ext/census/initialize.cc delete mode 100644 src/core/ext/census/intrusive_hash_map.c create mode 100644 src/core/ext/census/intrusive_hash_map.cc delete mode 100644 src/core/ext/census/mlog.c create mode 100644 src/core/ext/census/mlog.cc delete mode 100644 src/core/ext/census/operation.c create mode 100644 src/core/ext/census/operation.cc delete mode 100644 src/core/ext/census/placeholders.c create mode 100644 src/core/ext/census/placeholders.cc delete mode 100644 src/core/ext/census/resource.c create mode 100644 src/core/ext/census/resource.cc delete mode 100644 src/core/ext/census/trace_context.c create mode 100644 src/core/ext/census/trace_context.cc delete mode 100644 src/core/ext/census/tracing.c create mode 100644 src/core/ext/census/tracing.cc delete mode 100644 src/core/ext/census/window_stats.c create mode 100644 src/core/ext/census/window_stats.cc delete mode 100644 src/core/ext/filters/client_channel/channel_connectivity.c create mode 100644 src/core/ext/filters/client_channel/channel_connectivity.cc delete mode 100644 src/core/ext/filters/client_channel/client_channel.c create mode 100644 src/core/ext/filters/client_channel/client_channel.cc delete mode 100644 src/core/ext/filters/client_channel/client_channel_factory.c create mode 100644 src/core/ext/filters/client_channel/client_channel_factory.cc delete mode 100644 src/core/ext/filters/client_channel/client_channel_plugin.c create mode 100644 src/core/ext/filters/client_channel/client_channel_plugin.cc delete mode 100644 src/core/ext/filters/client_channel/connector.c create mode 100644 src/core/ext/filters/client_channel/connector.cc delete mode 100644 src/core/ext/filters/client_channel/http_connect_handshaker.c create mode 100644 src/core/ext/filters/client_channel/http_connect_handshaker.cc delete mode 100644 src/core/ext/filters/client_channel/http_proxy.c create mode 100644 src/core/ext/filters/client_channel/http_proxy.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy.c create mode 100644 src/core/ext/filters/client_channel/lb_policy.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c create mode 100644 src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy_factory.c create mode 100644 src/core/ext/filters/client_channel/lb_policy_factory.cc delete mode 100644 src/core/ext/filters/client_channel/lb_policy_registry.c create mode 100644 src/core/ext/filters/client_channel/lb_policy_registry.cc delete mode 100644 src/core/ext/filters/client_channel/parse_address.c create mode 100644 src/core/ext/filters/client_channel/parse_address.cc delete mode 100644 src/core/ext/filters/client_channel/proxy_mapper.c create mode 100644 src/core/ext/filters/client_channel/proxy_mapper.cc delete mode 100644 src/core/ext/filters/client_channel/proxy_mapper_registry.c create mode 100644 src/core/ext/filters/client_channel/proxy_mapper_registry.cc delete mode 100644 src/core/ext/filters/client_channel/resolver.c create mode 100644 src/core/ext/filters/client_channel/resolver.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c create mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c create mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c create mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c create mode 100644 src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c create mode 100644 src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c create mode 100644 src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc delete mode 100644 src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c create mode 100644 src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc delete mode 100644 src/core/ext/filters/client_channel/resolver_factory.c create mode 100644 src/core/ext/filters/client_channel/resolver_factory.cc delete mode 100644 src/core/ext/filters/client_channel/resolver_registry.c create mode 100644 src/core/ext/filters/client_channel/resolver_registry.cc delete mode 100644 src/core/ext/filters/client_channel/retry_throttle.c create mode 100644 src/core/ext/filters/client_channel/retry_throttle.cc delete mode 100644 src/core/ext/filters/client_channel/subchannel.c create mode 100644 src/core/ext/filters/client_channel/subchannel.cc delete mode 100644 src/core/ext/filters/client_channel/subchannel_index.c create mode 100644 src/core/ext/filters/client_channel/subchannel_index.cc delete mode 100644 src/core/ext/filters/client_channel/uri_parser.c create mode 100644 src/core/ext/filters/client_channel/uri_parser.cc delete mode 100644 src/core/ext/filters/deadline/deadline_filter.c create mode 100644 src/core/ext/filters/deadline/deadline_filter.cc delete mode 100644 src/core/ext/filters/http/client/http_client_filter.c create mode 100644 src/core/ext/filters/http/client/http_client_filter.cc delete mode 100644 src/core/ext/filters/http/http_filters_plugin.c create mode 100644 src/core/ext/filters/http/http_filters_plugin.cc delete mode 100644 src/core/ext/filters/http/message_compress/message_compress_filter.c create mode 100644 src/core/ext/filters/http/message_compress/message_compress_filter.cc delete mode 100644 src/core/ext/filters/http/server/http_server_filter.c create mode 100644 src/core/ext/filters/http/server/http_server_filter.cc delete mode 100644 src/core/ext/filters/load_reporting/server_load_reporting_filter.c create mode 100644 src/core/ext/filters/load_reporting/server_load_reporting_filter.cc delete mode 100644 src/core/ext/filters/load_reporting/server_load_reporting_plugin.c create mode 100644 src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc delete mode 100644 src/core/ext/filters/max_age/max_age_filter.c create mode 100644 src/core/ext/filters/max_age/max_age_filter.cc delete mode 100644 src/core/ext/filters/message_size/message_size_filter.c create mode 100644 src/core/ext/filters/message_size/message_size_filter.cc delete mode 100644 src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c create mode 100644 src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc delete mode 100644 src/core/ext/filters/workarounds/workaround_utils.c create mode 100644 src/core/ext/filters/workarounds/workaround_utils.cc delete mode 100644 src/core/ext/transport/chttp2/alpn/alpn.c create mode 100644 src/core/ext/transport/chttp2/alpn/alpn.cc delete mode 100644 src/core/ext/transport/chttp2/client/chttp2_connector.c create mode 100644 src/core/ext/transport/chttp2/client/chttp2_connector.cc delete mode 100644 src/core/ext/transport/chttp2/client/insecure/channel_create.c create mode 100644 src/core/ext/transport/chttp2/client/insecure/channel_create.cc delete mode 100644 src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c create mode 100644 src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc delete mode 100644 src/core/ext/transport/chttp2/client/secure/secure_channel_create.c create mode 100644 src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc delete mode 100644 src/core/ext/transport/chttp2/server/chttp2_server.c create mode 100644 src/core/ext/transport/chttp2/server/chttp2_server.cc delete mode 100644 src/core/ext/transport/chttp2/server/insecure/server_chttp2.c create mode 100644 src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc delete mode 100644 src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c create mode 100644 src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc delete mode 100644 src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c create mode 100644 src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc delete mode 100644 src/core/ext/transport/chttp2/transport/bin_decoder.c create mode 100644 src/core/ext/transport/chttp2/transport/bin_decoder.cc delete mode 100644 src/core/ext/transport/chttp2/transport/bin_encoder.c create mode 100644 src/core/ext/transport/chttp2/transport/bin_encoder.cc delete mode 100644 src/core/ext/transport/chttp2/transport/chttp2_plugin.c create mode 100644 src/core/ext/transport/chttp2/transport/chttp2_plugin.cc delete mode 100644 src/core/ext/transport/chttp2/transport/chttp2_transport.c create mode 100644 src/core/ext/transport/chttp2/transport/chttp2_transport.cc delete mode 100644 src/core/ext/transport/chttp2/transport/flow_control.c create mode 100644 src/core/ext/transport/chttp2/transport/flow_control.cc delete mode 100644 src/core/ext/transport/chttp2/transport/frame_data.c create mode 100644 src/core/ext/transport/chttp2/transport/frame_data.cc delete mode 100644 src/core/ext/transport/chttp2/transport/frame_goaway.c create mode 100644 src/core/ext/transport/chttp2/transport/frame_goaway.cc delete mode 100644 src/core/ext/transport/chttp2/transport/frame_ping.c create mode 100644 src/core/ext/transport/chttp2/transport/frame_ping.cc delete mode 100644 src/core/ext/transport/chttp2/transport/frame_rst_stream.c create mode 100644 src/core/ext/transport/chttp2/transport/frame_rst_stream.cc delete mode 100644 src/core/ext/transport/chttp2/transport/frame_settings.c create mode 100644 src/core/ext/transport/chttp2/transport/frame_settings.cc delete mode 100644 src/core/ext/transport/chttp2/transport/frame_window_update.c create mode 100644 src/core/ext/transport/chttp2/transport/frame_window_update.cc delete mode 100644 src/core/ext/transport/chttp2/transport/hpack_encoder.c create mode 100644 src/core/ext/transport/chttp2/transport/hpack_encoder.cc delete mode 100644 src/core/ext/transport/chttp2/transport/hpack_parser.c create mode 100644 src/core/ext/transport/chttp2/transport/hpack_parser.cc delete mode 100644 src/core/ext/transport/chttp2/transport/hpack_table.c create mode 100644 src/core/ext/transport/chttp2/transport/hpack_table.cc delete mode 100644 src/core/ext/transport/chttp2/transport/http2_settings.c create mode 100644 src/core/ext/transport/chttp2/transport/http2_settings.cc delete mode 100644 src/core/ext/transport/chttp2/transport/huffsyms.c create mode 100644 src/core/ext/transport/chttp2/transport/huffsyms.cc delete mode 100644 src/core/ext/transport/chttp2/transport/incoming_metadata.c create mode 100644 src/core/ext/transport/chttp2/transport/incoming_metadata.cc delete mode 100644 src/core/ext/transport/chttp2/transport/parsing.c create mode 100644 src/core/ext/transport/chttp2/transport/parsing.cc delete mode 100644 src/core/ext/transport/chttp2/transport/stream_lists.c create mode 100644 src/core/ext/transport/chttp2/transport/stream_lists.cc delete mode 100644 src/core/ext/transport/chttp2/transport/stream_map.c create mode 100644 src/core/ext/transport/chttp2/transport/stream_map.cc delete mode 100644 src/core/ext/transport/chttp2/transport/varint.c create mode 100644 src/core/ext/transport/chttp2/transport/varint.cc delete mode 100644 src/core/ext/transport/chttp2/transport/writing.c create mode 100644 src/core/ext/transport/chttp2/transport/writing.cc delete mode 100644 src/core/ext/transport/cronet/client/secure/cronet_channel_create.c create mode 100644 src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc delete mode 100644 src/core/ext/transport/cronet/transport/cronet_api_dummy.c create mode 100644 src/core/ext/transport/cronet/transport/cronet_api_dummy.cc delete mode 100644 src/core/ext/transport/cronet/transport/cronet_transport.c create mode 100644 src/core/ext/transport/cronet/transport/cronet_transport.cc delete mode 100644 src/core/ext/transport/inproc/inproc_plugin.c create mode 100644 src/core/ext/transport/inproc/inproc_plugin.cc delete mode 100644 src/core/ext/transport/inproc/inproc_transport.c create mode 100644 src/core/ext/transport/inproc/inproc_transport.cc delete mode 100644 src/core/lib/channel/channel_args.c create mode 100644 src/core/lib/channel/channel_args.cc delete mode 100644 src/core/lib/channel/channel_stack.c create mode 100644 src/core/lib/channel/channel_stack.cc delete mode 100644 src/core/lib/channel/channel_stack_builder.c create mode 100644 src/core/lib/channel/channel_stack_builder.cc delete mode 100644 src/core/lib/channel/connected_channel.c create mode 100644 src/core/lib/channel/connected_channel.cc delete mode 100644 src/core/lib/channel/handshaker.c create mode 100644 src/core/lib/channel/handshaker.cc delete mode 100644 src/core/lib/channel/handshaker_factory.c create mode 100644 src/core/lib/channel/handshaker_factory.cc delete mode 100644 src/core/lib/channel/handshaker_registry.c create mode 100644 src/core/lib/channel/handshaker_registry.cc delete mode 100644 src/core/lib/compression/compression.c create mode 100644 src/core/lib/compression/compression.cc delete mode 100644 src/core/lib/compression/message_compress.c create mode 100644 src/core/lib/compression/message_compress.cc delete mode 100644 src/core/lib/compression/stream_compression.c create mode 100644 src/core/lib/compression/stream_compression.cc delete mode 100644 src/core/lib/debug/stats.c create mode 100644 src/core/lib/debug/stats.cc delete mode 100644 src/core/lib/debug/stats_data.c create mode 100644 src/core/lib/debug/stats_data.cc delete mode 100644 src/core/lib/debug/trace.c create mode 100644 src/core/lib/debug/trace.cc delete mode 100644 src/core/lib/http/format_request.c create mode 100644 src/core/lib/http/format_request.cc delete mode 100644 src/core/lib/http/httpcli.c create mode 100644 src/core/lib/http/httpcli.cc delete mode 100644 src/core/lib/http/httpcli_security_connector.c create mode 100644 src/core/lib/http/httpcli_security_connector.cc delete mode 100644 src/core/lib/http/parser.c create mode 100644 src/core/lib/http/parser.cc delete mode 100644 src/core/lib/iomgr/call_combiner.c create mode 100644 src/core/lib/iomgr/call_combiner.cc delete mode 100644 src/core/lib/iomgr/closure.c create mode 100644 src/core/lib/iomgr/closure.cc delete mode 100644 src/core/lib/iomgr/combiner.c create mode 100644 src/core/lib/iomgr/combiner.cc delete mode 100644 src/core/lib/iomgr/endpoint.c create mode 100644 src/core/lib/iomgr/endpoint.cc delete mode 100644 src/core/lib/iomgr/endpoint_pair_posix.c create mode 100644 src/core/lib/iomgr/endpoint_pair_posix.cc delete mode 100644 src/core/lib/iomgr/endpoint_pair_uv.c create mode 100644 src/core/lib/iomgr/endpoint_pair_uv.cc delete mode 100644 src/core/lib/iomgr/endpoint_pair_windows.c create mode 100644 src/core/lib/iomgr/endpoint_pair_windows.cc delete mode 100644 src/core/lib/iomgr/error.c create mode 100644 src/core/lib/iomgr/error.cc delete mode 100644 src/core/lib/iomgr/ev_epoll1_linux.c create mode 100644 src/core/lib/iomgr/ev_epoll1_linux.cc delete mode 100644 src/core/lib/iomgr/ev_epollex_linux.c create mode 100644 src/core/lib/iomgr/ev_epollex_linux.cc delete mode 100644 src/core/lib/iomgr/ev_epollsig_linux.c create mode 100644 src/core/lib/iomgr/ev_epollsig_linux.cc delete mode 100644 src/core/lib/iomgr/ev_poll_posix.c create mode 100644 src/core/lib/iomgr/ev_poll_posix.cc delete mode 100644 src/core/lib/iomgr/ev_posix.c create mode 100644 src/core/lib/iomgr/ev_posix.cc delete mode 100644 src/core/lib/iomgr/ev_windows.c create mode 100644 src/core/lib/iomgr/ev_windows.cc delete mode 100644 src/core/lib/iomgr/exec_ctx.c create mode 100644 src/core/lib/iomgr/exec_ctx.cc delete mode 100644 src/core/lib/iomgr/executor.c create mode 100644 src/core/lib/iomgr/executor.cc delete mode 100644 src/core/lib/iomgr/gethostname_fallback.c create mode 100644 src/core/lib/iomgr/gethostname_fallback.cc delete mode 100644 src/core/lib/iomgr/gethostname_host_name_max.c create mode 100644 src/core/lib/iomgr/gethostname_host_name_max.cc delete mode 100644 src/core/lib/iomgr/gethostname_sysconf.c create mode 100644 src/core/lib/iomgr/gethostname_sysconf.cc delete mode 100644 src/core/lib/iomgr/iocp_windows.c create mode 100644 src/core/lib/iomgr/iocp_windows.cc delete mode 100644 src/core/lib/iomgr/iomgr.c create mode 100644 src/core/lib/iomgr/iomgr.cc delete mode 100644 src/core/lib/iomgr/iomgr_posix.c create mode 100644 src/core/lib/iomgr/iomgr_posix.cc delete mode 100644 src/core/lib/iomgr/iomgr_uv.c create mode 100644 src/core/lib/iomgr/iomgr_uv.cc delete mode 100644 src/core/lib/iomgr/iomgr_windows.c create mode 100644 src/core/lib/iomgr/iomgr_windows.cc delete mode 100644 src/core/lib/iomgr/is_epollexclusive_available.c create mode 100644 src/core/lib/iomgr/is_epollexclusive_available.cc delete mode 100644 src/core/lib/iomgr/load_file.c create mode 100644 src/core/lib/iomgr/load_file.cc delete mode 100644 src/core/lib/iomgr/lockfree_event.c create mode 100644 src/core/lib/iomgr/lockfree_event.cc delete mode 100644 src/core/lib/iomgr/network_status_tracker.c create mode 100644 src/core/lib/iomgr/network_status_tracker.cc delete mode 100644 src/core/lib/iomgr/polling_entity.c create mode 100644 src/core/lib/iomgr/polling_entity.cc delete mode 100644 src/core/lib/iomgr/pollset_set_uv.c create mode 100644 src/core/lib/iomgr/pollset_set_uv.cc delete mode 100644 src/core/lib/iomgr/pollset_set_windows.c create mode 100644 src/core/lib/iomgr/pollset_set_windows.cc delete mode 100644 src/core/lib/iomgr/pollset_uv.c create mode 100644 src/core/lib/iomgr/pollset_uv.cc delete mode 100644 src/core/lib/iomgr/pollset_windows.c create mode 100644 src/core/lib/iomgr/pollset_windows.cc delete mode 100644 src/core/lib/iomgr/resolve_address_posix.c create mode 100644 src/core/lib/iomgr/resolve_address_posix.cc delete mode 100644 src/core/lib/iomgr/resolve_address_uv.c create mode 100644 src/core/lib/iomgr/resolve_address_uv.cc delete mode 100644 src/core/lib/iomgr/resolve_address_windows.c create mode 100644 src/core/lib/iomgr/resolve_address_windows.cc delete mode 100644 src/core/lib/iomgr/resource_quota.c create mode 100644 src/core/lib/iomgr/resource_quota.cc delete mode 100644 src/core/lib/iomgr/sockaddr_utils.c create mode 100644 src/core/lib/iomgr/sockaddr_utils.cc delete mode 100644 src/core/lib/iomgr/socket_factory_posix.c create mode 100644 src/core/lib/iomgr/socket_factory_posix.cc delete mode 100644 src/core/lib/iomgr/socket_mutator.c create mode 100644 src/core/lib/iomgr/socket_mutator.cc delete mode 100644 src/core/lib/iomgr/socket_utils_common_posix.c create mode 100644 src/core/lib/iomgr/socket_utils_common_posix.cc delete mode 100644 src/core/lib/iomgr/socket_utils_linux.c create mode 100644 src/core/lib/iomgr/socket_utils_linux.cc delete mode 100644 src/core/lib/iomgr/socket_utils_posix.c create mode 100644 src/core/lib/iomgr/socket_utils_posix.cc delete mode 100644 src/core/lib/iomgr/socket_utils_uv.c create mode 100644 src/core/lib/iomgr/socket_utils_uv.cc delete mode 100644 src/core/lib/iomgr/socket_utils_windows.c create mode 100644 src/core/lib/iomgr/socket_utils_windows.cc delete mode 100644 src/core/lib/iomgr/socket_windows.c create mode 100644 src/core/lib/iomgr/socket_windows.cc delete mode 100644 src/core/lib/iomgr/tcp_client_posix.c create mode 100644 src/core/lib/iomgr/tcp_client_posix.cc delete mode 100644 src/core/lib/iomgr/tcp_client_uv.c create mode 100644 src/core/lib/iomgr/tcp_client_uv.cc delete mode 100644 src/core/lib/iomgr/tcp_client_windows.c create mode 100644 src/core/lib/iomgr/tcp_client_windows.cc delete mode 100644 src/core/lib/iomgr/tcp_posix.c create mode 100644 src/core/lib/iomgr/tcp_posix.cc delete mode 100644 src/core/lib/iomgr/tcp_server_posix.c create mode 100644 src/core/lib/iomgr/tcp_server_posix.cc delete mode 100644 src/core/lib/iomgr/tcp_server_utils_posix_common.c create mode 100644 src/core/lib/iomgr/tcp_server_utils_posix_common.cc delete mode 100644 src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c create mode 100644 src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc delete mode 100644 src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c create mode 100644 src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc delete mode 100644 src/core/lib/iomgr/tcp_server_uv.c create mode 100644 src/core/lib/iomgr/tcp_server_uv.cc delete mode 100644 src/core/lib/iomgr/tcp_server_windows.c create mode 100644 src/core/lib/iomgr/tcp_server_windows.cc delete mode 100644 src/core/lib/iomgr/tcp_uv.c create mode 100644 src/core/lib/iomgr/tcp_uv.cc delete mode 100644 src/core/lib/iomgr/tcp_windows.c create mode 100644 src/core/lib/iomgr/tcp_windows.cc delete mode 100644 src/core/lib/iomgr/time_averaged_stats.c create mode 100644 src/core/lib/iomgr/time_averaged_stats.cc delete mode 100644 src/core/lib/iomgr/timer_generic.c create mode 100644 src/core/lib/iomgr/timer_generic.cc delete mode 100644 src/core/lib/iomgr/timer_heap.c create mode 100644 src/core/lib/iomgr/timer_heap.cc delete mode 100644 src/core/lib/iomgr/timer_manager.c create mode 100644 src/core/lib/iomgr/timer_manager.cc delete mode 100644 src/core/lib/iomgr/timer_uv.c create mode 100644 src/core/lib/iomgr/timer_uv.cc delete mode 100644 src/core/lib/iomgr/udp_server.c create mode 100644 src/core/lib/iomgr/udp_server.cc delete mode 100644 src/core/lib/iomgr/unix_sockets_posix.c create mode 100644 src/core/lib/iomgr/unix_sockets_posix.cc delete mode 100644 src/core/lib/iomgr/unix_sockets_posix_noop.c create mode 100644 src/core/lib/iomgr/unix_sockets_posix_noop.cc delete mode 100644 src/core/lib/iomgr/wakeup_fd_cv.c create mode 100644 src/core/lib/iomgr/wakeup_fd_cv.cc delete mode 100644 src/core/lib/iomgr/wakeup_fd_eventfd.c create mode 100644 src/core/lib/iomgr/wakeup_fd_eventfd.cc delete mode 100644 src/core/lib/iomgr/wakeup_fd_nospecial.c create mode 100644 src/core/lib/iomgr/wakeup_fd_nospecial.cc delete mode 100644 src/core/lib/iomgr/wakeup_fd_pipe.c create mode 100644 src/core/lib/iomgr/wakeup_fd_pipe.cc delete mode 100644 src/core/lib/iomgr/wakeup_fd_posix.c create mode 100644 src/core/lib/iomgr/wakeup_fd_posix.cc delete mode 100644 src/core/lib/json/json.c create mode 100644 src/core/lib/json/json.cc delete mode 100644 src/core/lib/json/json_reader.c create mode 100644 src/core/lib/json/json_reader.cc delete mode 100644 src/core/lib/json/json_string.c create mode 100644 src/core/lib/json/json_string.cc delete mode 100644 src/core/lib/json/json_writer.c create mode 100644 src/core/lib/json/json_writer.cc delete mode 100644 src/core/lib/profiling/basic_timers.c create mode 100644 src/core/lib/profiling/basic_timers.cc delete mode 100644 src/core/lib/profiling/stap_timers.c create mode 100644 src/core/lib/profiling/stap_timers.cc delete mode 100644 src/core/lib/security/context/security_context.c create mode 100644 src/core/lib/security/context/security_context.cc delete mode 100644 src/core/lib/security/credentials/composite/composite_credentials.c create mode 100644 src/core/lib/security/credentials/composite/composite_credentials.cc delete mode 100644 src/core/lib/security/credentials/credentials.c create mode 100644 src/core/lib/security/credentials/credentials.cc delete mode 100644 src/core/lib/security/credentials/credentials_metadata.c create mode 100644 src/core/lib/security/credentials/credentials_metadata.cc delete mode 100644 src/core/lib/security/credentials/fake/fake_credentials.c create mode 100644 src/core/lib/security/credentials/fake/fake_credentials.cc delete mode 100644 src/core/lib/security/credentials/google_default/credentials_generic.c create mode 100644 src/core/lib/security/credentials/google_default/credentials_generic.cc delete mode 100644 src/core/lib/security/credentials/google_default/google_default_credentials.c create mode 100644 src/core/lib/security/credentials/google_default/google_default_credentials.cc delete mode 100644 src/core/lib/security/credentials/iam/iam_credentials.c create mode 100644 src/core/lib/security/credentials/iam/iam_credentials.cc delete mode 100644 src/core/lib/security/credentials/jwt/json_token.c create mode 100644 src/core/lib/security/credentials/jwt/json_token.cc delete mode 100644 src/core/lib/security/credentials/jwt/jwt_credentials.c create mode 100644 src/core/lib/security/credentials/jwt/jwt_credentials.cc delete mode 100644 src/core/lib/security/credentials/jwt/jwt_verifier.c create mode 100644 src/core/lib/security/credentials/jwt/jwt_verifier.cc delete mode 100644 src/core/lib/security/credentials/oauth2/oauth2_credentials.c create mode 100644 src/core/lib/security/credentials/oauth2/oauth2_credentials.cc delete mode 100644 src/core/lib/security/credentials/plugin/plugin_credentials.c create mode 100644 src/core/lib/security/credentials/plugin/plugin_credentials.cc delete mode 100644 src/core/lib/security/credentials/ssl/ssl_credentials.c create mode 100644 src/core/lib/security/credentials/ssl/ssl_credentials.cc delete mode 100644 src/core/lib/security/transport/client_auth_filter.c create mode 100644 src/core/lib/security/transport/client_auth_filter.cc delete mode 100644 src/core/lib/security/transport/lb_targets_info.c create mode 100644 src/core/lib/security/transport/lb_targets_info.cc delete mode 100644 src/core/lib/security/transport/secure_endpoint.c create mode 100644 src/core/lib/security/transport/secure_endpoint.cc delete mode 100644 src/core/lib/security/transport/security_connector.c create mode 100644 src/core/lib/security/transport/security_connector.cc delete mode 100644 src/core/lib/security/transport/security_handshaker.c create mode 100644 src/core/lib/security/transport/security_handshaker.cc delete mode 100644 src/core/lib/security/transport/server_auth_filter.c create mode 100644 src/core/lib/security/transport/server_auth_filter.cc delete mode 100644 src/core/lib/security/transport/tsi_error.c create mode 100644 src/core/lib/security/transport/tsi_error.cc delete mode 100644 src/core/lib/security/util/json_util.c create mode 100644 src/core/lib/security/util/json_util.cc delete mode 100644 src/core/lib/slice/b64.c create mode 100644 src/core/lib/slice/b64.cc delete mode 100644 src/core/lib/slice/percent_encoding.c create mode 100644 src/core/lib/slice/percent_encoding.cc delete mode 100644 src/core/lib/slice/slice.c create mode 100644 src/core/lib/slice/slice.cc delete mode 100644 src/core/lib/slice/slice_buffer.c create mode 100644 src/core/lib/slice/slice_buffer.cc delete mode 100644 src/core/lib/slice/slice_hash_table.c create mode 100644 src/core/lib/slice/slice_hash_table.cc delete mode 100644 src/core/lib/slice/slice_intern.c create mode 100644 src/core/lib/slice/slice_intern.cc delete mode 100644 src/core/lib/slice/slice_string_helpers.c create mode 100644 src/core/lib/slice/slice_string_helpers.cc delete mode 100644 src/core/lib/support/alloc.c create mode 100644 src/core/lib/support/alloc.cc delete mode 100644 src/core/lib/support/arena.c create mode 100644 src/core/lib/support/arena.cc delete mode 100644 src/core/lib/support/atm.c create mode 100644 src/core/lib/support/atm.cc delete mode 100644 src/core/lib/support/avl.c create mode 100644 src/core/lib/support/avl.cc delete mode 100644 src/core/lib/support/backoff.c create mode 100644 src/core/lib/support/backoff.cc delete mode 100644 src/core/lib/support/cmdline.c create mode 100644 src/core/lib/support/cmdline.cc delete mode 100644 src/core/lib/support/cpu_iphone.c create mode 100644 src/core/lib/support/cpu_iphone.cc delete mode 100644 src/core/lib/support/cpu_linux.c create mode 100644 src/core/lib/support/cpu_linux.cc delete mode 100644 src/core/lib/support/cpu_posix.c create mode 100644 src/core/lib/support/cpu_posix.cc delete mode 100644 src/core/lib/support/cpu_windows.c create mode 100644 src/core/lib/support/cpu_windows.cc delete mode 100644 src/core/lib/support/env_linux.c create mode 100644 src/core/lib/support/env_linux.cc delete mode 100644 src/core/lib/support/env_posix.c create mode 100644 src/core/lib/support/env_posix.cc delete mode 100644 src/core/lib/support/env_windows.c create mode 100644 src/core/lib/support/env_windows.cc delete mode 100644 src/core/lib/support/histogram.c create mode 100644 src/core/lib/support/histogram.cc delete mode 100644 src/core/lib/support/host_port.c create mode 100644 src/core/lib/support/host_port.cc delete mode 100644 src/core/lib/support/log.c create mode 100644 src/core/lib/support/log.cc delete mode 100644 src/core/lib/support/log_android.c create mode 100644 src/core/lib/support/log_android.cc delete mode 100644 src/core/lib/support/log_linux.c create mode 100644 src/core/lib/support/log_linux.cc delete mode 100644 src/core/lib/support/log_posix.c create mode 100644 src/core/lib/support/log_posix.cc delete mode 100644 src/core/lib/support/log_windows.c create mode 100644 src/core/lib/support/log_windows.cc delete mode 100644 src/core/lib/support/mpscq.c create mode 100644 src/core/lib/support/mpscq.cc delete mode 100644 src/core/lib/support/murmur_hash.c create mode 100644 src/core/lib/support/murmur_hash.cc delete mode 100644 src/core/lib/support/stack_lockfree.c create mode 100644 src/core/lib/support/stack_lockfree.cc delete mode 100644 src/core/lib/support/string.c create mode 100644 src/core/lib/support/string.cc delete mode 100644 src/core/lib/support/string_posix.c create mode 100644 src/core/lib/support/string_posix.cc delete mode 100644 src/core/lib/support/string_util_windows.c create mode 100644 src/core/lib/support/string_util_windows.cc delete mode 100644 src/core/lib/support/string_windows.c create mode 100644 src/core/lib/support/string_windows.cc delete mode 100644 src/core/lib/support/subprocess_posix.c create mode 100644 src/core/lib/support/subprocess_posix.cc delete mode 100644 src/core/lib/support/subprocess_windows.c create mode 100644 src/core/lib/support/subprocess_windows.cc delete mode 100644 src/core/lib/support/sync.c create mode 100644 src/core/lib/support/sync.cc delete mode 100644 src/core/lib/support/sync_posix.c create mode 100644 src/core/lib/support/sync_posix.cc delete mode 100644 src/core/lib/support/sync_windows.c create mode 100644 src/core/lib/support/sync_windows.cc delete mode 100644 src/core/lib/support/thd.c create mode 100644 src/core/lib/support/thd.cc delete mode 100644 src/core/lib/support/thd_posix.c create mode 100644 src/core/lib/support/thd_posix.cc delete mode 100644 src/core/lib/support/thd_windows.c create mode 100644 src/core/lib/support/thd_windows.cc delete mode 100644 src/core/lib/support/time.c create mode 100644 src/core/lib/support/time.cc delete mode 100644 src/core/lib/support/time_posix.c create mode 100644 src/core/lib/support/time_posix.cc delete mode 100644 src/core/lib/support/time_precise.c create mode 100644 src/core/lib/support/time_precise.cc delete mode 100644 src/core/lib/support/time_windows.c create mode 100644 src/core/lib/support/time_windows.cc delete mode 100644 src/core/lib/support/tls_pthread.c create mode 100644 src/core/lib/support/tls_pthread.cc delete mode 100644 src/core/lib/support/tmpfile_msys.c create mode 100644 src/core/lib/support/tmpfile_msys.cc delete mode 100644 src/core/lib/support/tmpfile_posix.c create mode 100644 src/core/lib/support/tmpfile_posix.cc delete mode 100644 src/core/lib/support/tmpfile_windows.c create mode 100644 src/core/lib/support/tmpfile_windows.cc delete mode 100644 src/core/lib/support/wrap_memcpy.c create mode 100644 src/core/lib/support/wrap_memcpy.cc delete mode 100644 src/core/lib/surface/alarm.c create mode 100644 src/core/lib/surface/alarm.cc delete mode 100644 src/core/lib/surface/api_trace.c create mode 100644 src/core/lib/surface/api_trace.cc delete mode 100644 src/core/lib/surface/byte_buffer.c create mode 100644 src/core/lib/surface/byte_buffer.cc delete mode 100644 src/core/lib/surface/byte_buffer_reader.c create mode 100644 src/core/lib/surface/byte_buffer_reader.cc delete mode 100644 src/core/lib/surface/call.c create mode 100644 src/core/lib/surface/call.cc delete mode 100644 src/core/lib/surface/call_details.c create mode 100644 src/core/lib/surface/call_details.cc delete mode 100644 src/core/lib/surface/call_log_batch.c create mode 100644 src/core/lib/surface/call_log_batch.cc delete mode 100644 src/core/lib/surface/channel.c create mode 100644 src/core/lib/surface/channel.cc delete mode 100644 src/core/lib/surface/channel_init.c create mode 100644 src/core/lib/surface/channel_init.cc delete mode 100644 src/core/lib/surface/channel_ping.c create mode 100644 src/core/lib/surface/channel_ping.cc delete mode 100644 src/core/lib/surface/channel_stack_type.c create mode 100644 src/core/lib/surface/channel_stack_type.cc delete mode 100644 src/core/lib/surface/completion_queue.c create mode 100644 src/core/lib/surface/completion_queue.cc delete mode 100644 src/core/lib/surface/completion_queue_factory.c create mode 100644 src/core/lib/surface/completion_queue_factory.cc delete mode 100644 src/core/lib/surface/event_string.c create mode 100644 src/core/lib/surface/event_string.cc delete mode 100644 src/core/lib/surface/init.c create mode 100644 src/core/lib/surface/init.cc delete mode 100644 src/core/lib/surface/init_secure.c create mode 100644 src/core/lib/surface/init_secure.cc delete mode 100644 src/core/lib/surface/init_unsecure.c create mode 100644 src/core/lib/surface/init_unsecure.cc delete mode 100644 src/core/lib/surface/metadata_array.c create mode 100644 src/core/lib/surface/metadata_array.cc delete mode 100644 src/core/lib/surface/server.c create mode 100644 src/core/lib/surface/server.cc delete mode 100644 src/core/lib/surface/validate_metadata.c create mode 100644 src/core/lib/surface/validate_metadata.cc delete mode 100644 src/core/lib/transport/bdp_estimator.c create mode 100644 src/core/lib/transport/bdp_estimator.cc delete mode 100644 src/core/lib/transport/byte_stream.c create mode 100644 src/core/lib/transport/byte_stream.cc delete mode 100644 src/core/lib/transport/connectivity_state.c create mode 100644 src/core/lib/transport/connectivity_state.cc delete mode 100644 src/core/lib/transport/error_utils.c create mode 100644 src/core/lib/transport/error_utils.cc delete mode 100644 src/core/lib/transport/metadata.c create mode 100644 src/core/lib/transport/metadata.cc delete mode 100644 src/core/lib/transport/metadata_batch.c create mode 100644 src/core/lib/transport/metadata_batch.cc delete mode 100644 src/core/lib/transport/pid_controller.c create mode 100644 src/core/lib/transport/pid_controller.cc delete mode 100644 src/core/lib/transport/service_config.c create mode 100644 src/core/lib/transport/service_config.cc delete mode 100644 src/core/lib/transport/static_metadata.c create mode 100644 src/core/lib/transport/static_metadata.cc delete mode 100644 src/core/lib/transport/status_conversion.c create mode 100644 src/core/lib/transport/status_conversion.cc delete mode 100644 src/core/lib/transport/timeout_encoding.c create mode 100644 src/core/lib/transport/timeout_encoding.cc delete mode 100644 src/core/lib/transport/transport.c create mode 100644 src/core/lib/transport/transport.cc delete mode 100644 src/core/lib/transport/transport_op_string.c create mode 100644 src/core/lib/transport/transport_op_string.cc delete mode 100644 src/core/tsi/fake_transport_security.c create mode 100644 src/core/tsi/fake_transport_security.cc delete mode 100644 src/core/tsi/gts_transport_security.c create mode 100644 src/core/tsi/gts_transport_security.cc delete mode 100644 src/core/tsi/ssl_transport_security.c create mode 100644 src/core/tsi/ssl_transport_security.cc delete mode 100644 src/core/tsi/transport_security.c create mode 100644 src/core/tsi/transport_security.cc delete mode 100644 src/core/tsi/transport_security_adapter.c create mode 100644 src/core/tsi/transport_security_adapter.cc delete mode 100644 src/core/tsi/transport_security_grpc.c create mode 100644 src/core/tsi/transport_security_grpc.cc (limited to 'src/core/lib/support') diff --git a/CMakeLists.txt b/CMakeLists.txt index fe838383f5..bbd8647e25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -780,52 +780,52 @@ endif (gRPC_BUILD_TESTS) add_library(gpr - src/core/lib/profiling/basic_timers.c - src/core/lib/profiling/stap_timers.c - src/core/lib/support/alloc.c - src/core/lib/support/arena.c - src/core/lib/support/atm.c - src/core/lib/support/avl.c - src/core/lib/support/backoff.c - src/core/lib/support/cmdline.c - src/core/lib/support/cpu_iphone.c - src/core/lib/support/cpu_linux.c - src/core/lib/support/cpu_posix.c - src/core/lib/support/cpu_windows.c - src/core/lib/support/env_linux.c - src/core/lib/support/env_posix.c - src/core/lib/support/env_windows.c - src/core/lib/support/histogram.c - src/core/lib/support/host_port.c - src/core/lib/support/log.c - src/core/lib/support/log_android.c - src/core/lib/support/log_linux.c - src/core/lib/support/log_posix.c - src/core/lib/support/log_windows.c - src/core/lib/support/mpscq.c - src/core/lib/support/murmur_hash.c - src/core/lib/support/stack_lockfree.c - src/core/lib/support/string.c - src/core/lib/support/string_posix.c - src/core/lib/support/string_util_windows.c - src/core/lib/support/string_windows.c - src/core/lib/support/subprocess_posix.c - src/core/lib/support/subprocess_windows.c - src/core/lib/support/sync.c - src/core/lib/support/sync_posix.c - src/core/lib/support/sync_windows.c - src/core/lib/support/thd.c - src/core/lib/support/thd_posix.c - src/core/lib/support/thd_windows.c - src/core/lib/support/time.c - src/core/lib/support/time_posix.c - src/core/lib/support/time_precise.c - src/core/lib/support/time_windows.c - src/core/lib/support/tls_pthread.c - src/core/lib/support/tmpfile_msys.c - src/core/lib/support/tmpfile_posix.c - src/core/lib/support/tmpfile_windows.c - src/core/lib/support/wrap_memcpy.c + src/core/lib/profiling/basic_timers.cc + src/core/lib/profiling/stap_timers.cc + src/core/lib/support/alloc.cc + src/core/lib/support/arena.cc + src/core/lib/support/atm.cc + src/core/lib/support/avl.cc + src/core/lib/support/backoff.cc + src/core/lib/support/cmdline.cc + src/core/lib/support/cpu_iphone.cc + src/core/lib/support/cpu_linux.cc + src/core/lib/support/cpu_posix.cc + src/core/lib/support/cpu_windows.cc + src/core/lib/support/env_linux.cc + src/core/lib/support/env_posix.cc + src/core/lib/support/env_windows.cc + src/core/lib/support/histogram.cc + src/core/lib/support/host_port.cc + src/core/lib/support/log.cc + src/core/lib/support/log_android.cc + src/core/lib/support/log_linux.cc + src/core/lib/support/log_posix.cc + src/core/lib/support/log_windows.cc + src/core/lib/support/mpscq.cc + src/core/lib/support/murmur_hash.cc + src/core/lib/support/stack_lockfree.cc + src/core/lib/support/string.cc + src/core/lib/support/string_posix.cc + src/core/lib/support/string_util_windows.cc + src/core/lib/support/string_windows.cc + src/core/lib/support/subprocess_posix.cc + src/core/lib/support/subprocess_windows.cc + src/core/lib/support/sync.cc + src/core/lib/support/sync_posix.cc + src/core/lib/support/sync_windows.cc + src/core/lib/support/thd.cc + src/core/lib/support/thd_posix.cc + src/core/lib/support/thd_windows.cc + src/core/lib/support/time.cc + src/core/lib/support/time_posix.cc + src/core/lib/support/time_precise.cc + src/core/lib/support/time_windows.cc + src/core/lib/support/tls_pthread.cc + src/core/lib/support/tmpfile_msys.cc + src/core/lib/support/tmpfile_posix.cc + src/core/lib/support/tmpfile_windows.cc + src/core/lib/support/wrap_memcpy.cc ) if(WIN32 AND MSVC) @@ -954,266 +954,264 @@ target_link_libraries(gpr_test_util endif (gRPC_BUILD_TESTS) add_library(grpc - src/core/lib/surface/init.c - src/core/lib/channel/channel_args.c - src/core/lib/channel/channel_stack.c - src/core/lib/channel/channel_stack_builder.c - src/core/lib/channel/connected_channel.c - src/core/lib/channel/handshaker.c - src/core/lib/channel/handshaker_factory.c - src/core/lib/channel/handshaker_registry.c - src/core/lib/compression/compression.c - src/core/lib/compression/message_compress.c - src/core/lib/compression/stream_compression.c - src/core/lib/compression/stream_compression_gzip.c - src/core/lib/compression/stream_compression_identity.c - src/core/lib/debug/stats.c - src/core/lib/debug/stats_data.c - src/core/lib/http/format_request.c - src/core/lib/http/httpcli.c - src/core/lib/http/parser.c - src/core/lib/iomgr/call_combiner.c - src/core/lib/iomgr/closure.c - src/core/lib/iomgr/combiner.c - src/core/lib/iomgr/endpoint.c - src/core/lib/iomgr/endpoint_pair_posix.c - src/core/lib/iomgr/endpoint_pair_uv.c - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c - src/core/lib/iomgr/ev_posix.c - src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/executor.c - src/core/lib/iomgr/gethostname_fallback.c - src/core/lib/iomgr/gethostname_host_name_max.c - src/core/lib/iomgr/gethostname_sysconf.c - src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_uv.c - src/core/lib/iomgr/iomgr_windows.c - src/core/lib/iomgr/is_epollexclusive_available.c - src/core/lib/iomgr/load_file.c - src/core/lib/iomgr/lockfree_event.c - src/core/lib/iomgr/network_status_tracker.c - src/core/lib/iomgr/polling_entity.c - src/core/lib/iomgr/pollset_set_uv.c - src/core/lib/iomgr/pollset_set_windows.c - src/core/lib/iomgr/pollset_uv.c - src/core/lib/iomgr/pollset_windows.c - src/core/lib/iomgr/resolve_address_posix.c - src/core/lib/iomgr/resolve_address_uv.c - src/core/lib/iomgr/resolve_address_windows.c - src/core/lib/iomgr/resource_quota.c - src/core/lib/iomgr/sockaddr_utils.c - src/core/lib/iomgr/socket_factory_posix.c - src/core/lib/iomgr/socket_mutator.c - src/core/lib/iomgr/socket_utils_common_posix.c - src/core/lib/iomgr/socket_utils_linux.c - src/core/lib/iomgr/socket_utils_posix.c - src/core/lib/iomgr/socket_utils_uv.c - src/core/lib/iomgr/socket_utils_windows.c - src/core/lib/iomgr/socket_windows.c - src/core/lib/iomgr/tcp_client_posix.c - src/core/lib/iomgr/tcp_client_uv.c - src/core/lib/iomgr/tcp_client_windows.c - src/core/lib/iomgr/tcp_posix.c - src/core/lib/iomgr/tcp_server_posix.c - src/core/lib/iomgr/tcp_server_utils_posix_common.c - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - src/core/lib/iomgr/tcp_server_uv.c - src/core/lib/iomgr/tcp_server_windows.c - src/core/lib/iomgr/tcp_uv.c - src/core/lib/iomgr/tcp_windows.c - src/core/lib/iomgr/time_averaged_stats.c - src/core/lib/iomgr/timer_generic.c - src/core/lib/iomgr/timer_heap.c - src/core/lib/iomgr/timer_manager.c - src/core/lib/iomgr/timer_uv.c - src/core/lib/iomgr/udp_server.c - src/core/lib/iomgr/unix_sockets_posix.c - src/core/lib/iomgr/unix_sockets_posix_noop.c - src/core/lib/iomgr/wakeup_fd_cv.c - src/core/lib/iomgr/wakeup_fd_eventfd.c - src/core/lib/iomgr/wakeup_fd_nospecial.c - src/core/lib/iomgr/wakeup_fd_pipe.c - src/core/lib/iomgr/wakeup_fd_posix.c - src/core/lib/json/json.c - src/core/lib/json/json_reader.c - src/core/lib/json/json_string.c - src/core/lib/json/json_writer.c - src/core/lib/slice/b64.c - src/core/lib/slice/percent_encoding.c - src/core/lib/slice/slice.c - src/core/lib/slice/slice_buffer.c - src/core/lib/slice/slice_hash_table.c - src/core/lib/slice/slice_intern.c - src/core/lib/slice/slice_string_helpers.c - src/core/lib/surface/alarm.c - src/core/lib/surface/api_trace.c - src/core/lib/surface/byte_buffer.c - src/core/lib/surface/byte_buffer_reader.c - src/core/lib/surface/call.c - src/core/lib/surface/call_details.c - src/core/lib/surface/call_log_batch.c - src/core/lib/surface/channel.c - src/core/lib/surface/channel_init.c - src/core/lib/surface/channel_ping.c - src/core/lib/surface/channel_stack_type.c - src/core/lib/surface/completion_queue.c - src/core/lib/surface/completion_queue_factory.c - src/core/lib/surface/event_string.c + src/core/lib/surface/init.cc + src/core/lib/channel/channel_args.cc + src/core/lib/channel/channel_stack.cc + src/core/lib/channel/channel_stack_builder.cc + src/core/lib/channel/connected_channel.cc + src/core/lib/channel/handshaker.cc + src/core/lib/channel/handshaker_factory.cc + src/core/lib/channel/handshaker_registry.cc + src/core/lib/compression/compression.cc + src/core/lib/compression/message_compress.cc + src/core/lib/compression/stream_compression.cc + src/core/lib/debug/stats.cc + src/core/lib/debug/stats_data.cc + src/core/lib/http/format_request.cc + src/core/lib/http/httpcli.cc + src/core/lib/http/parser.cc + src/core/lib/iomgr/call_combiner.cc + src/core/lib/iomgr/closure.cc + src/core/lib/iomgr/combiner.cc + src/core/lib/iomgr/endpoint.cc + src/core/lib/iomgr/endpoint_pair_posix.cc + src/core/lib/iomgr/endpoint_pair_uv.cc + src/core/lib/iomgr/endpoint_pair_windows.cc + src/core/lib/iomgr/error.cc + src/core/lib/iomgr/ev_epoll1_linux.cc + src/core/lib/iomgr/ev_epollex_linux.cc + src/core/lib/iomgr/ev_epollsig_linux.cc + src/core/lib/iomgr/ev_poll_posix.cc + src/core/lib/iomgr/ev_posix.cc + src/core/lib/iomgr/ev_windows.cc + src/core/lib/iomgr/exec_ctx.cc + src/core/lib/iomgr/executor.cc + src/core/lib/iomgr/gethostname_fallback.cc + src/core/lib/iomgr/gethostname_host_name_max.cc + src/core/lib/iomgr/gethostname_sysconf.cc + src/core/lib/iomgr/iocp_windows.cc + src/core/lib/iomgr/iomgr.cc + src/core/lib/iomgr/iomgr_posix.cc + src/core/lib/iomgr/iomgr_uv.cc + src/core/lib/iomgr/iomgr_windows.cc + src/core/lib/iomgr/is_epollexclusive_available.cc + src/core/lib/iomgr/load_file.cc + src/core/lib/iomgr/lockfree_event.cc + src/core/lib/iomgr/network_status_tracker.cc + src/core/lib/iomgr/polling_entity.cc + src/core/lib/iomgr/pollset_set_uv.cc + src/core/lib/iomgr/pollset_set_windows.cc + src/core/lib/iomgr/pollset_uv.cc + src/core/lib/iomgr/pollset_windows.cc + src/core/lib/iomgr/resolve_address_posix.cc + src/core/lib/iomgr/resolve_address_uv.cc + src/core/lib/iomgr/resolve_address_windows.cc + src/core/lib/iomgr/resource_quota.cc + src/core/lib/iomgr/sockaddr_utils.cc + src/core/lib/iomgr/socket_factory_posix.cc + src/core/lib/iomgr/socket_mutator.cc + src/core/lib/iomgr/socket_utils_common_posix.cc + src/core/lib/iomgr/socket_utils_linux.cc + src/core/lib/iomgr/socket_utils_posix.cc + src/core/lib/iomgr/socket_utils_uv.cc + src/core/lib/iomgr/socket_utils_windows.cc + src/core/lib/iomgr/socket_windows.cc + src/core/lib/iomgr/tcp_client_posix.cc + src/core/lib/iomgr/tcp_client_uv.cc + src/core/lib/iomgr/tcp_client_windows.cc + src/core/lib/iomgr/tcp_posix.cc + src/core/lib/iomgr/tcp_server_posix.cc + src/core/lib/iomgr/tcp_server_utils_posix_common.cc + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + src/core/lib/iomgr/tcp_server_uv.cc + src/core/lib/iomgr/tcp_server_windows.cc + src/core/lib/iomgr/tcp_uv.cc + src/core/lib/iomgr/tcp_windows.cc + src/core/lib/iomgr/time_averaged_stats.cc + src/core/lib/iomgr/timer_generic.cc + src/core/lib/iomgr/timer_heap.cc + src/core/lib/iomgr/timer_manager.cc + src/core/lib/iomgr/timer_uv.cc + src/core/lib/iomgr/udp_server.cc + src/core/lib/iomgr/unix_sockets_posix.cc + src/core/lib/iomgr/unix_sockets_posix_noop.cc + src/core/lib/iomgr/wakeup_fd_cv.cc + src/core/lib/iomgr/wakeup_fd_eventfd.cc + src/core/lib/iomgr/wakeup_fd_nospecial.cc + src/core/lib/iomgr/wakeup_fd_pipe.cc + src/core/lib/iomgr/wakeup_fd_posix.cc + src/core/lib/json/json.cc + src/core/lib/json/json_reader.cc + src/core/lib/json/json_string.cc + src/core/lib/json/json_writer.cc + src/core/lib/slice/b64.cc + src/core/lib/slice/percent_encoding.cc + src/core/lib/slice/slice.cc + src/core/lib/slice/slice_buffer.cc + src/core/lib/slice/slice_hash_table.cc + src/core/lib/slice/slice_intern.cc + src/core/lib/slice/slice_string_helpers.cc + src/core/lib/surface/alarm.cc + src/core/lib/surface/api_trace.cc + src/core/lib/surface/byte_buffer.cc + src/core/lib/surface/byte_buffer_reader.cc + src/core/lib/surface/call.cc + src/core/lib/surface/call_details.cc + src/core/lib/surface/call_log_batch.cc + src/core/lib/surface/channel.cc + src/core/lib/surface/channel_init.cc + src/core/lib/surface/channel_ping.cc + src/core/lib/surface/channel_stack_type.cc + src/core/lib/surface/completion_queue.cc + src/core/lib/surface/completion_queue_factory.cc + src/core/lib/surface/event_string.cc src/core/lib/surface/lame_client.cc - src/core/lib/surface/metadata_array.c - src/core/lib/surface/server.c - src/core/lib/surface/validate_metadata.c - src/core/lib/surface/version.c - src/core/lib/transport/bdp_estimator.c - src/core/lib/transport/byte_stream.c - src/core/lib/transport/connectivity_state.c - src/core/lib/transport/error_utils.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c - src/core/lib/transport/pid_controller.c - src/core/lib/transport/service_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/status_conversion.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c - src/core/lib/transport/transport_op_string.c - src/core/lib/debug/trace.c - src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c - src/core/ext/transport/chttp2/transport/bin_decoder.c - src/core/ext/transport/chttp2/transport/bin_encoder.c - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - src/core/ext/transport/chttp2/transport/chttp2_transport.c - src/core/ext/transport/chttp2/transport/flow_control.c - src/core/ext/transport/chttp2/transport/frame_data.c - src/core/ext/transport/chttp2/transport/frame_goaway.c - src/core/ext/transport/chttp2/transport/frame_ping.c - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - src/core/ext/transport/chttp2/transport/frame_settings.c - src/core/ext/transport/chttp2/transport/frame_window_update.c - src/core/ext/transport/chttp2/transport/hpack_encoder.c - src/core/ext/transport/chttp2/transport/hpack_parser.c - src/core/ext/transport/chttp2/transport/hpack_table.c - src/core/ext/transport/chttp2/transport/http2_settings.c - src/core/ext/transport/chttp2/transport/huffsyms.c - src/core/ext/transport/chttp2/transport/incoming_metadata.c - src/core/ext/transport/chttp2/transport/parsing.c - src/core/ext/transport/chttp2/transport/stream_lists.c - src/core/ext/transport/chttp2/transport/stream_map.c - src/core/ext/transport/chttp2/transport/varint.c - src/core/ext/transport/chttp2/transport/writing.c - src/core/ext/transport/chttp2/alpn/alpn.c - src/core/ext/filters/http/client/http_client_filter.c - src/core/ext/filters/http/http_filters_plugin.c - src/core/ext/filters/http/message_compress/message_compress_filter.c - src/core/ext/filters/http/server/http_server_filter.c - src/core/lib/http/httpcli_security_connector.c - src/core/lib/security/context/security_context.c - src/core/lib/security/credentials/composite/composite_credentials.c - src/core/lib/security/credentials/credentials.c - src/core/lib/security/credentials/credentials_metadata.c - src/core/lib/security/credentials/fake/fake_credentials.c - src/core/lib/security/credentials/google_default/credentials_generic.c - src/core/lib/security/credentials/google_default/google_default_credentials.c - src/core/lib/security/credentials/iam/iam_credentials.c - src/core/lib/security/credentials/jwt/json_token.c - src/core/lib/security/credentials/jwt/jwt_credentials.c - src/core/lib/security/credentials/jwt/jwt_verifier.c - src/core/lib/security/credentials/oauth2/oauth2_credentials.c - src/core/lib/security/credentials/plugin/plugin_credentials.c - src/core/lib/security/credentials/ssl/ssl_credentials.c - src/core/lib/security/transport/client_auth_filter.c - src/core/lib/security/transport/lb_targets_info.c - src/core/lib/security/transport/secure_endpoint.c - src/core/lib/security/transport/security_connector.c - src/core/lib/security/transport/security_handshaker.c - src/core/lib/security/transport/server_auth_filter.c - src/core/lib/security/transport/tsi_error.c - src/core/lib/security/util/json_util.c - src/core/lib/surface/init_secure.c - src/core/tsi/fake_transport_security.c - src/core/tsi/gts_transport_security.c - src/core/tsi/ssl_transport_security.c - src/core/tsi/transport_security_grpc.c - src/core/tsi/transport_security.c - src/core/tsi/transport_security_adapter.c - src/core/ext/transport/chttp2/server/chttp2_server.c - src/core/ext/transport/chttp2/client/secure/secure_channel_create.c - src/core/ext/filters/client_channel/channel_connectivity.c - src/core/ext/filters/client_channel/client_channel.c - src/core/ext/filters/client_channel/client_channel_factory.c - src/core/ext/filters/client_channel/client_channel_plugin.c - src/core/ext/filters/client_channel/connector.c - src/core/ext/filters/client_channel/http_connect_handshaker.c - src/core/ext/filters/client_channel/http_proxy.c - src/core/ext/filters/client_channel/lb_policy.c - src/core/ext/filters/client_channel/lb_policy_factory.c - src/core/ext/filters/client_channel/lb_policy_registry.c - src/core/ext/filters/client_channel/parse_address.c - src/core/ext/filters/client_channel/proxy_mapper.c - src/core/ext/filters/client_channel/proxy_mapper_registry.c - src/core/ext/filters/client_channel/resolver.c - src/core/ext/filters/client_channel/resolver_factory.c - src/core/ext/filters/client_channel/resolver_registry.c - src/core/ext/filters/client_channel/retry_throttle.c - src/core/ext/filters/client_channel/subchannel.c - src/core/ext/filters/client_channel/subchannel_index.c - src/core/ext/filters/client_channel/uri_parser.c - src/core/ext/filters/deadline/deadline_filter.c - src/core/ext/transport/chttp2/client/chttp2_connector.c - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c - src/core/ext/transport/chttp2/client/insecure/channel_create.c - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c - src/core/ext/transport/inproc/inproc_plugin.c - src/core/ext/transport/inproc/inproc_transport.c - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c + src/core/lib/surface/metadata_array.cc + src/core/lib/surface/server.cc + src/core/lib/surface/validate_metadata.cc + src/core/lib/surface/version.cc + src/core/lib/transport/bdp_estimator.cc + src/core/lib/transport/byte_stream.cc + src/core/lib/transport/connectivity_state.cc + src/core/lib/transport/error_utils.cc + src/core/lib/transport/metadata.cc + src/core/lib/transport/metadata_batch.cc + src/core/lib/transport/pid_controller.cc + src/core/lib/transport/service_config.cc + src/core/lib/transport/static_metadata.cc + src/core/lib/transport/status_conversion.cc + src/core/lib/transport/timeout_encoding.cc + src/core/lib/transport/transport.cc + src/core/lib/transport/transport_op_string.cc + src/core/lib/debug/trace.cc + src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc + src/core/ext/transport/chttp2/transport/bin_decoder.cc + src/core/ext/transport/chttp2/transport/bin_encoder.cc + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + src/core/ext/transport/chttp2/transport/chttp2_transport.cc + src/core/ext/transport/chttp2/transport/flow_control.cc + src/core/ext/transport/chttp2/transport/frame_data.cc + src/core/ext/transport/chttp2/transport/frame_goaway.cc + src/core/ext/transport/chttp2/transport/frame_ping.cc + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_settings.cc + src/core/ext/transport/chttp2/transport/frame_window_update.cc + src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_parser.cc + src/core/ext/transport/chttp2/transport/hpack_table.cc + src/core/ext/transport/chttp2/transport/http2_settings.cc + src/core/ext/transport/chttp2/transport/huffsyms.cc + src/core/ext/transport/chttp2/transport/incoming_metadata.cc + src/core/ext/transport/chttp2/transport/parsing.cc + src/core/ext/transport/chttp2/transport/stream_lists.cc + src/core/ext/transport/chttp2/transport/stream_map.cc + src/core/ext/transport/chttp2/transport/varint.cc + src/core/ext/transport/chttp2/transport/writing.cc + src/core/ext/transport/chttp2/alpn/alpn.cc + src/core/ext/filters/http/client/http_client_filter.cc + src/core/ext/filters/http/http_filters_plugin.cc + src/core/ext/filters/http/message_compress/message_compress_filter.cc + src/core/ext/filters/http/server/http_server_filter.cc + src/core/lib/http/httpcli_security_connector.cc + src/core/lib/security/context/security_context.cc + src/core/lib/security/credentials/composite/composite_credentials.cc + src/core/lib/security/credentials/credentials.cc + src/core/lib/security/credentials/credentials_metadata.cc + src/core/lib/security/credentials/fake/fake_credentials.cc + src/core/lib/security/credentials/google_default/credentials_generic.cc + src/core/lib/security/credentials/google_default/google_default_credentials.cc + src/core/lib/security/credentials/iam/iam_credentials.cc + src/core/lib/security/credentials/jwt/json_token.cc + src/core/lib/security/credentials/jwt/jwt_credentials.cc + src/core/lib/security/credentials/jwt/jwt_verifier.cc + src/core/lib/security/credentials/oauth2/oauth2_credentials.cc + src/core/lib/security/credentials/plugin/plugin_credentials.cc + src/core/lib/security/credentials/ssl/ssl_credentials.cc + src/core/lib/security/transport/client_auth_filter.cc + src/core/lib/security/transport/lb_targets_info.cc + src/core/lib/security/transport/secure_endpoint.cc + src/core/lib/security/transport/security_connector.cc + src/core/lib/security/transport/security_handshaker.cc + src/core/lib/security/transport/server_auth_filter.cc + src/core/lib/security/transport/tsi_error.cc + src/core/lib/security/util/json_util.cc + src/core/lib/surface/init_secure.cc + src/core/tsi/fake_transport_security.cc + src/core/tsi/gts_transport_security.cc + src/core/tsi/ssl_transport_security.cc + src/core/tsi/transport_security_grpc.cc + src/core/tsi/transport_security.cc + src/core/tsi/transport_security_adapter.cc + src/core/ext/transport/chttp2/server/chttp2_server.cc + src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc + src/core/ext/filters/client_channel/channel_connectivity.cc + src/core/ext/filters/client_channel/client_channel.cc + src/core/ext/filters/client_channel/client_channel_factory.cc + src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/connector.cc + src/core/ext/filters/client_channel/http_connect_handshaker.cc + src/core/ext/filters/client_channel/http_proxy.cc + src/core/ext/filters/client_channel/lb_policy.cc + src/core/ext/filters/client_channel/lb_policy_factory.cc + src/core/ext/filters/client_channel/lb_policy_registry.cc + src/core/ext/filters/client_channel/parse_address.cc + src/core/ext/filters/client_channel/proxy_mapper.cc + src/core/ext/filters/client_channel/proxy_mapper_registry.cc + src/core/ext/filters/client_channel/resolver.cc + src/core/ext/filters/client_channel/resolver_factory.cc + src/core/ext/filters/client_channel/resolver_registry.cc + src/core/ext/filters/client_channel/retry_throttle.cc + src/core/ext/filters/client_channel/subchannel.cc + src/core/ext/filters/client_channel/subchannel_index.cc + src/core/ext/filters/client_channel/uri_parser.cc + src/core/ext/filters/deadline/deadline_filter.cc + src/core/ext/transport/chttp2/client/chttp2_connector.cc + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc + src/core/ext/transport/chttp2/client/insecure/channel_create.cc + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc + src/core/ext/transport/inproc/inproc_plugin.cc + src/core/ext/transport/inproc/inproc_transport.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc third_party/nanopb/pb_common.c third_party/nanopb/pb_decode.c third_party/nanopb/pb_encode.c - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c - src/core/ext/filters/load_reporting/server_load_reporting_filter.c - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c - src/core/ext/census/base_resources.c - src/core/ext/census/context.c - src/core/ext/census/gen/census.pb.c - src/core/ext/census/gen/trace_context.pb.c - src/core/ext/census/grpc_context.c - 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 - src/core/ext/census/resource.c - src/core/ext/census/trace_context.c - src/core/ext/census/tracing.c - src/core/ext/filters/max_age/max_age_filter.c - src/core/ext/filters/message_size/message_size_filter.c - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c - src/core/ext/filters/workarounds/workaround_utils.c + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc + src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc + src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc + src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc + src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc + src/core/ext/census/base_resources.cc + src/core/ext/census/context.cc + src/core/ext/census/gen/census.pb.cc + src/core/ext/census/gen/trace_context.pb.cc + src/core/ext/census/grpc_context.cc + src/core/ext/census/grpc_filter.cc + src/core/ext/census/grpc_plugin.cc + src/core/ext/census/initialize.cc + src/core/ext/census/intrusive_hash_map.cc + src/core/ext/census/mlog.cc + src/core/ext/census/operation.cc + src/core/ext/census/placeholders.cc + src/core/ext/census/resource.cc + src/core/ext/census/trace_context.cc + src/core/ext/census/tracing.cc + src/core/ext/filters/max_age/max_age_filter.cc + src/core/ext/filters/message_size/message_size_filter.cc + src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc + src/core/ext/filters/workarounds/workaround_utils.cc src/core/plugin_registry/grpc_plugin_registry.cc ) @@ -1305,224 +1303,222 @@ endif() add_library(grpc_cronet - src/core/lib/surface/init.c - src/core/lib/channel/channel_args.c - src/core/lib/channel/channel_stack.c - src/core/lib/channel/channel_stack_builder.c - src/core/lib/channel/connected_channel.c - src/core/lib/channel/handshaker.c - src/core/lib/channel/handshaker_factory.c - src/core/lib/channel/handshaker_registry.c - src/core/lib/compression/compression.c - src/core/lib/compression/message_compress.c - src/core/lib/compression/stream_compression.c - src/core/lib/compression/stream_compression_gzip.c - src/core/lib/compression/stream_compression_identity.c - src/core/lib/debug/stats.c - src/core/lib/debug/stats_data.c - src/core/lib/http/format_request.c - src/core/lib/http/httpcli.c - src/core/lib/http/parser.c - src/core/lib/iomgr/call_combiner.c - src/core/lib/iomgr/closure.c - src/core/lib/iomgr/combiner.c - src/core/lib/iomgr/endpoint.c - src/core/lib/iomgr/endpoint_pair_posix.c - src/core/lib/iomgr/endpoint_pair_uv.c - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c - src/core/lib/iomgr/ev_posix.c - src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/executor.c - src/core/lib/iomgr/gethostname_fallback.c - src/core/lib/iomgr/gethostname_host_name_max.c - src/core/lib/iomgr/gethostname_sysconf.c - src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_uv.c - src/core/lib/iomgr/iomgr_windows.c - src/core/lib/iomgr/is_epollexclusive_available.c - src/core/lib/iomgr/load_file.c - src/core/lib/iomgr/lockfree_event.c - src/core/lib/iomgr/network_status_tracker.c - src/core/lib/iomgr/polling_entity.c - src/core/lib/iomgr/pollset_set_uv.c - src/core/lib/iomgr/pollset_set_windows.c - src/core/lib/iomgr/pollset_uv.c - src/core/lib/iomgr/pollset_windows.c - src/core/lib/iomgr/resolve_address_posix.c - src/core/lib/iomgr/resolve_address_uv.c - src/core/lib/iomgr/resolve_address_windows.c - src/core/lib/iomgr/resource_quota.c - src/core/lib/iomgr/sockaddr_utils.c - src/core/lib/iomgr/socket_factory_posix.c - src/core/lib/iomgr/socket_mutator.c - src/core/lib/iomgr/socket_utils_common_posix.c - src/core/lib/iomgr/socket_utils_linux.c - src/core/lib/iomgr/socket_utils_posix.c - src/core/lib/iomgr/socket_utils_uv.c - src/core/lib/iomgr/socket_utils_windows.c - src/core/lib/iomgr/socket_windows.c - src/core/lib/iomgr/tcp_client_posix.c - src/core/lib/iomgr/tcp_client_uv.c - src/core/lib/iomgr/tcp_client_windows.c - src/core/lib/iomgr/tcp_posix.c - src/core/lib/iomgr/tcp_server_posix.c - src/core/lib/iomgr/tcp_server_utils_posix_common.c - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - src/core/lib/iomgr/tcp_server_uv.c - src/core/lib/iomgr/tcp_server_windows.c - src/core/lib/iomgr/tcp_uv.c - src/core/lib/iomgr/tcp_windows.c - src/core/lib/iomgr/time_averaged_stats.c - src/core/lib/iomgr/timer_generic.c - src/core/lib/iomgr/timer_heap.c - src/core/lib/iomgr/timer_manager.c - src/core/lib/iomgr/timer_uv.c - src/core/lib/iomgr/udp_server.c - src/core/lib/iomgr/unix_sockets_posix.c - src/core/lib/iomgr/unix_sockets_posix_noop.c - src/core/lib/iomgr/wakeup_fd_cv.c - src/core/lib/iomgr/wakeup_fd_eventfd.c - src/core/lib/iomgr/wakeup_fd_nospecial.c - src/core/lib/iomgr/wakeup_fd_pipe.c - src/core/lib/iomgr/wakeup_fd_posix.c - src/core/lib/json/json.c - src/core/lib/json/json_reader.c - src/core/lib/json/json_string.c - src/core/lib/json/json_writer.c - src/core/lib/slice/b64.c - src/core/lib/slice/percent_encoding.c - src/core/lib/slice/slice.c - src/core/lib/slice/slice_buffer.c - src/core/lib/slice/slice_hash_table.c - src/core/lib/slice/slice_intern.c - src/core/lib/slice/slice_string_helpers.c - src/core/lib/surface/alarm.c - src/core/lib/surface/api_trace.c - src/core/lib/surface/byte_buffer.c - src/core/lib/surface/byte_buffer_reader.c - src/core/lib/surface/call.c - src/core/lib/surface/call_details.c - src/core/lib/surface/call_log_batch.c - src/core/lib/surface/channel.c - src/core/lib/surface/channel_init.c - src/core/lib/surface/channel_ping.c - src/core/lib/surface/channel_stack_type.c - src/core/lib/surface/completion_queue.c - src/core/lib/surface/completion_queue_factory.c - src/core/lib/surface/event_string.c + src/core/lib/surface/init.cc + src/core/lib/channel/channel_args.cc + src/core/lib/channel/channel_stack.cc + src/core/lib/channel/channel_stack_builder.cc + src/core/lib/channel/connected_channel.cc + src/core/lib/channel/handshaker.cc + src/core/lib/channel/handshaker_factory.cc + src/core/lib/channel/handshaker_registry.cc + src/core/lib/compression/compression.cc + src/core/lib/compression/message_compress.cc + src/core/lib/compression/stream_compression.cc + src/core/lib/debug/stats.cc + src/core/lib/debug/stats_data.cc + src/core/lib/http/format_request.cc + src/core/lib/http/httpcli.cc + src/core/lib/http/parser.cc + src/core/lib/iomgr/call_combiner.cc + src/core/lib/iomgr/closure.cc + src/core/lib/iomgr/combiner.cc + src/core/lib/iomgr/endpoint.cc + src/core/lib/iomgr/endpoint_pair_posix.cc + src/core/lib/iomgr/endpoint_pair_uv.cc + src/core/lib/iomgr/endpoint_pair_windows.cc + src/core/lib/iomgr/error.cc + src/core/lib/iomgr/ev_epoll1_linux.cc + src/core/lib/iomgr/ev_epollex_linux.cc + src/core/lib/iomgr/ev_epollsig_linux.cc + src/core/lib/iomgr/ev_poll_posix.cc + src/core/lib/iomgr/ev_posix.cc + src/core/lib/iomgr/ev_windows.cc + src/core/lib/iomgr/exec_ctx.cc + src/core/lib/iomgr/executor.cc + src/core/lib/iomgr/gethostname_fallback.cc + src/core/lib/iomgr/gethostname_host_name_max.cc + src/core/lib/iomgr/gethostname_sysconf.cc + src/core/lib/iomgr/iocp_windows.cc + src/core/lib/iomgr/iomgr.cc + src/core/lib/iomgr/iomgr_posix.cc + src/core/lib/iomgr/iomgr_uv.cc + src/core/lib/iomgr/iomgr_windows.cc + src/core/lib/iomgr/is_epollexclusive_available.cc + src/core/lib/iomgr/load_file.cc + src/core/lib/iomgr/lockfree_event.cc + src/core/lib/iomgr/network_status_tracker.cc + src/core/lib/iomgr/polling_entity.cc + src/core/lib/iomgr/pollset_set_uv.cc + src/core/lib/iomgr/pollset_set_windows.cc + src/core/lib/iomgr/pollset_uv.cc + src/core/lib/iomgr/pollset_windows.cc + src/core/lib/iomgr/resolve_address_posix.cc + src/core/lib/iomgr/resolve_address_uv.cc + src/core/lib/iomgr/resolve_address_windows.cc + src/core/lib/iomgr/resource_quota.cc + src/core/lib/iomgr/sockaddr_utils.cc + src/core/lib/iomgr/socket_factory_posix.cc + src/core/lib/iomgr/socket_mutator.cc + src/core/lib/iomgr/socket_utils_common_posix.cc + src/core/lib/iomgr/socket_utils_linux.cc + src/core/lib/iomgr/socket_utils_posix.cc + src/core/lib/iomgr/socket_utils_uv.cc + src/core/lib/iomgr/socket_utils_windows.cc + src/core/lib/iomgr/socket_windows.cc + src/core/lib/iomgr/tcp_client_posix.cc + src/core/lib/iomgr/tcp_client_uv.cc + src/core/lib/iomgr/tcp_client_windows.cc + src/core/lib/iomgr/tcp_posix.cc + src/core/lib/iomgr/tcp_server_posix.cc + src/core/lib/iomgr/tcp_server_utils_posix_common.cc + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + src/core/lib/iomgr/tcp_server_uv.cc + src/core/lib/iomgr/tcp_server_windows.cc + src/core/lib/iomgr/tcp_uv.cc + src/core/lib/iomgr/tcp_windows.cc + src/core/lib/iomgr/time_averaged_stats.cc + src/core/lib/iomgr/timer_generic.cc + src/core/lib/iomgr/timer_heap.cc + src/core/lib/iomgr/timer_manager.cc + src/core/lib/iomgr/timer_uv.cc + src/core/lib/iomgr/udp_server.cc + src/core/lib/iomgr/unix_sockets_posix.cc + src/core/lib/iomgr/unix_sockets_posix_noop.cc + src/core/lib/iomgr/wakeup_fd_cv.cc + src/core/lib/iomgr/wakeup_fd_eventfd.cc + src/core/lib/iomgr/wakeup_fd_nospecial.cc + src/core/lib/iomgr/wakeup_fd_pipe.cc + src/core/lib/iomgr/wakeup_fd_posix.cc + src/core/lib/json/json.cc + src/core/lib/json/json_reader.cc + src/core/lib/json/json_string.cc + src/core/lib/json/json_writer.cc + src/core/lib/slice/b64.cc + src/core/lib/slice/percent_encoding.cc + src/core/lib/slice/slice.cc + src/core/lib/slice/slice_buffer.cc + src/core/lib/slice/slice_hash_table.cc + src/core/lib/slice/slice_intern.cc + src/core/lib/slice/slice_string_helpers.cc + src/core/lib/surface/alarm.cc + src/core/lib/surface/api_trace.cc + src/core/lib/surface/byte_buffer.cc + src/core/lib/surface/byte_buffer_reader.cc + src/core/lib/surface/call.cc + src/core/lib/surface/call_details.cc + src/core/lib/surface/call_log_batch.cc + src/core/lib/surface/channel.cc + src/core/lib/surface/channel_init.cc + src/core/lib/surface/channel_ping.cc + src/core/lib/surface/channel_stack_type.cc + src/core/lib/surface/completion_queue.cc + src/core/lib/surface/completion_queue_factory.cc + src/core/lib/surface/event_string.cc src/core/lib/surface/lame_client.cc - src/core/lib/surface/metadata_array.c - src/core/lib/surface/server.c - src/core/lib/surface/validate_metadata.c - src/core/lib/surface/version.c - src/core/lib/transport/bdp_estimator.c - src/core/lib/transport/byte_stream.c - src/core/lib/transport/connectivity_state.c - src/core/lib/transport/error_utils.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c - src/core/lib/transport/pid_controller.c - src/core/lib/transport/service_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/status_conversion.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c - src/core/lib/transport/transport_op_string.c - src/core/lib/debug/trace.c - src/core/ext/transport/cronet/client/secure/cronet_channel_create.c - src/core/ext/transport/cronet/transport/cronet_api_dummy.c - src/core/ext/transport/cronet/transport/cronet_transport.c - src/core/ext/transport/chttp2/client/secure/secure_channel_create.c - src/core/ext/transport/chttp2/transport/bin_decoder.c - src/core/ext/transport/chttp2/transport/bin_encoder.c - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - src/core/ext/transport/chttp2/transport/chttp2_transport.c - src/core/ext/transport/chttp2/transport/flow_control.c - src/core/ext/transport/chttp2/transport/frame_data.c - src/core/ext/transport/chttp2/transport/frame_goaway.c - src/core/ext/transport/chttp2/transport/frame_ping.c - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - src/core/ext/transport/chttp2/transport/frame_settings.c - src/core/ext/transport/chttp2/transport/frame_window_update.c - src/core/ext/transport/chttp2/transport/hpack_encoder.c - src/core/ext/transport/chttp2/transport/hpack_parser.c - src/core/ext/transport/chttp2/transport/hpack_table.c - src/core/ext/transport/chttp2/transport/http2_settings.c - src/core/ext/transport/chttp2/transport/huffsyms.c - src/core/ext/transport/chttp2/transport/incoming_metadata.c - src/core/ext/transport/chttp2/transport/parsing.c - src/core/ext/transport/chttp2/transport/stream_lists.c - src/core/ext/transport/chttp2/transport/stream_map.c - src/core/ext/transport/chttp2/transport/varint.c - src/core/ext/transport/chttp2/transport/writing.c - src/core/ext/transport/chttp2/alpn/alpn.c - src/core/ext/filters/http/client/http_client_filter.c - src/core/ext/filters/http/http_filters_plugin.c - src/core/ext/filters/http/message_compress/message_compress_filter.c - src/core/ext/filters/http/server/http_server_filter.c - src/core/ext/filters/client_channel/channel_connectivity.c - src/core/ext/filters/client_channel/client_channel.c - src/core/ext/filters/client_channel/client_channel_factory.c - src/core/ext/filters/client_channel/client_channel_plugin.c - src/core/ext/filters/client_channel/connector.c - src/core/ext/filters/client_channel/http_connect_handshaker.c - src/core/ext/filters/client_channel/http_proxy.c - src/core/ext/filters/client_channel/lb_policy.c - src/core/ext/filters/client_channel/lb_policy_factory.c - src/core/ext/filters/client_channel/lb_policy_registry.c - src/core/ext/filters/client_channel/parse_address.c - src/core/ext/filters/client_channel/proxy_mapper.c - src/core/ext/filters/client_channel/proxy_mapper_registry.c - src/core/ext/filters/client_channel/resolver.c - src/core/ext/filters/client_channel/resolver_factory.c - src/core/ext/filters/client_channel/resolver_registry.c - src/core/ext/filters/client_channel/retry_throttle.c - src/core/ext/filters/client_channel/subchannel.c - src/core/ext/filters/client_channel/subchannel_index.c - src/core/ext/filters/client_channel/uri_parser.c - src/core/ext/filters/deadline/deadline_filter.c - src/core/lib/http/httpcli_security_connector.c - src/core/lib/security/context/security_context.c - src/core/lib/security/credentials/composite/composite_credentials.c - src/core/lib/security/credentials/credentials.c - src/core/lib/security/credentials/credentials_metadata.c - src/core/lib/security/credentials/fake/fake_credentials.c - src/core/lib/security/credentials/google_default/credentials_generic.c - src/core/lib/security/credentials/google_default/google_default_credentials.c - src/core/lib/security/credentials/iam/iam_credentials.c - src/core/lib/security/credentials/jwt/json_token.c - src/core/lib/security/credentials/jwt/jwt_credentials.c - src/core/lib/security/credentials/jwt/jwt_verifier.c - src/core/lib/security/credentials/oauth2/oauth2_credentials.c - src/core/lib/security/credentials/plugin/plugin_credentials.c - src/core/lib/security/credentials/ssl/ssl_credentials.c - src/core/lib/security/transport/client_auth_filter.c - src/core/lib/security/transport/lb_targets_info.c - src/core/lib/security/transport/secure_endpoint.c - src/core/lib/security/transport/security_connector.c - src/core/lib/security/transport/security_handshaker.c - src/core/lib/security/transport/server_auth_filter.c - src/core/lib/security/transport/tsi_error.c - src/core/lib/security/util/json_util.c - src/core/lib/surface/init_secure.c - src/core/tsi/fake_transport_security.c - src/core/tsi/gts_transport_security.c - src/core/tsi/ssl_transport_security.c - src/core/tsi/transport_security_grpc.c - src/core/tsi/transport_security.c - src/core/tsi/transport_security_adapter.c - src/core/ext/transport/chttp2/client/chttp2_connector.c - src/core/ext/filters/load_reporting/server_load_reporting_filter.c - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c + src/core/lib/surface/metadata_array.cc + src/core/lib/surface/server.cc + src/core/lib/surface/validate_metadata.cc + src/core/lib/surface/version.cc + src/core/lib/transport/bdp_estimator.cc + src/core/lib/transport/byte_stream.cc + src/core/lib/transport/connectivity_state.cc + src/core/lib/transport/error_utils.cc + src/core/lib/transport/metadata.cc + src/core/lib/transport/metadata_batch.cc + src/core/lib/transport/pid_controller.cc + src/core/lib/transport/service_config.cc + src/core/lib/transport/static_metadata.cc + src/core/lib/transport/status_conversion.cc + src/core/lib/transport/timeout_encoding.cc + src/core/lib/transport/transport.cc + src/core/lib/transport/transport_op_string.cc + src/core/lib/debug/trace.cc + src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc + src/core/ext/transport/cronet/transport/cronet_api_dummy.cc + src/core/ext/transport/cronet/transport/cronet_transport.cc + src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc + src/core/ext/transport/chttp2/transport/bin_decoder.cc + src/core/ext/transport/chttp2/transport/bin_encoder.cc + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + src/core/ext/transport/chttp2/transport/chttp2_transport.cc + src/core/ext/transport/chttp2/transport/flow_control.cc + src/core/ext/transport/chttp2/transport/frame_data.cc + src/core/ext/transport/chttp2/transport/frame_goaway.cc + src/core/ext/transport/chttp2/transport/frame_ping.cc + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_settings.cc + src/core/ext/transport/chttp2/transport/frame_window_update.cc + src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_parser.cc + src/core/ext/transport/chttp2/transport/hpack_table.cc + src/core/ext/transport/chttp2/transport/http2_settings.cc + src/core/ext/transport/chttp2/transport/huffsyms.cc + src/core/ext/transport/chttp2/transport/incoming_metadata.cc + src/core/ext/transport/chttp2/transport/parsing.cc + src/core/ext/transport/chttp2/transport/stream_lists.cc + src/core/ext/transport/chttp2/transport/stream_map.cc + src/core/ext/transport/chttp2/transport/varint.cc + src/core/ext/transport/chttp2/transport/writing.cc + src/core/ext/transport/chttp2/alpn/alpn.cc + src/core/ext/filters/http/client/http_client_filter.cc + src/core/ext/filters/http/http_filters_plugin.cc + src/core/ext/filters/http/message_compress/message_compress_filter.cc + src/core/ext/filters/http/server/http_server_filter.cc + src/core/ext/filters/client_channel/channel_connectivity.cc + src/core/ext/filters/client_channel/client_channel.cc + src/core/ext/filters/client_channel/client_channel_factory.cc + src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/connector.cc + src/core/ext/filters/client_channel/http_connect_handshaker.cc + src/core/ext/filters/client_channel/http_proxy.cc + src/core/ext/filters/client_channel/lb_policy.cc + src/core/ext/filters/client_channel/lb_policy_factory.cc + src/core/ext/filters/client_channel/lb_policy_registry.cc + src/core/ext/filters/client_channel/parse_address.cc + src/core/ext/filters/client_channel/proxy_mapper.cc + src/core/ext/filters/client_channel/proxy_mapper_registry.cc + src/core/ext/filters/client_channel/resolver.cc + src/core/ext/filters/client_channel/resolver_factory.cc + src/core/ext/filters/client_channel/resolver_registry.cc + src/core/ext/filters/client_channel/retry_throttle.cc + src/core/ext/filters/client_channel/subchannel.cc + src/core/ext/filters/client_channel/subchannel_index.cc + src/core/ext/filters/client_channel/uri_parser.cc + src/core/ext/filters/deadline/deadline_filter.cc + src/core/lib/http/httpcli_security_connector.cc + src/core/lib/security/context/security_context.cc + src/core/lib/security/credentials/composite/composite_credentials.cc + src/core/lib/security/credentials/credentials.cc + src/core/lib/security/credentials/credentials_metadata.cc + src/core/lib/security/credentials/fake/fake_credentials.cc + src/core/lib/security/credentials/google_default/credentials_generic.cc + src/core/lib/security/credentials/google_default/google_default_credentials.cc + src/core/lib/security/credentials/iam/iam_credentials.cc + src/core/lib/security/credentials/jwt/json_token.cc + src/core/lib/security/credentials/jwt/jwt_credentials.cc + src/core/lib/security/credentials/jwt/jwt_verifier.cc + src/core/lib/security/credentials/oauth2/oauth2_credentials.cc + src/core/lib/security/credentials/plugin/plugin_credentials.cc + src/core/lib/security/credentials/ssl/ssl_credentials.cc + src/core/lib/security/transport/client_auth_filter.cc + src/core/lib/security/transport/lb_targets_info.cc + src/core/lib/security/transport/secure_endpoint.cc + src/core/lib/security/transport/security_connector.cc + src/core/lib/security/transport/security_handshaker.cc + src/core/lib/security/transport/server_auth_filter.cc + src/core/lib/security/transport/tsi_error.cc + src/core/lib/security/util/json_util.cc + src/core/lib/surface/init_secure.cc + src/core/tsi/fake_transport_security.cc + src/core/tsi/gts_transport_security.cc + src/core/tsi/ssl_transport_security.cc + src/core/tsi/transport_security_grpc.cc + src/core/tsi/transport_security.cc + src/core/tsi/transport_security_adapter.cc + src/core/ext/transport/chttp2/client/chttp2_connector.cc + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc src/core/plugin_registry/grpc_cronet_plugin_registry.cc ) @@ -1610,201 +1606,199 @@ add_library(grpc_test_util test/core/end2end/data/server1_key.c test/core/end2end/data/test_root_cert.c test/core/security/oauth2_utils.c - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c - test/core/end2end/cq_verifier.c - test/core/end2end/fixtures/http_proxy_fixture.c - test/core/end2end/fixtures/proxy.c - test/core/iomgr/endpoint_tests.c - test/core/util/debugger_macros.c - test/core/util/grpc_profiler.c - test/core/util/memory_counters.c - test/core/util/mock_endpoint.c - test/core/util/parse_hexstring.c - test/core/util/passthru_endpoint.c - test/core/util/port.c - test/core/util/port_server_client.c - test/core/util/slice_splitter.c - test/core/util/trickle_endpoint.c - src/core/lib/channel/channel_args.c - src/core/lib/channel/channel_stack.c - src/core/lib/channel/channel_stack_builder.c - src/core/lib/channel/connected_channel.c - src/core/lib/channel/handshaker.c - src/core/lib/channel/handshaker_factory.c - src/core/lib/channel/handshaker_registry.c - src/core/lib/compression/compression.c - src/core/lib/compression/message_compress.c - src/core/lib/compression/stream_compression.c - src/core/lib/compression/stream_compression_gzip.c - src/core/lib/compression/stream_compression_identity.c - src/core/lib/debug/stats.c - src/core/lib/debug/stats_data.c - src/core/lib/http/format_request.c - src/core/lib/http/httpcli.c - src/core/lib/http/parser.c - src/core/lib/iomgr/call_combiner.c - src/core/lib/iomgr/closure.c - src/core/lib/iomgr/combiner.c - src/core/lib/iomgr/endpoint.c - src/core/lib/iomgr/endpoint_pair_posix.c - src/core/lib/iomgr/endpoint_pair_uv.c - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c - src/core/lib/iomgr/ev_posix.c - src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/executor.c - src/core/lib/iomgr/gethostname_fallback.c - src/core/lib/iomgr/gethostname_host_name_max.c - src/core/lib/iomgr/gethostname_sysconf.c - src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_uv.c - src/core/lib/iomgr/iomgr_windows.c - src/core/lib/iomgr/is_epollexclusive_available.c - src/core/lib/iomgr/load_file.c - src/core/lib/iomgr/lockfree_event.c - src/core/lib/iomgr/network_status_tracker.c - src/core/lib/iomgr/polling_entity.c - src/core/lib/iomgr/pollset_set_uv.c - src/core/lib/iomgr/pollset_set_windows.c - src/core/lib/iomgr/pollset_uv.c - src/core/lib/iomgr/pollset_windows.c - src/core/lib/iomgr/resolve_address_posix.c - src/core/lib/iomgr/resolve_address_uv.c - src/core/lib/iomgr/resolve_address_windows.c - src/core/lib/iomgr/resource_quota.c - src/core/lib/iomgr/sockaddr_utils.c - src/core/lib/iomgr/socket_factory_posix.c - src/core/lib/iomgr/socket_mutator.c - src/core/lib/iomgr/socket_utils_common_posix.c - src/core/lib/iomgr/socket_utils_linux.c - src/core/lib/iomgr/socket_utils_posix.c - src/core/lib/iomgr/socket_utils_uv.c - src/core/lib/iomgr/socket_utils_windows.c - src/core/lib/iomgr/socket_windows.c - src/core/lib/iomgr/tcp_client_posix.c - src/core/lib/iomgr/tcp_client_uv.c - src/core/lib/iomgr/tcp_client_windows.c - src/core/lib/iomgr/tcp_posix.c - src/core/lib/iomgr/tcp_server_posix.c - src/core/lib/iomgr/tcp_server_utils_posix_common.c - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - src/core/lib/iomgr/tcp_server_uv.c - src/core/lib/iomgr/tcp_server_windows.c - src/core/lib/iomgr/tcp_uv.c - src/core/lib/iomgr/tcp_windows.c - src/core/lib/iomgr/time_averaged_stats.c - src/core/lib/iomgr/timer_generic.c - src/core/lib/iomgr/timer_heap.c - src/core/lib/iomgr/timer_manager.c - src/core/lib/iomgr/timer_uv.c - src/core/lib/iomgr/udp_server.c - src/core/lib/iomgr/unix_sockets_posix.c - src/core/lib/iomgr/unix_sockets_posix_noop.c - src/core/lib/iomgr/wakeup_fd_cv.c - src/core/lib/iomgr/wakeup_fd_eventfd.c - src/core/lib/iomgr/wakeup_fd_nospecial.c - src/core/lib/iomgr/wakeup_fd_pipe.c - src/core/lib/iomgr/wakeup_fd_posix.c - src/core/lib/json/json.c - src/core/lib/json/json_reader.c - src/core/lib/json/json_string.c - src/core/lib/json/json_writer.c - src/core/lib/slice/b64.c - src/core/lib/slice/percent_encoding.c - src/core/lib/slice/slice.c - src/core/lib/slice/slice_buffer.c - src/core/lib/slice/slice_hash_table.c - src/core/lib/slice/slice_intern.c - src/core/lib/slice/slice_string_helpers.c - src/core/lib/surface/alarm.c - src/core/lib/surface/api_trace.c - src/core/lib/surface/byte_buffer.c - src/core/lib/surface/byte_buffer_reader.c - src/core/lib/surface/call.c - src/core/lib/surface/call_details.c - src/core/lib/surface/call_log_batch.c - src/core/lib/surface/channel.c - src/core/lib/surface/channel_init.c - src/core/lib/surface/channel_ping.c - src/core/lib/surface/channel_stack_type.c - src/core/lib/surface/completion_queue.c - src/core/lib/surface/completion_queue_factory.c - src/core/lib/surface/event_string.c + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc + test/core/end2end/cq_verifier.cc + test/core/end2end/fixtures/http_proxy_fixture.cc + test/core/end2end/fixtures/proxy.cc + test/core/iomgr/endpoint_tests.cc + test/core/util/debugger_macros.cc + test/core/util/grpc_profiler.cc + test/core/util/memory_counters.cc + test/core/util/mock_endpoint.cc + test/core/util/parse_hexstring.cc + test/core/util/passthru_endpoint.cc + test/core/util/port.cc + test/core/util/port_server_client.cc + test/core/util/slice_splitter.cc + test/core/util/trickle_endpoint.cc + src/core/lib/channel/channel_args.cc + src/core/lib/channel/channel_stack.cc + src/core/lib/channel/channel_stack_builder.cc + src/core/lib/channel/connected_channel.cc + src/core/lib/channel/handshaker.cc + src/core/lib/channel/handshaker_factory.cc + src/core/lib/channel/handshaker_registry.cc + src/core/lib/compression/compression.cc + src/core/lib/compression/message_compress.cc + src/core/lib/compression/stream_compression.cc + src/core/lib/debug/stats.cc + src/core/lib/debug/stats_data.cc + src/core/lib/http/format_request.cc + src/core/lib/http/httpcli.cc + src/core/lib/http/parser.cc + src/core/lib/iomgr/call_combiner.cc + src/core/lib/iomgr/closure.cc + src/core/lib/iomgr/combiner.cc + src/core/lib/iomgr/endpoint.cc + src/core/lib/iomgr/endpoint_pair_posix.cc + src/core/lib/iomgr/endpoint_pair_uv.cc + src/core/lib/iomgr/endpoint_pair_windows.cc + src/core/lib/iomgr/error.cc + src/core/lib/iomgr/ev_epoll1_linux.cc + src/core/lib/iomgr/ev_epollex_linux.cc + src/core/lib/iomgr/ev_epollsig_linux.cc + src/core/lib/iomgr/ev_poll_posix.cc + src/core/lib/iomgr/ev_posix.cc + src/core/lib/iomgr/ev_windows.cc + src/core/lib/iomgr/exec_ctx.cc + src/core/lib/iomgr/executor.cc + src/core/lib/iomgr/gethostname_fallback.cc + src/core/lib/iomgr/gethostname_host_name_max.cc + src/core/lib/iomgr/gethostname_sysconf.cc + src/core/lib/iomgr/iocp_windows.cc + src/core/lib/iomgr/iomgr.cc + src/core/lib/iomgr/iomgr_posix.cc + src/core/lib/iomgr/iomgr_uv.cc + src/core/lib/iomgr/iomgr_windows.cc + src/core/lib/iomgr/is_epollexclusive_available.cc + src/core/lib/iomgr/load_file.cc + src/core/lib/iomgr/lockfree_event.cc + src/core/lib/iomgr/network_status_tracker.cc + src/core/lib/iomgr/polling_entity.cc + src/core/lib/iomgr/pollset_set_uv.cc + src/core/lib/iomgr/pollset_set_windows.cc + src/core/lib/iomgr/pollset_uv.cc + src/core/lib/iomgr/pollset_windows.cc + src/core/lib/iomgr/resolve_address_posix.cc + src/core/lib/iomgr/resolve_address_uv.cc + src/core/lib/iomgr/resolve_address_windows.cc + src/core/lib/iomgr/resource_quota.cc + src/core/lib/iomgr/sockaddr_utils.cc + src/core/lib/iomgr/socket_factory_posix.cc + src/core/lib/iomgr/socket_mutator.cc + src/core/lib/iomgr/socket_utils_common_posix.cc + src/core/lib/iomgr/socket_utils_linux.cc + src/core/lib/iomgr/socket_utils_posix.cc + src/core/lib/iomgr/socket_utils_uv.cc + src/core/lib/iomgr/socket_utils_windows.cc + src/core/lib/iomgr/socket_windows.cc + src/core/lib/iomgr/tcp_client_posix.cc + src/core/lib/iomgr/tcp_client_uv.cc + src/core/lib/iomgr/tcp_client_windows.cc + src/core/lib/iomgr/tcp_posix.cc + src/core/lib/iomgr/tcp_server_posix.cc + src/core/lib/iomgr/tcp_server_utils_posix_common.cc + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + src/core/lib/iomgr/tcp_server_uv.cc + src/core/lib/iomgr/tcp_server_windows.cc + src/core/lib/iomgr/tcp_uv.cc + src/core/lib/iomgr/tcp_windows.cc + src/core/lib/iomgr/time_averaged_stats.cc + src/core/lib/iomgr/timer_generic.cc + src/core/lib/iomgr/timer_heap.cc + src/core/lib/iomgr/timer_manager.cc + src/core/lib/iomgr/timer_uv.cc + src/core/lib/iomgr/udp_server.cc + src/core/lib/iomgr/unix_sockets_posix.cc + src/core/lib/iomgr/unix_sockets_posix_noop.cc + src/core/lib/iomgr/wakeup_fd_cv.cc + src/core/lib/iomgr/wakeup_fd_eventfd.cc + src/core/lib/iomgr/wakeup_fd_nospecial.cc + src/core/lib/iomgr/wakeup_fd_pipe.cc + src/core/lib/iomgr/wakeup_fd_posix.cc + src/core/lib/json/json.cc + src/core/lib/json/json_reader.cc + src/core/lib/json/json_string.cc + src/core/lib/json/json_writer.cc + src/core/lib/slice/b64.cc + src/core/lib/slice/percent_encoding.cc + src/core/lib/slice/slice.cc + src/core/lib/slice/slice_buffer.cc + src/core/lib/slice/slice_hash_table.cc + src/core/lib/slice/slice_intern.cc + src/core/lib/slice/slice_string_helpers.cc + src/core/lib/surface/alarm.cc + src/core/lib/surface/api_trace.cc + src/core/lib/surface/byte_buffer.cc + src/core/lib/surface/byte_buffer_reader.cc + src/core/lib/surface/call.cc + src/core/lib/surface/call_details.cc + src/core/lib/surface/call_log_batch.cc + src/core/lib/surface/channel.cc + src/core/lib/surface/channel_init.cc + src/core/lib/surface/channel_ping.cc + src/core/lib/surface/channel_stack_type.cc + src/core/lib/surface/completion_queue.cc + src/core/lib/surface/completion_queue_factory.cc + src/core/lib/surface/event_string.cc src/core/lib/surface/lame_client.cc - src/core/lib/surface/metadata_array.c - src/core/lib/surface/server.c - src/core/lib/surface/validate_metadata.c - src/core/lib/surface/version.c - src/core/lib/transport/bdp_estimator.c - src/core/lib/transport/byte_stream.c - src/core/lib/transport/connectivity_state.c - src/core/lib/transport/error_utils.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c - src/core/lib/transport/pid_controller.c - src/core/lib/transport/service_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/status_conversion.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c - src/core/lib/transport/transport_op_string.c - src/core/lib/debug/trace.c - src/core/ext/filters/client_channel/channel_connectivity.c - src/core/ext/filters/client_channel/client_channel.c - src/core/ext/filters/client_channel/client_channel_factory.c - src/core/ext/filters/client_channel/client_channel_plugin.c - src/core/ext/filters/client_channel/connector.c - src/core/ext/filters/client_channel/http_connect_handshaker.c - src/core/ext/filters/client_channel/http_proxy.c - src/core/ext/filters/client_channel/lb_policy.c - src/core/ext/filters/client_channel/lb_policy_factory.c - src/core/ext/filters/client_channel/lb_policy_registry.c - src/core/ext/filters/client_channel/parse_address.c - src/core/ext/filters/client_channel/proxy_mapper.c - src/core/ext/filters/client_channel/proxy_mapper_registry.c - src/core/ext/filters/client_channel/resolver.c - src/core/ext/filters/client_channel/resolver_factory.c - src/core/ext/filters/client_channel/resolver_registry.c - src/core/ext/filters/client_channel/retry_throttle.c - src/core/ext/filters/client_channel/subchannel.c - src/core/ext/filters/client_channel/subchannel_index.c - src/core/ext/filters/client_channel/uri_parser.c - src/core/ext/filters/deadline/deadline_filter.c - src/core/ext/transport/chttp2/transport/bin_decoder.c - src/core/ext/transport/chttp2/transport/bin_encoder.c - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - src/core/ext/transport/chttp2/transport/chttp2_transport.c - src/core/ext/transport/chttp2/transport/flow_control.c - src/core/ext/transport/chttp2/transport/frame_data.c - src/core/ext/transport/chttp2/transport/frame_goaway.c - src/core/ext/transport/chttp2/transport/frame_ping.c - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - src/core/ext/transport/chttp2/transport/frame_settings.c - src/core/ext/transport/chttp2/transport/frame_window_update.c - src/core/ext/transport/chttp2/transport/hpack_encoder.c - src/core/ext/transport/chttp2/transport/hpack_parser.c - src/core/ext/transport/chttp2/transport/hpack_table.c - src/core/ext/transport/chttp2/transport/http2_settings.c - src/core/ext/transport/chttp2/transport/huffsyms.c - src/core/ext/transport/chttp2/transport/incoming_metadata.c - src/core/ext/transport/chttp2/transport/parsing.c - src/core/ext/transport/chttp2/transport/stream_lists.c - src/core/ext/transport/chttp2/transport/stream_map.c - src/core/ext/transport/chttp2/transport/varint.c - src/core/ext/transport/chttp2/transport/writing.c - src/core/ext/transport/chttp2/alpn/alpn.c - src/core/ext/filters/http/client/http_client_filter.c - src/core/ext/filters/http/http_filters_plugin.c - src/core/ext/filters/http/message_compress/message_compress_filter.c - src/core/ext/filters/http/server/http_server_filter.c + src/core/lib/surface/metadata_array.cc + src/core/lib/surface/server.cc + src/core/lib/surface/validate_metadata.cc + src/core/lib/surface/version.cc + src/core/lib/transport/bdp_estimator.cc + src/core/lib/transport/byte_stream.cc + src/core/lib/transport/connectivity_state.cc + src/core/lib/transport/error_utils.cc + src/core/lib/transport/metadata.cc + src/core/lib/transport/metadata_batch.cc + src/core/lib/transport/pid_controller.cc + src/core/lib/transport/service_config.cc + src/core/lib/transport/static_metadata.cc + src/core/lib/transport/status_conversion.cc + src/core/lib/transport/timeout_encoding.cc + src/core/lib/transport/transport.cc + src/core/lib/transport/transport_op_string.cc + src/core/lib/debug/trace.cc + src/core/ext/filters/client_channel/channel_connectivity.cc + src/core/ext/filters/client_channel/client_channel.cc + src/core/ext/filters/client_channel/client_channel_factory.cc + src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/connector.cc + src/core/ext/filters/client_channel/http_connect_handshaker.cc + src/core/ext/filters/client_channel/http_proxy.cc + src/core/ext/filters/client_channel/lb_policy.cc + src/core/ext/filters/client_channel/lb_policy_factory.cc + src/core/ext/filters/client_channel/lb_policy_registry.cc + src/core/ext/filters/client_channel/parse_address.cc + src/core/ext/filters/client_channel/proxy_mapper.cc + src/core/ext/filters/client_channel/proxy_mapper_registry.cc + src/core/ext/filters/client_channel/resolver.cc + src/core/ext/filters/client_channel/resolver_factory.cc + src/core/ext/filters/client_channel/resolver_registry.cc + src/core/ext/filters/client_channel/retry_throttle.cc + src/core/ext/filters/client_channel/subchannel.cc + src/core/ext/filters/client_channel/subchannel_index.cc + src/core/ext/filters/client_channel/uri_parser.cc + src/core/ext/filters/deadline/deadline_filter.cc + src/core/ext/transport/chttp2/transport/bin_decoder.cc + src/core/ext/transport/chttp2/transport/bin_encoder.cc + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + src/core/ext/transport/chttp2/transport/chttp2_transport.cc + src/core/ext/transport/chttp2/transport/flow_control.cc + src/core/ext/transport/chttp2/transport/frame_data.cc + src/core/ext/transport/chttp2/transport/frame_goaway.cc + src/core/ext/transport/chttp2/transport/frame_ping.cc + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_settings.cc + src/core/ext/transport/chttp2/transport/frame_window_update.cc + src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_parser.cc + src/core/ext/transport/chttp2/transport/hpack_table.cc + src/core/ext/transport/chttp2/transport/http2_settings.cc + src/core/ext/transport/chttp2/transport/huffsyms.cc + src/core/ext/transport/chttp2/transport/incoming_metadata.cc + src/core/ext/transport/chttp2/transport/parsing.cc + src/core/ext/transport/chttp2/transport/stream_lists.cc + src/core/ext/transport/chttp2/transport/stream_map.cc + src/core/ext/transport/chttp2/transport/varint.cc + src/core/ext/transport/chttp2/transport/writing.cc + src/core/ext/transport/chttp2/alpn/alpn.cc + src/core/ext/filters/http/client/http_client_filter.cc + src/core/ext/filters/http/http_filters_plugin.cc + src/core/ext/filters/http/message_compress/message_compress_filter.cc + src/core/ext/filters/http/server/http_server_filter.cc ) if(WIN32 AND MSVC) @@ -1873,201 +1867,199 @@ endif (gRPC_BUILD_TESTS) if (gRPC_BUILD_TESTS) add_library(grpc_test_util_unsecure - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c - test/core/end2end/cq_verifier.c - test/core/end2end/fixtures/http_proxy_fixture.c - test/core/end2end/fixtures/proxy.c - test/core/iomgr/endpoint_tests.c - test/core/util/debugger_macros.c - test/core/util/grpc_profiler.c - test/core/util/memory_counters.c - test/core/util/mock_endpoint.c - test/core/util/parse_hexstring.c - test/core/util/passthru_endpoint.c - test/core/util/port.c - test/core/util/port_server_client.c - test/core/util/slice_splitter.c - test/core/util/trickle_endpoint.c - src/core/lib/channel/channel_args.c - src/core/lib/channel/channel_stack.c - src/core/lib/channel/channel_stack_builder.c - src/core/lib/channel/connected_channel.c - src/core/lib/channel/handshaker.c - src/core/lib/channel/handshaker_factory.c - src/core/lib/channel/handshaker_registry.c - src/core/lib/compression/compression.c - src/core/lib/compression/message_compress.c - src/core/lib/compression/stream_compression.c - src/core/lib/compression/stream_compression_gzip.c - src/core/lib/compression/stream_compression_identity.c - src/core/lib/debug/stats.c - src/core/lib/debug/stats_data.c - src/core/lib/http/format_request.c - src/core/lib/http/httpcli.c - src/core/lib/http/parser.c - src/core/lib/iomgr/call_combiner.c - src/core/lib/iomgr/closure.c - src/core/lib/iomgr/combiner.c - src/core/lib/iomgr/endpoint.c - src/core/lib/iomgr/endpoint_pair_posix.c - src/core/lib/iomgr/endpoint_pair_uv.c - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c - src/core/lib/iomgr/ev_posix.c - src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/executor.c - src/core/lib/iomgr/gethostname_fallback.c - src/core/lib/iomgr/gethostname_host_name_max.c - src/core/lib/iomgr/gethostname_sysconf.c - src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_uv.c - src/core/lib/iomgr/iomgr_windows.c - src/core/lib/iomgr/is_epollexclusive_available.c - src/core/lib/iomgr/load_file.c - src/core/lib/iomgr/lockfree_event.c - src/core/lib/iomgr/network_status_tracker.c - src/core/lib/iomgr/polling_entity.c - src/core/lib/iomgr/pollset_set_uv.c - src/core/lib/iomgr/pollset_set_windows.c - src/core/lib/iomgr/pollset_uv.c - src/core/lib/iomgr/pollset_windows.c - src/core/lib/iomgr/resolve_address_posix.c - src/core/lib/iomgr/resolve_address_uv.c - src/core/lib/iomgr/resolve_address_windows.c - src/core/lib/iomgr/resource_quota.c - src/core/lib/iomgr/sockaddr_utils.c - src/core/lib/iomgr/socket_factory_posix.c - src/core/lib/iomgr/socket_mutator.c - src/core/lib/iomgr/socket_utils_common_posix.c - src/core/lib/iomgr/socket_utils_linux.c - src/core/lib/iomgr/socket_utils_posix.c - src/core/lib/iomgr/socket_utils_uv.c - src/core/lib/iomgr/socket_utils_windows.c - src/core/lib/iomgr/socket_windows.c - src/core/lib/iomgr/tcp_client_posix.c - src/core/lib/iomgr/tcp_client_uv.c - src/core/lib/iomgr/tcp_client_windows.c - src/core/lib/iomgr/tcp_posix.c - src/core/lib/iomgr/tcp_server_posix.c - src/core/lib/iomgr/tcp_server_utils_posix_common.c - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - src/core/lib/iomgr/tcp_server_uv.c - src/core/lib/iomgr/tcp_server_windows.c - src/core/lib/iomgr/tcp_uv.c - src/core/lib/iomgr/tcp_windows.c - src/core/lib/iomgr/time_averaged_stats.c - src/core/lib/iomgr/timer_generic.c - src/core/lib/iomgr/timer_heap.c - src/core/lib/iomgr/timer_manager.c - src/core/lib/iomgr/timer_uv.c - src/core/lib/iomgr/udp_server.c - src/core/lib/iomgr/unix_sockets_posix.c - src/core/lib/iomgr/unix_sockets_posix_noop.c - src/core/lib/iomgr/wakeup_fd_cv.c - src/core/lib/iomgr/wakeup_fd_eventfd.c - src/core/lib/iomgr/wakeup_fd_nospecial.c - src/core/lib/iomgr/wakeup_fd_pipe.c - src/core/lib/iomgr/wakeup_fd_posix.c - src/core/lib/json/json.c - src/core/lib/json/json_reader.c - src/core/lib/json/json_string.c - src/core/lib/json/json_writer.c - src/core/lib/slice/b64.c - src/core/lib/slice/percent_encoding.c - src/core/lib/slice/slice.c - src/core/lib/slice/slice_buffer.c - src/core/lib/slice/slice_hash_table.c - src/core/lib/slice/slice_intern.c - src/core/lib/slice/slice_string_helpers.c - src/core/lib/surface/alarm.c - src/core/lib/surface/api_trace.c - src/core/lib/surface/byte_buffer.c - src/core/lib/surface/byte_buffer_reader.c - src/core/lib/surface/call.c - src/core/lib/surface/call_details.c - src/core/lib/surface/call_log_batch.c - src/core/lib/surface/channel.c - src/core/lib/surface/channel_init.c - src/core/lib/surface/channel_ping.c - src/core/lib/surface/channel_stack_type.c - src/core/lib/surface/completion_queue.c - src/core/lib/surface/completion_queue_factory.c - src/core/lib/surface/event_string.c + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc + test/core/end2end/cq_verifier.cc + test/core/end2end/fixtures/http_proxy_fixture.cc + test/core/end2end/fixtures/proxy.cc + test/core/iomgr/endpoint_tests.cc + test/core/util/debugger_macros.cc + test/core/util/grpc_profiler.cc + test/core/util/memory_counters.cc + test/core/util/mock_endpoint.cc + test/core/util/parse_hexstring.cc + test/core/util/passthru_endpoint.cc + test/core/util/port.cc + test/core/util/port_server_client.cc + test/core/util/slice_splitter.cc + test/core/util/trickle_endpoint.cc + src/core/lib/channel/channel_args.cc + src/core/lib/channel/channel_stack.cc + src/core/lib/channel/channel_stack_builder.cc + src/core/lib/channel/connected_channel.cc + src/core/lib/channel/handshaker.cc + src/core/lib/channel/handshaker_factory.cc + src/core/lib/channel/handshaker_registry.cc + src/core/lib/compression/compression.cc + src/core/lib/compression/message_compress.cc + src/core/lib/compression/stream_compression.cc + src/core/lib/debug/stats.cc + src/core/lib/debug/stats_data.cc + src/core/lib/http/format_request.cc + src/core/lib/http/httpcli.cc + src/core/lib/http/parser.cc + src/core/lib/iomgr/call_combiner.cc + src/core/lib/iomgr/closure.cc + src/core/lib/iomgr/combiner.cc + src/core/lib/iomgr/endpoint.cc + src/core/lib/iomgr/endpoint_pair_posix.cc + src/core/lib/iomgr/endpoint_pair_uv.cc + src/core/lib/iomgr/endpoint_pair_windows.cc + src/core/lib/iomgr/error.cc + src/core/lib/iomgr/ev_epoll1_linux.cc + src/core/lib/iomgr/ev_epollex_linux.cc + src/core/lib/iomgr/ev_epollsig_linux.cc + src/core/lib/iomgr/ev_poll_posix.cc + src/core/lib/iomgr/ev_posix.cc + src/core/lib/iomgr/ev_windows.cc + src/core/lib/iomgr/exec_ctx.cc + src/core/lib/iomgr/executor.cc + src/core/lib/iomgr/gethostname_fallback.cc + src/core/lib/iomgr/gethostname_host_name_max.cc + src/core/lib/iomgr/gethostname_sysconf.cc + src/core/lib/iomgr/iocp_windows.cc + src/core/lib/iomgr/iomgr.cc + src/core/lib/iomgr/iomgr_posix.cc + src/core/lib/iomgr/iomgr_uv.cc + src/core/lib/iomgr/iomgr_windows.cc + src/core/lib/iomgr/is_epollexclusive_available.cc + src/core/lib/iomgr/load_file.cc + src/core/lib/iomgr/lockfree_event.cc + src/core/lib/iomgr/network_status_tracker.cc + src/core/lib/iomgr/polling_entity.cc + src/core/lib/iomgr/pollset_set_uv.cc + src/core/lib/iomgr/pollset_set_windows.cc + src/core/lib/iomgr/pollset_uv.cc + src/core/lib/iomgr/pollset_windows.cc + src/core/lib/iomgr/resolve_address_posix.cc + src/core/lib/iomgr/resolve_address_uv.cc + src/core/lib/iomgr/resolve_address_windows.cc + src/core/lib/iomgr/resource_quota.cc + src/core/lib/iomgr/sockaddr_utils.cc + src/core/lib/iomgr/socket_factory_posix.cc + src/core/lib/iomgr/socket_mutator.cc + src/core/lib/iomgr/socket_utils_common_posix.cc + src/core/lib/iomgr/socket_utils_linux.cc + src/core/lib/iomgr/socket_utils_posix.cc + src/core/lib/iomgr/socket_utils_uv.cc + src/core/lib/iomgr/socket_utils_windows.cc + src/core/lib/iomgr/socket_windows.cc + src/core/lib/iomgr/tcp_client_posix.cc + src/core/lib/iomgr/tcp_client_uv.cc + src/core/lib/iomgr/tcp_client_windows.cc + src/core/lib/iomgr/tcp_posix.cc + src/core/lib/iomgr/tcp_server_posix.cc + src/core/lib/iomgr/tcp_server_utils_posix_common.cc + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + src/core/lib/iomgr/tcp_server_uv.cc + src/core/lib/iomgr/tcp_server_windows.cc + src/core/lib/iomgr/tcp_uv.cc + src/core/lib/iomgr/tcp_windows.cc + src/core/lib/iomgr/time_averaged_stats.cc + src/core/lib/iomgr/timer_generic.cc + src/core/lib/iomgr/timer_heap.cc + src/core/lib/iomgr/timer_manager.cc + src/core/lib/iomgr/timer_uv.cc + src/core/lib/iomgr/udp_server.cc + src/core/lib/iomgr/unix_sockets_posix.cc + src/core/lib/iomgr/unix_sockets_posix_noop.cc + src/core/lib/iomgr/wakeup_fd_cv.cc + src/core/lib/iomgr/wakeup_fd_eventfd.cc + src/core/lib/iomgr/wakeup_fd_nospecial.cc + src/core/lib/iomgr/wakeup_fd_pipe.cc + src/core/lib/iomgr/wakeup_fd_posix.cc + src/core/lib/json/json.cc + src/core/lib/json/json_reader.cc + src/core/lib/json/json_string.cc + src/core/lib/json/json_writer.cc + src/core/lib/slice/b64.cc + src/core/lib/slice/percent_encoding.cc + src/core/lib/slice/slice.cc + src/core/lib/slice/slice_buffer.cc + src/core/lib/slice/slice_hash_table.cc + src/core/lib/slice/slice_intern.cc + src/core/lib/slice/slice_string_helpers.cc + src/core/lib/surface/alarm.cc + src/core/lib/surface/api_trace.cc + src/core/lib/surface/byte_buffer.cc + src/core/lib/surface/byte_buffer_reader.cc + src/core/lib/surface/call.cc + src/core/lib/surface/call_details.cc + src/core/lib/surface/call_log_batch.cc + src/core/lib/surface/channel.cc + src/core/lib/surface/channel_init.cc + src/core/lib/surface/channel_ping.cc + src/core/lib/surface/channel_stack_type.cc + src/core/lib/surface/completion_queue.cc + src/core/lib/surface/completion_queue_factory.cc + src/core/lib/surface/event_string.cc src/core/lib/surface/lame_client.cc - src/core/lib/surface/metadata_array.c - src/core/lib/surface/server.c - src/core/lib/surface/validate_metadata.c - src/core/lib/surface/version.c - src/core/lib/transport/bdp_estimator.c - src/core/lib/transport/byte_stream.c - src/core/lib/transport/connectivity_state.c - src/core/lib/transport/error_utils.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c - src/core/lib/transport/pid_controller.c - src/core/lib/transport/service_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/status_conversion.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c - src/core/lib/transport/transport_op_string.c - src/core/lib/debug/trace.c - src/core/ext/filters/client_channel/channel_connectivity.c - src/core/ext/filters/client_channel/client_channel.c - src/core/ext/filters/client_channel/client_channel_factory.c - src/core/ext/filters/client_channel/client_channel_plugin.c - src/core/ext/filters/client_channel/connector.c - src/core/ext/filters/client_channel/http_connect_handshaker.c - src/core/ext/filters/client_channel/http_proxy.c - src/core/ext/filters/client_channel/lb_policy.c - src/core/ext/filters/client_channel/lb_policy_factory.c - src/core/ext/filters/client_channel/lb_policy_registry.c - src/core/ext/filters/client_channel/parse_address.c - src/core/ext/filters/client_channel/proxy_mapper.c - src/core/ext/filters/client_channel/proxy_mapper_registry.c - src/core/ext/filters/client_channel/resolver.c - src/core/ext/filters/client_channel/resolver_factory.c - src/core/ext/filters/client_channel/resolver_registry.c - src/core/ext/filters/client_channel/retry_throttle.c - src/core/ext/filters/client_channel/subchannel.c - src/core/ext/filters/client_channel/subchannel_index.c - src/core/ext/filters/client_channel/uri_parser.c - src/core/ext/filters/deadline/deadline_filter.c - src/core/ext/transport/chttp2/transport/bin_decoder.c - src/core/ext/transport/chttp2/transport/bin_encoder.c - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - src/core/ext/transport/chttp2/transport/chttp2_transport.c - src/core/ext/transport/chttp2/transport/flow_control.c - src/core/ext/transport/chttp2/transport/frame_data.c - src/core/ext/transport/chttp2/transport/frame_goaway.c - src/core/ext/transport/chttp2/transport/frame_ping.c - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - src/core/ext/transport/chttp2/transport/frame_settings.c - src/core/ext/transport/chttp2/transport/frame_window_update.c - src/core/ext/transport/chttp2/transport/hpack_encoder.c - src/core/ext/transport/chttp2/transport/hpack_parser.c - src/core/ext/transport/chttp2/transport/hpack_table.c - src/core/ext/transport/chttp2/transport/http2_settings.c - src/core/ext/transport/chttp2/transport/huffsyms.c - src/core/ext/transport/chttp2/transport/incoming_metadata.c - src/core/ext/transport/chttp2/transport/parsing.c - src/core/ext/transport/chttp2/transport/stream_lists.c - src/core/ext/transport/chttp2/transport/stream_map.c - src/core/ext/transport/chttp2/transport/varint.c - src/core/ext/transport/chttp2/transport/writing.c - src/core/ext/transport/chttp2/alpn/alpn.c - src/core/ext/filters/http/client/http_client_filter.c - src/core/ext/filters/http/http_filters_plugin.c - src/core/ext/filters/http/message_compress/message_compress_filter.c - src/core/ext/filters/http/server/http_server_filter.c + src/core/lib/surface/metadata_array.cc + src/core/lib/surface/server.cc + src/core/lib/surface/validate_metadata.cc + src/core/lib/surface/version.cc + src/core/lib/transport/bdp_estimator.cc + src/core/lib/transport/byte_stream.cc + src/core/lib/transport/connectivity_state.cc + src/core/lib/transport/error_utils.cc + src/core/lib/transport/metadata.cc + src/core/lib/transport/metadata_batch.cc + src/core/lib/transport/pid_controller.cc + src/core/lib/transport/service_config.cc + src/core/lib/transport/static_metadata.cc + src/core/lib/transport/status_conversion.cc + src/core/lib/transport/timeout_encoding.cc + src/core/lib/transport/transport.cc + src/core/lib/transport/transport_op_string.cc + src/core/lib/debug/trace.cc + src/core/ext/filters/client_channel/channel_connectivity.cc + src/core/ext/filters/client_channel/client_channel.cc + src/core/ext/filters/client_channel/client_channel_factory.cc + src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/connector.cc + src/core/ext/filters/client_channel/http_connect_handshaker.cc + src/core/ext/filters/client_channel/http_proxy.cc + src/core/ext/filters/client_channel/lb_policy.cc + src/core/ext/filters/client_channel/lb_policy_factory.cc + src/core/ext/filters/client_channel/lb_policy_registry.cc + src/core/ext/filters/client_channel/parse_address.cc + src/core/ext/filters/client_channel/proxy_mapper.cc + src/core/ext/filters/client_channel/proxy_mapper_registry.cc + src/core/ext/filters/client_channel/resolver.cc + src/core/ext/filters/client_channel/resolver_factory.cc + src/core/ext/filters/client_channel/resolver_registry.cc + src/core/ext/filters/client_channel/retry_throttle.cc + src/core/ext/filters/client_channel/subchannel.cc + src/core/ext/filters/client_channel/subchannel_index.cc + src/core/ext/filters/client_channel/uri_parser.cc + src/core/ext/filters/deadline/deadline_filter.cc + src/core/ext/transport/chttp2/transport/bin_decoder.cc + src/core/ext/transport/chttp2/transport/bin_encoder.cc + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + src/core/ext/transport/chttp2/transport/chttp2_transport.cc + src/core/ext/transport/chttp2/transport/flow_control.cc + src/core/ext/transport/chttp2/transport/frame_data.cc + src/core/ext/transport/chttp2/transport/frame_goaway.cc + src/core/ext/transport/chttp2/transport/frame_ping.cc + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_settings.cc + src/core/ext/transport/chttp2/transport/frame_window_update.cc + src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_parser.cc + src/core/ext/transport/chttp2/transport/hpack_table.cc + src/core/ext/transport/chttp2/transport/http2_settings.cc + src/core/ext/transport/chttp2/transport/huffsyms.cc + src/core/ext/transport/chttp2/transport/incoming_metadata.cc + src/core/ext/transport/chttp2/transport/parsing.cc + src/core/ext/transport/chttp2/transport/stream_lists.cc + src/core/ext/transport/chttp2/transport/stream_map.cc + src/core/ext/transport/chttp2/transport/varint.cc + src/core/ext/transport/chttp2/transport/writing.cc + src/core/ext/transport/chttp2/alpn/alpn.cc + src/core/ext/filters/http/client/http_client_filter.cc + src/core/ext/filters/http/http_filters_plugin.cc + src/core/ext/filters/http/message_compress/message_compress_filter.cc + src/core/ext/filters/http/server/http_server_filter.cc ) if(WIN32 AND MSVC) @@ -2135,235 +2127,233 @@ endforeach() endif (gRPC_BUILD_TESTS) add_library(grpc_unsecure - src/core/lib/surface/init.c - src/core/lib/surface/init_unsecure.c - src/core/lib/channel/channel_args.c - src/core/lib/channel/channel_stack.c - src/core/lib/channel/channel_stack_builder.c - src/core/lib/channel/connected_channel.c - src/core/lib/channel/handshaker.c - src/core/lib/channel/handshaker_factory.c - src/core/lib/channel/handshaker_registry.c - src/core/lib/compression/compression.c - src/core/lib/compression/message_compress.c - src/core/lib/compression/stream_compression.c - src/core/lib/compression/stream_compression_gzip.c - src/core/lib/compression/stream_compression_identity.c - src/core/lib/debug/stats.c - src/core/lib/debug/stats_data.c - src/core/lib/http/format_request.c - src/core/lib/http/httpcli.c - src/core/lib/http/parser.c - src/core/lib/iomgr/call_combiner.c - src/core/lib/iomgr/closure.c - src/core/lib/iomgr/combiner.c - src/core/lib/iomgr/endpoint.c - src/core/lib/iomgr/endpoint_pair_posix.c - src/core/lib/iomgr/endpoint_pair_uv.c - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c - src/core/lib/iomgr/ev_posix.c - src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/executor.c - src/core/lib/iomgr/gethostname_fallback.c - src/core/lib/iomgr/gethostname_host_name_max.c - src/core/lib/iomgr/gethostname_sysconf.c - src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_uv.c - src/core/lib/iomgr/iomgr_windows.c - src/core/lib/iomgr/is_epollexclusive_available.c - src/core/lib/iomgr/load_file.c - src/core/lib/iomgr/lockfree_event.c - src/core/lib/iomgr/network_status_tracker.c - src/core/lib/iomgr/polling_entity.c - src/core/lib/iomgr/pollset_set_uv.c - src/core/lib/iomgr/pollset_set_windows.c - src/core/lib/iomgr/pollset_uv.c - src/core/lib/iomgr/pollset_windows.c - src/core/lib/iomgr/resolve_address_posix.c - src/core/lib/iomgr/resolve_address_uv.c - src/core/lib/iomgr/resolve_address_windows.c - src/core/lib/iomgr/resource_quota.c - src/core/lib/iomgr/sockaddr_utils.c - src/core/lib/iomgr/socket_factory_posix.c - src/core/lib/iomgr/socket_mutator.c - src/core/lib/iomgr/socket_utils_common_posix.c - src/core/lib/iomgr/socket_utils_linux.c - src/core/lib/iomgr/socket_utils_posix.c - src/core/lib/iomgr/socket_utils_uv.c - src/core/lib/iomgr/socket_utils_windows.c - src/core/lib/iomgr/socket_windows.c - src/core/lib/iomgr/tcp_client_posix.c - src/core/lib/iomgr/tcp_client_uv.c - src/core/lib/iomgr/tcp_client_windows.c - src/core/lib/iomgr/tcp_posix.c - src/core/lib/iomgr/tcp_server_posix.c - src/core/lib/iomgr/tcp_server_utils_posix_common.c - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - src/core/lib/iomgr/tcp_server_uv.c - src/core/lib/iomgr/tcp_server_windows.c - src/core/lib/iomgr/tcp_uv.c - src/core/lib/iomgr/tcp_windows.c - src/core/lib/iomgr/time_averaged_stats.c - src/core/lib/iomgr/timer_generic.c - src/core/lib/iomgr/timer_heap.c - src/core/lib/iomgr/timer_manager.c - src/core/lib/iomgr/timer_uv.c - src/core/lib/iomgr/udp_server.c - src/core/lib/iomgr/unix_sockets_posix.c - src/core/lib/iomgr/unix_sockets_posix_noop.c - src/core/lib/iomgr/wakeup_fd_cv.c - src/core/lib/iomgr/wakeup_fd_eventfd.c - src/core/lib/iomgr/wakeup_fd_nospecial.c - src/core/lib/iomgr/wakeup_fd_pipe.c - src/core/lib/iomgr/wakeup_fd_posix.c - src/core/lib/json/json.c - src/core/lib/json/json_reader.c - src/core/lib/json/json_string.c - src/core/lib/json/json_writer.c - src/core/lib/slice/b64.c - src/core/lib/slice/percent_encoding.c - src/core/lib/slice/slice.c - src/core/lib/slice/slice_buffer.c - src/core/lib/slice/slice_hash_table.c - src/core/lib/slice/slice_intern.c - src/core/lib/slice/slice_string_helpers.c - src/core/lib/surface/alarm.c - src/core/lib/surface/api_trace.c - src/core/lib/surface/byte_buffer.c - src/core/lib/surface/byte_buffer_reader.c - src/core/lib/surface/call.c - src/core/lib/surface/call_details.c - src/core/lib/surface/call_log_batch.c - src/core/lib/surface/channel.c - src/core/lib/surface/channel_init.c - src/core/lib/surface/channel_ping.c - src/core/lib/surface/channel_stack_type.c - src/core/lib/surface/completion_queue.c - src/core/lib/surface/completion_queue_factory.c - src/core/lib/surface/event_string.c + src/core/lib/surface/init.cc + src/core/lib/surface/init_unsecure.cc + src/core/lib/channel/channel_args.cc + src/core/lib/channel/channel_stack.cc + src/core/lib/channel/channel_stack_builder.cc + src/core/lib/channel/connected_channel.cc + src/core/lib/channel/handshaker.cc + src/core/lib/channel/handshaker_factory.cc + src/core/lib/channel/handshaker_registry.cc + src/core/lib/compression/compression.cc + src/core/lib/compression/message_compress.cc + src/core/lib/compression/stream_compression.cc + src/core/lib/debug/stats.cc + src/core/lib/debug/stats_data.cc + src/core/lib/http/format_request.cc + src/core/lib/http/httpcli.cc + src/core/lib/http/parser.cc + src/core/lib/iomgr/call_combiner.cc + src/core/lib/iomgr/closure.cc + src/core/lib/iomgr/combiner.cc + src/core/lib/iomgr/endpoint.cc + src/core/lib/iomgr/endpoint_pair_posix.cc + src/core/lib/iomgr/endpoint_pair_uv.cc + src/core/lib/iomgr/endpoint_pair_windows.cc + src/core/lib/iomgr/error.cc + src/core/lib/iomgr/ev_epoll1_linux.cc + src/core/lib/iomgr/ev_epollex_linux.cc + src/core/lib/iomgr/ev_epollsig_linux.cc + src/core/lib/iomgr/ev_poll_posix.cc + src/core/lib/iomgr/ev_posix.cc + src/core/lib/iomgr/ev_windows.cc + src/core/lib/iomgr/exec_ctx.cc + src/core/lib/iomgr/executor.cc + src/core/lib/iomgr/gethostname_fallback.cc + src/core/lib/iomgr/gethostname_host_name_max.cc + src/core/lib/iomgr/gethostname_sysconf.cc + src/core/lib/iomgr/iocp_windows.cc + src/core/lib/iomgr/iomgr.cc + src/core/lib/iomgr/iomgr_posix.cc + src/core/lib/iomgr/iomgr_uv.cc + src/core/lib/iomgr/iomgr_windows.cc + src/core/lib/iomgr/is_epollexclusive_available.cc + src/core/lib/iomgr/load_file.cc + src/core/lib/iomgr/lockfree_event.cc + src/core/lib/iomgr/network_status_tracker.cc + src/core/lib/iomgr/polling_entity.cc + src/core/lib/iomgr/pollset_set_uv.cc + src/core/lib/iomgr/pollset_set_windows.cc + src/core/lib/iomgr/pollset_uv.cc + src/core/lib/iomgr/pollset_windows.cc + src/core/lib/iomgr/resolve_address_posix.cc + src/core/lib/iomgr/resolve_address_uv.cc + src/core/lib/iomgr/resolve_address_windows.cc + src/core/lib/iomgr/resource_quota.cc + src/core/lib/iomgr/sockaddr_utils.cc + src/core/lib/iomgr/socket_factory_posix.cc + src/core/lib/iomgr/socket_mutator.cc + src/core/lib/iomgr/socket_utils_common_posix.cc + src/core/lib/iomgr/socket_utils_linux.cc + src/core/lib/iomgr/socket_utils_posix.cc + src/core/lib/iomgr/socket_utils_uv.cc + src/core/lib/iomgr/socket_utils_windows.cc + src/core/lib/iomgr/socket_windows.cc + src/core/lib/iomgr/tcp_client_posix.cc + src/core/lib/iomgr/tcp_client_uv.cc + src/core/lib/iomgr/tcp_client_windows.cc + src/core/lib/iomgr/tcp_posix.cc + src/core/lib/iomgr/tcp_server_posix.cc + src/core/lib/iomgr/tcp_server_utils_posix_common.cc + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + src/core/lib/iomgr/tcp_server_uv.cc + src/core/lib/iomgr/tcp_server_windows.cc + src/core/lib/iomgr/tcp_uv.cc + src/core/lib/iomgr/tcp_windows.cc + src/core/lib/iomgr/time_averaged_stats.cc + src/core/lib/iomgr/timer_generic.cc + src/core/lib/iomgr/timer_heap.cc + src/core/lib/iomgr/timer_manager.cc + src/core/lib/iomgr/timer_uv.cc + src/core/lib/iomgr/udp_server.cc + src/core/lib/iomgr/unix_sockets_posix.cc + src/core/lib/iomgr/unix_sockets_posix_noop.cc + src/core/lib/iomgr/wakeup_fd_cv.cc + src/core/lib/iomgr/wakeup_fd_eventfd.cc + src/core/lib/iomgr/wakeup_fd_nospecial.cc + src/core/lib/iomgr/wakeup_fd_pipe.cc + src/core/lib/iomgr/wakeup_fd_posix.cc + src/core/lib/json/json.cc + src/core/lib/json/json_reader.cc + src/core/lib/json/json_string.cc + src/core/lib/json/json_writer.cc + src/core/lib/slice/b64.cc + src/core/lib/slice/percent_encoding.cc + src/core/lib/slice/slice.cc + src/core/lib/slice/slice_buffer.cc + src/core/lib/slice/slice_hash_table.cc + src/core/lib/slice/slice_intern.cc + src/core/lib/slice/slice_string_helpers.cc + src/core/lib/surface/alarm.cc + src/core/lib/surface/api_trace.cc + src/core/lib/surface/byte_buffer.cc + src/core/lib/surface/byte_buffer_reader.cc + src/core/lib/surface/call.cc + src/core/lib/surface/call_details.cc + src/core/lib/surface/call_log_batch.cc + src/core/lib/surface/channel.cc + src/core/lib/surface/channel_init.cc + src/core/lib/surface/channel_ping.cc + src/core/lib/surface/channel_stack_type.cc + src/core/lib/surface/completion_queue.cc + src/core/lib/surface/completion_queue_factory.cc + src/core/lib/surface/event_string.cc src/core/lib/surface/lame_client.cc - src/core/lib/surface/metadata_array.c - src/core/lib/surface/server.c - src/core/lib/surface/validate_metadata.c - src/core/lib/surface/version.c - src/core/lib/transport/bdp_estimator.c - src/core/lib/transport/byte_stream.c - src/core/lib/transport/connectivity_state.c - src/core/lib/transport/error_utils.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c - src/core/lib/transport/pid_controller.c - src/core/lib/transport/service_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/status_conversion.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c - src/core/lib/transport/transport_op_string.c - src/core/lib/debug/trace.c - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c - src/core/ext/transport/chttp2/transport/bin_decoder.c - src/core/ext/transport/chttp2/transport/bin_encoder.c - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - src/core/ext/transport/chttp2/transport/chttp2_transport.c - src/core/ext/transport/chttp2/transport/flow_control.c - src/core/ext/transport/chttp2/transport/frame_data.c - src/core/ext/transport/chttp2/transport/frame_goaway.c - src/core/ext/transport/chttp2/transport/frame_ping.c - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - src/core/ext/transport/chttp2/transport/frame_settings.c - src/core/ext/transport/chttp2/transport/frame_window_update.c - src/core/ext/transport/chttp2/transport/hpack_encoder.c - src/core/ext/transport/chttp2/transport/hpack_parser.c - src/core/ext/transport/chttp2/transport/hpack_table.c - src/core/ext/transport/chttp2/transport/http2_settings.c - src/core/ext/transport/chttp2/transport/huffsyms.c - src/core/ext/transport/chttp2/transport/incoming_metadata.c - src/core/ext/transport/chttp2/transport/parsing.c - src/core/ext/transport/chttp2/transport/stream_lists.c - src/core/ext/transport/chttp2/transport/stream_map.c - src/core/ext/transport/chttp2/transport/varint.c - src/core/ext/transport/chttp2/transport/writing.c - src/core/ext/transport/chttp2/alpn/alpn.c - src/core/ext/filters/http/client/http_client_filter.c - src/core/ext/filters/http/http_filters_plugin.c - src/core/ext/filters/http/message_compress/message_compress_filter.c - src/core/ext/filters/http/server/http_server_filter.c - src/core/ext/transport/chttp2/server/chttp2_server.c - src/core/ext/transport/chttp2/client/insecure/channel_create.c - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c - src/core/ext/transport/chttp2/client/chttp2_connector.c - src/core/ext/filters/client_channel/channel_connectivity.c - src/core/ext/filters/client_channel/client_channel.c - src/core/ext/filters/client_channel/client_channel_factory.c - src/core/ext/filters/client_channel/client_channel_plugin.c - src/core/ext/filters/client_channel/connector.c - src/core/ext/filters/client_channel/http_connect_handshaker.c - src/core/ext/filters/client_channel/http_proxy.c - src/core/ext/filters/client_channel/lb_policy.c - src/core/ext/filters/client_channel/lb_policy_factory.c - src/core/ext/filters/client_channel/lb_policy_registry.c - src/core/ext/filters/client_channel/parse_address.c - src/core/ext/filters/client_channel/proxy_mapper.c - src/core/ext/filters/client_channel/proxy_mapper_registry.c - src/core/ext/filters/client_channel/resolver.c - src/core/ext/filters/client_channel/resolver_factory.c - src/core/ext/filters/client_channel/resolver_registry.c - src/core/ext/filters/client_channel/retry_throttle.c - src/core/ext/filters/client_channel/subchannel.c - src/core/ext/filters/client_channel/subchannel_index.c - src/core/ext/filters/client_channel/uri_parser.c - src/core/ext/filters/deadline/deadline_filter.c - src/core/ext/transport/inproc/inproc_plugin.c - src/core/ext/transport/inproc/inproc_transport.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c - src/core/ext/filters/load_reporting/server_load_reporting_filter.c - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c + src/core/lib/surface/metadata_array.cc + src/core/lib/surface/server.cc + src/core/lib/surface/validate_metadata.cc + src/core/lib/surface/version.cc + src/core/lib/transport/bdp_estimator.cc + src/core/lib/transport/byte_stream.cc + src/core/lib/transport/connectivity_state.cc + src/core/lib/transport/error_utils.cc + src/core/lib/transport/metadata.cc + src/core/lib/transport/metadata_batch.cc + src/core/lib/transport/pid_controller.cc + src/core/lib/transport/service_config.cc + src/core/lib/transport/static_metadata.cc + src/core/lib/transport/status_conversion.cc + src/core/lib/transport/timeout_encoding.cc + src/core/lib/transport/transport.cc + src/core/lib/transport/transport_op_string.cc + src/core/lib/debug/trace.cc + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc + src/core/ext/transport/chttp2/transport/bin_decoder.cc + src/core/ext/transport/chttp2/transport/bin_encoder.cc + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + src/core/ext/transport/chttp2/transport/chttp2_transport.cc + src/core/ext/transport/chttp2/transport/flow_control.cc + src/core/ext/transport/chttp2/transport/frame_data.cc + src/core/ext/transport/chttp2/transport/frame_goaway.cc + src/core/ext/transport/chttp2/transport/frame_ping.cc + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_settings.cc + src/core/ext/transport/chttp2/transport/frame_window_update.cc + src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_parser.cc + src/core/ext/transport/chttp2/transport/hpack_table.cc + src/core/ext/transport/chttp2/transport/http2_settings.cc + src/core/ext/transport/chttp2/transport/huffsyms.cc + src/core/ext/transport/chttp2/transport/incoming_metadata.cc + src/core/ext/transport/chttp2/transport/parsing.cc + src/core/ext/transport/chttp2/transport/stream_lists.cc + src/core/ext/transport/chttp2/transport/stream_map.cc + src/core/ext/transport/chttp2/transport/varint.cc + src/core/ext/transport/chttp2/transport/writing.cc + src/core/ext/transport/chttp2/alpn/alpn.cc + src/core/ext/filters/http/client/http_client_filter.cc + src/core/ext/filters/http/http_filters_plugin.cc + src/core/ext/filters/http/message_compress/message_compress_filter.cc + src/core/ext/filters/http/server/http_server_filter.cc + src/core/ext/transport/chttp2/server/chttp2_server.cc + src/core/ext/transport/chttp2/client/insecure/channel_create.cc + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc + src/core/ext/transport/chttp2/client/chttp2_connector.cc + src/core/ext/filters/client_channel/channel_connectivity.cc + src/core/ext/filters/client_channel/client_channel.cc + src/core/ext/filters/client_channel/client_channel_factory.cc + src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/connector.cc + src/core/ext/filters/client_channel/http_connect_handshaker.cc + src/core/ext/filters/client_channel/http_proxy.cc + src/core/ext/filters/client_channel/lb_policy.cc + src/core/ext/filters/client_channel/lb_policy_factory.cc + src/core/ext/filters/client_channel/lb_policy_registry.cc + src/core/ext/filters/client_channel/parse_address.cc + src/core/ext/filters/client_channel/proxy_mapper.cc + src/core/ext/filters/client_channel/proxy_mapper_registry.cc + src/core/ext/filters/client_channel/resolver.cc + src/core/ext/filters/client_channel/resolver_factory.cc + src/core/ext/filters/client_channel/resolver_registry.cc + src/core/ext/filters/client_channel/retry_throttle.cc + src/core/ext/filters/client_channel/subchannel.cc + src/core/ext/filters/client_channel/subchannel_index.cc + src/core/ext/filters/client_channel/uri_parser.cc + src/core/ext/filters/deadline/deadline_filter.cc + src/core/ext/transport/inproc/inproc_plugin.cc + src/core/ext/transport/inproc/inproc_transport.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc + src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc + src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc third_party/nanopb/pb_common.c third_party/nanopb/pb_decode.c third_party/nanopb/pb_encode.c - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c - src/core/ext/census/base_resources.c - src/core/ext/census/context.c - src/core/ext/census/gen/census.pb.c - src/core/ext/census/gen/trace_context.pb.c - src/core/ext/census/grpc_context.c - 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 - src/core/ext/census/resource.c - src/core/ext/census/trace_context.c - src/core/ext/census/tracing.c - src/core/ext/filters/max_age/max_age_filter.c - src/core/ext/filters/message_size/message_size_filter.c - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c - src/core/ext/filters/workarounds/workaround_utils.c + src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc + src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc + src/core/ext/census/base_resources.cc + src/core/ext/census/context.cc + src/core/ext/census/gen/census.pb.cc + src/core/ext/census/gen/trace_context.pb.cc + src/core/ext/census/grpc_context.cc + src/core/ext/census/grpc_filter.cc + src/core/ext/census/grpc_plugin.cc + src/core/ext/census/initialize.cc + src/core/ext/census/intrusive_hash_map.cc + src/core/ext/census/mlog.cc + src/core/ext/census/operation.cc + src/core/ext/census/placeholders.cc + src/core/ext/census/resource.cc + src/core/ext/census/trace_context.cc + src/core/ext/census/tracing.cc + src/core/ext/filters/max_age/max_age_filter.cc + src/core/ext/filters/message_size/message_size_filter.cc + src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc + src/core/ext/filters/workarounds/workaround_utils.cc src/core/plugin_registry/grpc_unsecure_plugin_registry.cc ) @@ -2869,207 +2859,205 @@ add_library(grpc++_cronet src/cpp/util/string_ref.cc src/cpp/util/time_cc.cc src/cpp/codegen/codegen_init.cc - src/core/ext/transport/chttp2/client/insecure/channel_create.c - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c - src/core/ext/transport/chttp2/client/chttp2_connector.c - src/core/ext/transport/chttp2/transport/bin_decoder.c - src/core/ext/transport/chttp2/transport/bin_encoder.c - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - src/core/ext/transport/chttp2/transport/chttp2_transport.c - src/core/ext/transport/chttp2/transport/flow_control.c - src/core/ext/transport/chttp2/transport/frame_data.c - src/core/ext/transport/chttp2/transport/frame_goaway.c - src/core/ext/transport/chttp2/transport/frame_ping.c - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - src/core/ext/transport/chttp2/transport/frame_settings.c - src/core/ext/transport/chttp2/transport/frame_window_update.c - src/core/ext/transport/chttp2/transport/hpack_encoder.c - src/core/ext/transport/chttp2/transport/hpack_parser.c - src/core/ext/transport/chttp2/transport/hpack_table.c - src/core/ext/transport/chttp2/transport/http2_settings.c - src/core/ext/transport/chttp2/transport/huffsyms.c - src/core/ext/transport/chttp2/transport/incoming_metadata.c - src/core/ext/transport/chttp2/transport/parsing.c - src/core/ext/transport/chttp2/transport/stream_lists.c - src/core/ext/transport/chttp2/transport/stream_map.c - src/core/ext/transport/chttp2/transport/varint.c - src/core/ext/transport/chttp2/transport/writing.c - src/core/lib/channel/channel_args.c - src/core/lib/channel/channel_stack.c - src/core/lib/channel/channel_stack_builder.c - src/core/lib/channel/connected_channel.c - src/core/lib/channel/handshaker.c - src/core/lib/channel/handshaker_factory.c - src/core/lib/channel/handshaker_registry.c - src/core/lib/compression/compression.c - src/core/lib/compression/message_compress.c - src/core/lib/compression/stream_compression.c - src/core/lib/compression/stream_compression_gzip.c - src/core/lib/compression/stream_compression_identity.c - src/core/lib/debug/stats.c - src/core/lib/debug/stats_data.c - src/core/lib/http/format_request.c - src/core/lib/http/httpcli.c - src/core/lib/http/parser.c - src/core/lib/iomgr/call_combiner.c - src/core/lib/iomgr/closure.c - src/core/lib/iomgr/combiner.c - src/core/lib/iomgr/endpoint.c - src/core/lib/iomgr/endpoint_pair_posix.c - src/core/lib/iomgr/endpoint_pair_uv.c - src/core/lib/iomgr/endpoint_pair_windows.c - src/core/lib/iomgr/error.c - src/core/lib/iomgr/ev_epoll1_linux.c - src/core/lib/iomgr/ev_epollex_linux.c - src/core/lib/iomgr/ev_epollsig_linux.c - src/core/lib/iomgr/ev_poll_posix.c - src/core/lib/iomgr/ev_posix.c - src/core/lib/iomgr/ev_windows.c - src/core/lib/iomgr/exec_ctx.c - src/core/lib/iomgr/executor.c - src/core/lib/iomgr/gethostname_fallback.c - src/core/lib/iomgr/gethostname_host_name_max.c - src/core/lib/iomgr/gethostname_sysconf.c - src/core/lib/iomgr/iocp_windows.c - src/core/lib/iomgr/iomgr.c - src/core/lib/iomgr/iomgr_posix.c - src/core/lib/iomgr/iomgr_uv.c - src/core/lib/iomgr/iomgr_windows.c - src/core/lib/iomgr/is_epollexclusive_available.c - src/core/lib/iomgr/load_file.c - src/core/lib/iomgr/lockfree_event.c - src/core/lib/iomgr/network_status_tracker.c - src/core/lib/iomgr/polling_entity.c - src/core/lib/iomgr/pollset_set_uv.c - src/core/lib/iomgr/pollset_set_windows.c - src/core/lib/iomgr/pollset_uv.c - src/core/lib/iomgr/pollset_windows.c - src/core/lib/iomgr/resolve_address_posix.c - src/core/lib/iomgr/resolve_address_uv.c - src/core/lib/iomgr/resolve_address_windows.c - src/core/lib/iomgr/resource_quota.c - src/core/lib/iomgr/sockaddr_utils.c - src/core/lib/iomgr/socket_factory_posix.c - src/core/lib/iomgr/socket_mutator.c - src/core/lib/iomgr/socket_utils_common_posix.c - src/core/lib/iomgr/socket_utils_linux.c - src/core/lib/iomgr/socket_utils_posix.c - src/core/lib/iomgr/socket_utils_uv.c - src/core/lib/iomgr/socket_utils_windows.c - src/core/lib/iomgr/socket_windows.c - src/core/lib/iomgr/tcp_client_posix.c - src/core/lib/iomgr/tcp_client_uv.c - src/core/lib/iomgr/tcp_client_windows.c - src/core/lib/iomgr/tcp_posix.c - src/core/lib/iomgr/tcp_server_posix.c - src/core/lib/iomgr/tcp_server_utils_posix_common.c - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - src/core/lib/iomgr/tcp_server_uv.c - src/core/lib/iomgr/tcp_server_windows.c - src/core/lib/iomgr/tcp_uv.c - src/core/lib/iomgr/tcp_windows.c - src/core/lib/iomgr/time_averaged_stats.c - src/core/lib/iomgr/timer_generic.c - src/core/lib/iomgr/timer_heap.c - src/core/lib/iomgr/timer_manager.c - src/core/lib/iomgr/timer_uv.c - src/core/lib/iomgr/udp_server.c - src/core/lib/iomgr/unix_sockets_posix.c - src/core/lib/iomgr/unix_sockets_posix_noop.c - src/core/lib/iomgr/wakeup_fd_cv.c - src/core/lib/iomgr/wakeup_fd_eventfd.c - src/core/lib/iomgr/wakeup_fd_nospecial.c - src/core/lib/iomgr/wakeup_fd_pipe.c - src/core/lib/iomgr/wakeup_fd_posix.c - src/core/lib/json/json.c - src/core/lib/json/json_reader.c - src/core/lib/json/json_string.c - src/core/lib/json/json_writer.c - src/core/lib/slice/b64.c - src/core/lib/slice/percent_encoding.c - src/core/lib/slice/slice.c - src/core/lib/slice/slice_buffer.c - src/core/lib/slice/slice_hash_table.c - src/core/lib/slice/slice_intern.c - src/core/lib/slice/slice_string_helpers.c - src/core/lib/surface/alarm.c - src/core/lib/surface/api_trace.c - src/core/lib/surface/byte_buffer.c - src/core/lib/surface/byte_buffer_reader.c - src/core/lib/surface/call.c - src/core/lib/surface/call_details.c - src/core/lib/surface/call_log_batch.c - src/core/lib/surface/channel.c - src/core/lib/surface/channel_init.c - src/core/lib/surface/channel_ping.c - src/core/lib/surface/channel_stack_type.c - src/core/lib/surface/completion_queue.c - src/core/lib/surface/completion_queue_factory.c - src/core/lib/surface/event_string.c + src/core/ext/transport/chttp2/client/insecure/channel_create.cc + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc + src/core/ext/transport/chttp2/client/chttp2_connector.cc + src/core/ext/transport/chttp2/transport/bin_decoder.cc + src/core/ext/transport/chttp2/transport/bin_encoder.cc + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + src/core/ext/transport/chttp2/transport/chttp2_transport.cc + src/core/ext/transport/chttp2/transport/flow_control.cc + src/core/ext/transport/chttp2/transport/frame_data.cc + src/core/ext/transport/chttp2/transport/frame_goaway.cc + src/core/ext/transport/chttp2/transport/frame_ping.cc + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + src/core/ext/transport/chttp2/transport/frame_settings.cc + src/core/ext/transport/chttp2/transport/frame_window_update.cc + src/core/ext/transport/chttp2/transport/hpack_encoder.cc + src/core/ext/transport/chttp2/transport/hpack_parser.cc + src/core/ext/transport/chttp2/transport/hpack_table.cc + src/core/ext/transport/chttp2/transport/http2_settings.cc + src/core/ext/transport/chttp2/transport/huffsyms.cc + src/core/ext/transport/chttp2/transport/incoming_metadata.cc + src/core/ext/transport/chttp2/transport/parsing.cc + src/core/ext/transport/chttp2/transport/stream_lists.cc + src/core/ext/transport/chttp2/transport/stream_map.cc + src/core/ext/transport/chttp2/transport/varint.cc + src/core/ext/transport/chttp2/transport/writing.cc + src/core/lib/channel/channel_args.cc + src/core/lib/channel/channel_stack.cc + src/core/lib/channel/channel_stack_builder.cc + src/core/lib/channel/connected_channel.cc + src/core/lib/channel/handshaker.cc + src/core/lib/channel/handshaker_factory.cc + src/core/lib/channel/handshaker_registry.cc + src/core/lib/compression/compression.cc + src/core/lib/compression/message_compress.cc + src/core/lib/compression/stream_compression.cc + src/core/lib/debug/stats.cc + src/core/lib/debug/stats_data.cc + src/core/lib/http/format_request.cc + src/core/lib/http/httpcli.cc + src/core/lib/http/parser.cc + src/core/lib/iomgr/call_combiner.cc + src/core/lib/iomgr/closure.cc + src/core/lib/iomgr/combiner.cc + src/core/lib/iomgr/endpoint.cc + src/core/lib/iomgr/endpoint_pair_posix.cc + src/core/lib/iomgr/endpoint_pair_uv.cc + src/core/lib/iomgr/endpoint_pair_windows.cc + src/core/lib/iomgr/error.cc + src/core/lib/iomgr/ev_epoll1_linux.cc + src/core/lib/iomgr/ev_epollex_linux.cc + src/core/lib/iomgr/ev_epollsig_linux.cc + src/core/lib/iomgr/ev_poll_posix.cc + src/core/lib/iomgr/ev_posix.cc + src/core/lib/iomgr/ev_windows.cc + src/core/lib/iomgr/exec_ctx.cc + src/core/lib/iomgr/executor.cc + src/core/lib/iomgr/gethostname_fallback.cc + src/core/lib/iomgr/gethostname_host_name_max.cc + src/core/lib/iomgr/gethostname_sysconf.cc + src/core/lib/iomgr/iocp_windows.cc + src/core/lib/iomgr/iomgr.cc + src/core/lib/iomgr/iomgr_posix.cc + src/core/lib/iomgr/iomgr_uv.cc + src/core/lib/iomgr/iomgr_windows.cc + src/core/lib/iomgr/is_epollexclusive_available.cc + src/core/lib/iomgr/load_file.cc + src/core/lib/iomgr/lockfree_event.cc + src/core/lib/iomgr/network_status_tracker.cc + src/core/lib/iomgr/polling_entity.cc + src/core/lib/iomgr/pollset_set_uv.cc + src/core/lib/iomgr/pollset_set_windows.cc + src/core/lib/iomgr/pollset_uv.cc + src/core/lib/iomgr/pollset_windows.cc + src/core/lib/iomgr/resolve_address_posix.cc + src/core/lib/iomgr/resolve_address_uv.cc + src/core/lib/iomgr/resolve_address_windows.cc + src/core/lib/iomgr/resource_quota.cc + src/core/lib/iomgr/sockaddr_utils.cc + src/core/lib/iomgr/socket_factory_posix.cc + src/core/lib/iomgr/socket_mutator.cc + src/core/lib/iomgr/socket_utils_common_posix.cc + src/core/lib/iomgr/socket_utils_linux.cc + src/core/lib/iomgr/socket_utils_posix.cc + src/core/lib/iomgr/socket_utils_uv.cc + src/core/lib/iomgr/socket_utils_windows.cc + src/core/lib/iomgr/socket_windows.cc + src/core/lib/iomgr/tcp_client_posix.cc + src/core/lib/iomgr/tcp_client_uv.cc + src/core/lib/iomgr/tcp_client_windows.cc + src/core/lib/iomgr/tcp_posix.cc + src/core/lib/iomgr/tcp_server_posix.cc + src/core/lib/iomgr/tcp_server_utils_posix_common.cc + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + src/core/lib/iomgr/tcp_server_uv.cc + src/core/lib/iomgr/tcp_server_windows.cc + src/core/lib/iomgr/tcp_uv.cc + src/core/lib/iomgr/tcp_windows.cc + src/core/lib/iomgr/time_averaged_stats.cc + src/core/lib/iomgr/timer_generic.cc + src/core/lib/iomgr/timer_heap.cc + src/core/lib/iomgr/timer_manager.cc + src/core/lib/iomgr/timer_uv.cc + src/core/lib/iomgr/udp_server.cc + src/core/lib/iomgr/unix_sockets_posix.cc + src/core/lib/iomgr/unix_sockets_posix_noop.cc + src/core/lib/iomgr/wakeup_fd_cv.cc + src/core/lib/iomgr/wakeup_fd_eventfd.cc + src/core/lib/iomgr/wakeup_fd_nospecial.cc + src/core/lib/iomgr/wakeup_fd_pipe.cc + src/core/lib/iomgr/wakeup_fd_posix.cc + src/core/lib/json/json.cc + src/core/lib/json/json_reader.cc + src/core/lib/json/json_string.cc + src/core/lib/json/json_writer.cc + src/core/lib/slice/b64.cc + src/core/lib/slice/percent_encoding.cc + src/core/lib/slice/slice.cc + src/core/lib/slice/slice_buffer.cc + src/core/lib/slice/slice_hash_table.cc + src/core/lib/slice/slice_intern.cc + src/core/lib/slice/slice_string_helpers.cc + src/core/lib/surface/alarm.cc + src/core/lib/surface/api_trace.cc + src/core/lib/surface/byte_buffer.cc + src/core/lib/surface/byte_buffer_reader.cc + src/core/lib/surface/call.cc + src/core/lib/surface/call_details.cc + src/core/lib/surface/call_log_batch.cc + src/core/lib/surface/channel.cc + src/core/lib/surface/channel_init.cc + src/core/lib/surface/channel_ping.cc + src/core/lib/surface/channel_stack_type.cc + src/core/lib/surface/completion_queue.cc + src/core/lib/surface/completion_queue_factory.cc + src/core/lib/surface/event_string.cc src/core/lib/surface/lame_client.cc - src/core/lib/surface/metadata_array.c - src/core/lib/surface/server.c - src/core/lib/surface/validate_metadata.c - src/core/lib/surface/version.c - src/core/lib/transport/bdp_estimator.c - src/core/lib/transport/byte_stream.c - src/core/lib/transport/connectivity_state.c - src/core/lib/transport/error_utils.c - src/core/lib/transport/metadata.c - src/core/lib/transport/metadata_batch.c - src/core/lib/transport/pid_controller.c - src/core/lib/transport/service_config.c - src/core/lib/transport/static_metadata.c - src/core/lib/transport/status_conversion.c - src/core/lib/transport/timeout_encoding.c - src/core/lib/transport/transport.c - src/core/lib/transport/transport_op_string.c - src/core/lib/debug/trace.c - src/core/ext/transport/chttp2/alpn/alpn.c - src/core/ext/filters/http/client/http_client_filter.c - src/core/ext/filters/http/http_filters_plugin.c - src/core/ext/filters/http/message_compress/message_compress_filter.c - src/core/ext/filters/http/server/http_server_filter.c - src/core/ext/filters/client_channel/channel_connectivity.c - src/core/ext/filters/client_channel/client_channel.c - src/core/ext/filters/client_channel/client_channel_factory.c - src/core/ext/filters/client_channel/client_channel_plugin.c - src/core/ext/filters/client_channel/connector.c - src/core/ext/filters/client_channel/http_connect_handshaker.c - src/core/ext/filters/client_channel/http_proxy.c - src/core/ext/filters/client_channel/lb_policy.c - src/core/ext/filters/client_channel/lb_policy_factory.c - src/core/ext/filters/client_channel/lb_policy_registry.c - src/core/ext/filters/client_channel/parse_address.c - src/core/ext/filters/client_channel/proxy_mapper.c - src/core/ext/filters/client_channel/proxy_mapper_registry.c - src/core/ext/filters/client_channel/resolver.c - src/core/ext/filters/client_channel/resolver_factory.c - src/core/ext/filters/client_channel/resolver_registry.c - src/core/ext/filters/client_channel/retry_throttle.c - src/core/ext/filters/client_channel/subchannel.c - src/core/ext/filters/client_channel/subchannel_index.c - src/core/ext/filters/client_channel/uri_parser.c - src/core/ext/filters/deadline/deadline_filter.c - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c - src/core/ext/transport/chttp2/server/chttp2_server.c - src/core/ext/census/base_resources.c - src/core/ext/census/context.c - src/core/ext/census/gen/census.pb.c - src/core/ext/census/gen/trace_context.pb.c - src/core/ext/census/grpc_context.c - 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 - src/core/ext/census/resource.c - src/core/ext/census/trace_context.c - src/core/ext/census/tracing.c + src/core/lib/surface/metadata_array.cc + src/core/lib/surface/server.cc + src/core/lib/surface/validate_metadata.cc + src/core/lib/surface/version.cc + src/core/lib/transport/bdp_estimator.cc + src/core/lib/transport/byte_stream.cc + src/core/lib/transport/connectivity_state.cc + src/core/lib/transport/error_utils.cc + src/core/lib/transport/metadata.cc + src/core/lib/transport/metadata_batch.cc + src/core/lib/transport/pid_controller.cc + src/core/lib/transport/service_config.cc + src/core/lib/transport/static_metadata.cc + src/core/lib/transport/status_conversion.cc + src/core/lib/transport/timeout_encoding.cc + src/core/lib/transport/transport.cc + src/core/lib/transport/transport_op_string.cc + src/core/lib/debug/trace.cc + src/core/ext/transport/chttp2/alpn/alpn.cc + src/core/ext/filters/http/client/http_client_filter.cc + src/core/ext/filters/http/http_filters_plugin.cc + src/core/ext/filters/http/message_compress/message_compress_filter.cc + src/core/ext/filters/http/server/http_server_filter.cc + src/core/ext/filters/client_channel/channel_connectivity.cc + src/core/ext/filters/client_channel/client_channel.cc + src/core/ext/filters/client_channel/client_channel_factory.cc + src/core/ext/filters/client_channel/client_channel_plugin.cc + src/core/ext/filters/client_channel/connector.cc + src/core/ext/filters/client_channel/http_connect_handshaker.cc + src/core/ext/filters/client_channel/http_proxy.cc + src/core/ext/filters/client_channel/lb_policy.cc + src/core/ext/filters/client_channel/lb_policy_factory.cc + src/core/ext/filters/client_channel/lb_policy_registry.cc + src/core/ext/filters/client_channel/parse_address.cc + src/core/ext/filters/client_channel/proxy_mapper.cc + src/core/ext/filters/client_channel/proxy_mapper_registry.cc + src/core/ext/filters/client_channel/resolver.cc + src/core/ext/filters/client_channel/resolver_factory.cc + src/core/ext/filters/client_channel/resolver_registry.cc + src/core/ext/filters/client_channel/retry_throttle.cc + src/core/ext/filters/client_channel/subchannel.cc + src/core/ext/filters/client_channel/subchannel_index.cc + src/core/ext/filters/client_channel/uri_parser.cc + src/core/ext/filters/deadline/deadline_filter.cc + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc + src/core/ext/transport/chttp2/server/chttp2_server.cc + src/core/ext/census/base_resources.cc + src/core/ext/census/context.cc + src/core/ext/census/gen/census.pb.cc + src/core/ext/census/gen/trace_context.pb.cc + src/core/ext/census/grpc_context.cc + src/core/ext/census/grpc_filter.cc + src/core/ext/census/grpc_plugin.cc + src/core/ext/census/initialize.cc + src/core/ext/census/intrusive_hash_map.cc + src/core/ext/census/mlog.cc + src/core/ext/census/operation.cc + src/core/ext/census/placeholders.cc + src/core/ext/census/resource.cc + src/core/ext/census/trace_context.cc + src/core/ext/census/tracing.cc third_party/nanopb/pb_common.c third_party/nanopb/pb_decode.c third_party/nanopb/pb_encode.c diff --git a/Makefile b/Makefile index 7f657ef289..d846e9a43d 100644 --- a/Makefile +++ b/Makefile @@ -2794,52 +2794,52 @@ clean: LIBGPR_SRC = \ - src/core/lib/profiling/basic_timers.c \ - src/core/lib/profiling/stap_timers.c \ - src/core/lib/support/alloc.c \ - src/core/lib/support/arena.c \ - src/core/lib/support/atm.c \ - src/core/lib/support/avl.c \ - src/core/lib/support/backoff.c \ - src/core/lib/support/cmdline.c \ - src/core/lib/support/cpu_iphone.c \ - src/core/lib/support/cpu_linux.c \ - src/core/lib/support/cpu_posix.c \ - src/core/lib/support/cpu_windows.c \ - src/core/lib/support/env_linux.c \ - src/core/lib/support/env_posix.c \ - src/core/lib/support/env_windows.c \ - src/core/lib/support/histogram.c \ - src/core/lib/support/host_port.c \ - src/core/lib/support/log.c \ - src/core/lib/support/log_android.c \ - src/core/lib/support/log_linux.c \ - src/core/lib/support/log_posix.c \ - src/core/lib/support/log_windows.c \ - src/core/lib/support/mpscq.c \ - src/core/lib/support/murmur_hash.c \ - src/core/lib/support/stack_lockfree.c \ - src/core/lib/support/string.c \ - src/core/lib/support/string_posix.c \ - src/core/lib/support/string_util_windows.c \ - src/core/lib/support/string_windows.c \ - src/core/lib/support/subprocess_posix.c \ - src/core/lib/support/subprocess_windows.c \ - src/core/lib/support/sync.c \ - src/core/lib/support/sync_posix.c \ - src/core/lib/support/sync_windows.c \ - src/core/lib/support/thd.c \ - src/core/lib/support/thd_posix.c \ - src/core/lib/support/thd_windows.c \ - src/core/lib/support/time.c \ - src/core/lib/support/time_posix.c \ - src/core/lib/support/time_precise.c \ - src/core/lib/support/time_windows.c \ - src/core/lib/support/tls_pthread.c \ - src/core/lib/support/tmpfile_msys.c \ - src/core/lib/support/tmpfile_posix.c \ - src/core/lib/support/tmpfile_windows.c \ - src/core/lib/support/wrap_memcpy.c \ + src/core/lib/profiling/basic_timers.cc \ + src/core/lib/profiling/stap_timers.cc \ + src/core/lib/support/alloc.cc \ + src/core/lib/support/arena.cc \ + src/core/lib/support/atm.cc \ + src/core/lib/support/avl.cc \ + src/core/lib/support/backoff.cc \ + src/core/lib/support/cmdline.cc \ + src/core/lib/support/cpu_iphone.cc \ + src/core/lib/support/cpu_linux.cc \ + src/core/lib/support/cpu_posix.cc \ + src/core/lib/support/cpu_windows.cc \ + src/core/lib/support/env_linux.cc \ + src/core/lib/support/env_posix.cc \ + src/core/lib/support/env_windows.cc \ + src/core/lib/support/histogram.cc \ + src/core/lib/support/host_port.cc \ + src/core/lib/support/log.cc \ + src/core/lib/support/log_android.cc \ + src/core/lib/support/log_linux.cc \ + src/core/lib/support/log_posix.cc \ + src/core/lib/support/log_windows.cc \ + src/core/lib/support/mpscq.cc \ + src/core/lib/support/murmur_hash.cc \ + src/core/lib/support/stack_lockfree.cc \ + src/core/lib/support/string.cc \ + src/core/lib/support/string_posix.cc \ + src/core/lib/support/string_util_windows.cc \ + src/core/lib/support/string_windows.cc \ + src/core/lib/support/subprocess_posix.cc \ + src/core/lib/support/subprocess_windows.cc \ + src/core/lib/support/sync.cc \ + src/core/lib/support/sync_posix.cc \ + src/core/lib/support/sync_windows.cc \ + src/core/lib/support/thd.cc \ + src/core/lib/support/thd_posix.cc \ + src/core/lib/support/thd_windows.cc \ + src/core/lib/support/time.cc \ + src/core/lib/support/time_posix.cc \ + src/core/lib/support/time_precise.cc \ + src/core/lib/support/time_windows.cc \ + src/core/lib/support/tls_pthread.cc \ + src/core/lib/support/tmpfile_msys.cc \ + src/core/lib/support/tmpfile_posix.cc \ + src/core/lib/support/tmpfile_windows.cc \ + src/core/lib/support/wrap_memcpy.cc \ PUBLIC_HEADERS_C += \ include/grpc/support/alloc.h \ @@ -2945,266 +2945,264 @@ endif LIBGRPC_SRC = \ - src/core/lib/surface/init.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/lib/surface/init.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ - src/core/lib/http/httpcli_security_connector.c \ - src/core/lib/security/context/security_context.c \ - src/core/lib/security/credentials/composite/composite_credentials.c \ - src/core/lib/security/credentials/credentials.c \ - src/core/lib/security/credentials/credentials_metadata.c \ - src/core/lib/security/credentials/fake/fake_credentials.c \ - src/core/lib/security/credentials/google_default/credentials_generic.c \ - src/core/lib/security/credentials/google_default/google_default_credentials.c \ - src/core/lib/security/credentials/iam/iam_credentials.c \ - src/core/lib/security/credentials/jwt/json_token.c \ - src/core/lib/security/credentials/jwt/jwt_credentials.c \ - src/core/lib/security/credentials/jwt/jwt_verifier.c \ - src/core/lib/security/credentials/oauth2/oauth2_credentials.c \ - src/core/lib/security/credentials/plugin/plugin_credentials.c \ - src/core/lib/security/credentials/ssl/ssl_credentials.c \ - src/core/lib/security/transport/client_auth_filter.c \ - src/core/lib/security/transport/lb_targets_info.c \ - src/core/lib/security/transport/secure_endpoint.c \ - src/core/lib/security/transport/security_connector.c \ - src/core/lib/security/transport/security_handshaker.c \ - src/core/lib/security/transport/server_auth_filter.c \ - src/core/lib/security/transport/tsi_error.c \ - src/core/lib/security/util/json_util.c \ - src/core/lib/surface/init_secure.c \ - src/core/tsi/fake_transport_security.c \ - src/core/tsi/gts_transport_security.c \ - src/core/tsi/ssl_transport_security.c \ - src/core/tsi/transport_security_grpc.c \ - src/core/tsi/transport_security.c \ - src/core/tsi/transport_security_adapter.c \ - src/core/ext/transport/chttp2/server/chttp2_server.c \ - src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/ext/transport/chttp2/client/chttp2_connector.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \ - src/core/ext/transport/inproc/inproc_plugin.c \ - src/core/ext/transport/inproc/inproc_transport.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ + src/core/lib/http/httpcli_security_connector.cc \ + src/core/lib/security/context/security_context.cc \ + src/core/lib/security/credentials/composite/composite_credentials.cc \ + src/core/lib/security/credentials/credentials.cc \ + src/core/lib/security/credentials/credentials_metadata.cc \ + src/core/lib/security/credentials/fake/fake_credentials.cc \ + src/core/lib/security/credentials/google_default/credentials_generic.cc \ + src/core/lib/security/credentials/google_default/google_default_credentials.cc \ + src/core/lib/security/credentials/iam/iam_credentials.cc \ + src/core/lib/security/credentials/jwt/json_token.cc \ + src/core/lib/security/credentials/jwt/jwt_credentials.cc \ + src/core/lib/security/credentials/jwt/jwt_verifier.cc \ + src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \ + src/core/lib/security/credentials/plugin/plugin_credentials.cc \ + src/core/lib/security/credentials/ssl/ssl_credentials.cc \ + src/core/lib/security/transport/client_auth_filter.cc \ + src/core/lib/security/transport/lb_targets_info.cc \ + src/core/lib/security/transport/secure_endpoint.cc \ + src/core/lib/security/transport/security_connector.cc \ + src/core/lib/security/transport/security_handshaker.cc \ + src/core/lib/security/transport/server_auth_filter.cc \ + src/core/lib/security/transport/tsi_error.cc \ + src/core/lib/security/util/json_util.cc \ + src/core/lib/surface/init_secure.cc \ + src/core/tsi/fake_transport_security.cc \ + src/core/tsi/gts_transport_security.cc \ + src/core/tsi/ssl_transport_security.cc \ + src/core/tsi/transport_security_grpc.cc \ + src/core/tsi/transport_security.cc \ + src/core/tsi/transport_security_adapter.cc \ + src/core/ext/transport/chttp2/server/chttp2_server.cc \ + src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/ext/transport/chttp2/client/chttp2_connector.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \ + src/core/ext/transport/inproc/inproc_plugin.cc \ + src/core/ext/transport/inproc/inproc_transport.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c \ - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c \ - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c \ - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c \ - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c \ - src/core/ext/filters/load_reporting/server_load_reporting_filter.c \ - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c \ - src/core/ext/census/base_resources.c \ - src/core/ext/census/context.c \ - src/core/ext/census/gen/census.pb.c \ - src/core/ext/census/gen/trace_context.pb.c \ - src/core/ext/census/grpc_context.c \ - 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 \ - src/core/ext/census/resource.c \ - src/core/ext/census/trace_context.c \ - src/core/ext/census/tracing.c \ - src/core/ext/filters/max_age/max_age_filter.c \ - src/core/ext/filters/message_size/message_size_filter.c \ - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ - src/core/ext/filters/workarounds/workaround_utils.c \ + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ + src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ + src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ + src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \ + src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ + src/core/ext/census/base_resources.cc \ + src/core/ext/census/context.cc \ + src/core/ext/census/gen/census.pb.cc \ + src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/grpc_context.cc \ + src/core/ext/census/grpc_filter.cc \ + src/core/ext/census/grpc_plugin.cc \ + src/core/ext/census/initialize.cc \ + src/core/ext/census/intrusive_hash_map.cc \ + src/core/ext/census/mlog.cc \ + src/core/ext/census/operation.cc \ + src/core/ext/census/placeholders.cc \ + src/core/ext/census/resource.cc \ + src/core/ext/census/trace_context.cc \ + src/core/ext/census/tracing.cc \ + src/core/ext/filters/max_age/max_age_filter.cc \ + src/core/ext/filters/message_size/message_size_filter.cc \ + src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc \ + src/core/ext/filters/workarounds/workaround_utils.cc \ src/core/plugin_registry/grpc_plugin_registry.cc \ PUBLIC_HEADERS_C += \ @@ -3296,224 +3294,222 @@ endif LIBGRPC_CRONET_SRC = \ - src/core/lib/surface/init.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/lib/surface/init.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/transport/cronet/client/secure/cronet_channel_create.c \ - src/core/ext/transport/cronet/transport/cronet_api_dummy.c \ - src/core/ext/transport/cronet/transport/cronet_transport.c \ - src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/lib/http/httpcli_security_connector.c \ - src/core/lib/security/context/security_context.c \ - src/core/lib/security/credentials/composite/composite_credentials.c \ - src/core/lib/security/credentials/credentials.c \ - src/core/lib/security/credentials/credentials_metadata.c \ - src/core/lib/security/credentials/fake/fake_credentials.c \ - src/core/lib/security/credentials/google_default/credentials_generic.c \ - src/core/lib/security/credentials/google_default/google_default_credentials.c \ - src/core/lib/security/credentials/iam/iam_credentials.c \ - src/core/lib/security/credentials/jwt/json_token.c \ - src/core/lib/security/credentials/jwt/jwt_credentials.c \ - src/core/lib/security/credentials/jwt/jwt_verifier.c \ - src/core/lib/security/credentials/oauth2/oauth2_credentials.c \ - src/core/lib/security/credentials/plugin/plugin_credentials.c \ - src/core/lib/security/credentials/ssl/ssl_credentials.c \ - src/core/lib/security/transport/client_auth_filter.c \ - src/core/lib/security/transport/lb_targets_info.c \ - src/core/lib/security/transport/secure_endpoint.c \ - src/core/lib/security/transport/security_connector.c \ - src/core/lib/security/transport/security_handshaker.c \ - src/core/lib/security/transport/server_auth_filter.c \ - src/core/lib/security/transport/tsi_error.c \ - src/core/lib/security/util/json_util.c \ - src/core/lib/surface/init_secure.c \ - src/core/tsi/fake_transport_security.c \ - src/core/tsi/gts_transport_security.c \ - src/core/tsi/ssl_transport_security.c \ - src/core/tsi/transport_security_grpc.c \ - src/core/tsi/transport_security.c \ - src/core/tsi/transport_security_adapter.c \ - src/core/ext/transport/chttp2/client/chttp2_connector.c \ - src/core/ext/filters/load_reporting/server_load_reporting_filter.c \ - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc \ + src/core/ext/transport/cronet/transport/cronet_api_dummy.cc \ + src/core/ext/transport/cronet/transport/cronet_transport.cc \ + src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/lib/http/httpcli_security_connector.cc \ + src/core/lib/security/context/security_context.cc \ + src/core/lib/security/credentials/composite/composite_credentials.cc \ + src/core/lib/security/credentials/credentials.cc \ + src/core/lib/security/credentials/credentials_metadata.cc \ + src/core/lib/security/credentials/fake/fake_credentials.cc \ + src/core/lib/security/credentials/google_default/credentials_generic.cc \ + src/core/lib/security/credentials/google_default/google_default_credentials.cc \ + src/core/lib/security/credentials/iam/iam_credentials.cc \ + src/core/lib/security/credentials/jwt/json_token.cc \ + src/core/lib/security/credentials/jwt/jwt_credentials.cc \ + src/core/lib/security/credentials/jwt/jwt_verifier.cc \ + src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \ + src/core/lib/security/credentials/plugin/plugin_credentials.cc \ + src/core/lib/security/credentials/ssl/ssl_credentials.cc \ + src/core/lib/security/transport/client_auth_filter.cc \ + src/core/lib/security/transport/lb_targets_info.cc \ + src/core/lib/security/transport/secure_endpoint.cc \ + src/core/lib/security/transport/security_connector.cc \ + src/core/lib/security/transport/security_handshaker.cc \ + src/core/lib/security/transport/server_auth_filter.cc \ + src/core/lib/security/transport/tsi_error.cc \ + src/core/lib/security/util/json_util.cc \ + src/core/lib/surface/init_secure.cc \ + src/core/tsi/fake_transport_security.cc \ + src/core/tsi/gts_transport_security.cc \ + src/core/tsi/ssl_transport_security.cc \ + src/core/tsi/transport_security_grpc.cc \ + src/core/tsi/transport_security.cc \ + src/core/tsi/transport_security_adapter.cc \ + src/core/ext/transport/chttp2/client/chttp2_connector.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ src/core/plugin_registry/grpc_cronet_plugin_registry.cc \ PUBLIC_HEADERS_C += \ @@ -3600,201 +3596,199 @@ LIBGRPC_TEST_UTIL_SRC = \ test/core/end2end/data/server1_key.c \ test/core/end2end/data/test_root_cert.c \ test/core/security/oauth2_utils.c \ - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c \ - test/core/end2end/cq_verifier.c \ - test/core/end2end/fixtures/http_proxy_fixture.c \ - test/core/end2end/fixtures/proxy.c \ - test/core/iomgr/endpoint_tests.c \ - test/core/util/debugger_macros.c \ - test/core/util/grpc_profiler.c \ - test/core/util/memory_counters.c \ - test/core/util/mock_endpoint.c \ - test/core/util/parse_hexstring.c \ - test/core/util/passthru_endpoint.c \ - test/core/util/port.c \ - test/core/util/port_server_client.c \ - test/core/util/slice_splitter.c \ - test/core/util/trickle_endpoint.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ + test/core/end2end/cq_verifier.cc \ + test/core/end2end/fixtures/http_proxy_fixture.cc \ + test/core/end2end/fixtures/proxy.cc \ + test/core/iomgr/endpoint_tests.cc \ + test/core/util/debugger_macros.cc \ + test/core/util/grpc_profiler.cc \ + test/core/util/memory_counters.cc \ + test/core/util/mock_endpoint.cc \ + test/core/util/parse_hexstring.cc \ + test/core/util/passthru_endpoint.cc \ + test/core/util/port.cc \ + test/core/util/port_server_client.cc \ + test/core/util/slice_splitter.cc \ + test/core/util/trickle_endpoint.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ PUBLIC_HEADERS_C += \ include/grpc/impl/codegen/byte_buffer.h \ @@ -3854,201 +3848,199 @@ endif LIBGRPC_TEST_UTIL_UNSECURE_SRC = \ - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c \ - test/core/end2end/cq_verifier.c \ - test/core/end2end/fixtures/http_proxy_fixture.c \ - test/core/end2end/fixtures/proxy.c \ - test/core/iomgr/endpoint_tests.c \ - test/core/util/debugger_macros.c \ - test/core/util/grpc_profiler.c \ - test/core/util/memory_counters.c \ - test/core/util/mock_endpoint.c \ - test/core/util/parse_hexstring.c \ - test/core/util/passthru_endpoint.c \ - test/core/util/port.c \ - test/core/util/port_server_client.c \ - test/core/util/slice_splitter.c \ - test/core/util/trickle_endpoint.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ + test/core/end2end/cq_verifier.cc \ + test/core/end2end/fixtures/http_proxy_fixture.cc \ + test/core/end2end/fixtures/proxy.cc \ + test/core/iomgr/endpoint_tests.cc \ + test/core/util/debugger_macros.cc \ + test/core/util/grpc_profiler.cc \ + test/core/util/memory_counters.cc \ + test/core/util/mock_endpoint.cc \ + test/core/util/parse_hexstring.cc \ + test/core/util/passthru_endpoint.cc \ + test/core/util/port.cc \ + test/core/util/port_server_client.cc \ + test/core/util/slice_splitter.cc \ + test/core/util/trickle_endpoint.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ PUBLIC_HEADERS_C += \ include/grpc/impl/codegen/byte_buffer.h \ @@ -4094,235 +4086,233 @@ endif LIBGRPC_UNSECURE_SRC = \ - src/core/lib/surface/init.c \ - src/core/lib/surface/init_unsecure.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/lib/surface/init.cc \ + src/core/lib/surface/init_unsecure.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ - src/core/ext/transport/chttp2/server/chttp2_server.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \ - src/core/ext/transport/chttp2/client/chttp2_connector.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/ext/transport/inproc/inproc_plugin.c \ - src/core/ext/transport/inproc/inproc_transport.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c \ - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c \ - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c \ - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c \ - src/core/ext/filters/load_reporting/server_load_reporting_filter.c \ - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ + src/core/ext/transport/chttp2/server/chttp2_server.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \ + src/core/ext/transport/chttp2/client/chttp2_connector.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/ext/transport/inproc/inproc_plugin.cc \ + src/core/ext/transport/inproc/inproc_transport.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ + src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \ + src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c \ - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c \ - src/core/ext/census/base_resources.c \ - src/core/ext/census/context.c \ - src/core/ext/census/gen/census.pb.c \ - src/core/ext/census/gen/trace_context.pb.c \ - src/core/ext/census/grpc_context.c \ - 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 \ - src/core/ext/census/resource.c \ - src/core/ext/census/trace_context.c \ - src/core/ext/census/tracing.c \ - src/core/ext/filters/max_age/max_age_filter.c \ - src/core/ext/filters/message_size/message_size_filter.c \ - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ - src/core/ext/filters/workarounds/workaround_utils.c \ + src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ + src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ + src/core/ext/census/base_resources.cc \ + src/core/ext/census/context.cc \ + src/core/ext/census/gen/census.pb.cc \ + src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/grpc_context.cc \ + src/core/ext/census/grpc_filter.cc \ + src/core/ext/census/grpc_plugin.cc \ + src/core/ext/census/initialize.cc \ + src/core/ext/census/intrusive_hash_map.cc \ + src/core/ext/census/mlog.cc \ + src/core/ext/census/operation.cc \ + src/core/ext/census/placeholders.cc \ + src/core/ext/census/resource.cc \ + src/core/ext/census/trace_context.cc \ + src/core/ext/census/tracing.cc \ + src/core/ext/filters/max_age/max_age_filter.cc \ + src/core/ext/filters/message_size/message_size_filter.cc \ + src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc \ + src/core/ext/filters/workarounds/workaround_utils.cc \ src/core/plugin_registry/grpc_unsecure_plugin_registry.cc \ PUBLIC_HEADERS_C += \ @@ -4811,207 +4801,205 @@ LIBGRPC++_CRONET_SRC = \ src/cpp/util/string_ref.cc \ src/cpp/util/time_cc.cc \ src/cpp/codegen/codegen_init.cc \ - src/core/ext/transport/chttp2/client/insecure/channel_create.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \ - src/core/ext/transport/chttp2/client/chttp2_connector.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/ext/transport/chttp2/client/insecure/channel_create.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \ + src/core/ext/transport/chttp2/client/chttp2_connector.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \ - src/core/ext/transport/chttp2/server/chttp2_server.c \ - src/core/ext/census/base_resources.c \ - src/core/ext/census/context.c \ - src/core/ext/census/gen/census.pb.c \ - src/core/ext/census/gen/trace_context.pb.c \ - src/core/ext/census/grpc_context.c \ - 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 \ - src/core/ext/census/resource.c \ - src/core/ext/census/trace_context.c \ - src/core/ext/census/tracing.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \ + src/core/ext/transport/chttp2/server/chttp2_server.cc \ + src/core/ext/census/base_resources.cc \ + src/core/ext/census/context.cc \ + src/core/ext/census/gen/census.pb.cc \ + src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/grpc_context.cc \ + src/core/ext/census/grpc_filter.cc \ + src/core/ext/census/grpc_plugin.cc \ + src/core/ext/census/initialize.cc \ + src/core/ext/census/intrusive_hash_map.cc \ + src/core/ext/census/mlog.cc \ + src/core/ext/census/operation.cc \ + src/core/ext/census/placeholders.cc \ + src/core/ext/census/resource.cc \ + src/core/ext/census/trace_context.cc \ + src/core/ext/census/tracing.cc \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ @@ -20158,44 +20146,44 @@ ifneq ($(OPENSSL_DEP),) # This is to ensure the embedded OpenSSL is built beforehand, properly # installing headers to their final destination on the drive. We need this # otherwise parallel compilation will fail if a source is compiled first. -src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c: $(OPENSSL_DEP) -src/core/ext/transport/chttp2/client/secure/secure_channel_create.c: $(OPENSSL_DEP) -src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c: $(OPENSSL_DEP) -src/core/ext/transport/cronet/client/secure/cronet_channel_create.c: $(OPENSSL_DEP) -src/core/ext/transport/cronet/transport/cronet_api_dummy.c: $(OPENSSL_DEP) -src/core/ext/transport/cronet/transport/cronet_transport.c: $(OPENSSL_DEP) -src/core/lib/http/httpcli_security_connector.c: $(OPENSSL_DEP) -src/core/lib/security/context/security_context.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/composite/composite_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/credentials_metadata.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/fake/fake_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/google_default/credentials_generic.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/google_default/google_default_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/iam/iam_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/jwt/json_token.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/jwt/jwt_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/jwt/jwt_verifier.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/oauth2/oauth2_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/plugin/plugin_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/credentials/ssl/ssl_credentials.c: $(OPENSSL_DEP) -src/core/lib/security/transport/client_auth_filter.c: $(OPENSSL_DEP) -src/core/lib/security/transport/lb_targets_info.c: $(OPENSSL_DEP) -src/core/lib/security/transport/secure_endpoint.c: $(OPENSSL_DEP) -src/core/lib/security/transport/security_connector.c: $(OPENSSL_DEP) -src/core/lib/security/transport/security_handshaker.c: $(OPENSSL_DEP) -src/core/lib/security/transport/server_auth_filter.c: $(OPENSSL_DEP) -src/core/lib/security/transport/tsi_error.c: $(OPENSSL_DEP) -src/core/lib/security/util/json_util.c: $(OPENSSL_DEP) -src/core/lib/surface/init_secure.c: $(OPENSSL_DEP) +src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc: $(OPENSSL_DEP) +src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc: $(OPENSSL_DEP) +src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc: $(OPENSSL_DEP) +src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc: $(OPENSSL_DEP) +src/core/ext/transport/cronet/transport/cronet_api_dummy.cc: $(OPENSSL_DEP) +src/core/ext/transport/cronet/transport/cronet_transport.cc: $(OPENSSL_DEP) +src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP) +src/core/lib/security/context/security_context.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/composite/composite_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/credentials_metadata.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/fake/fake_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/google_default/credentials_generic.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/google_default/google_default_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/iam/iam_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/jwt/json_token.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/jwt/jwt_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/jwt/jwt_verifier.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/oauth2/oauth2_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/plugin/plugin_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/credentials/ssl/ssl_credentials.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/client_auth_filter.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/lb_targets_info.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/secure_endpoint.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/security_connector.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/security_handshaker.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/server_auth_filter.cc: $(OPENSSL_DEP) +src/core/lib/security/transport/tsi_error.cc: $(OPENSSL_DEP) +src/core/lib/security/util/json_util.cc: $(OPENSSL_DEP) +src/core/lib/surface/init_secure.cc: $(OPENSSL_DEP) src/core/plugin_registry/grpc_cronet_plugin_registry.cc: $(OPENSSL_DEP) src/core/plugin_registry/grpc_plugin_registry.cc: $(OPENSSL_DEP) -src/core/tsi/fake_transport_security.c: $(OPENSSL_DEP) -src/core/tsi/gts_transport_security.c: $(OPENSSL_DEP) -src/core/tsi/ssl_transport_security.c: $(OPENSSL_DEP) -src/core/tsi/transport_security.c: $(OPENSSL_DEP) -src/core/tsi/transport_security_adapter.c: $(OPENSSL_DEP) -src/core/tsi/transport_security_grpc.c: $(OPENSSL_DEP) +src/core/tsi/fake_transport_security.cc: $(OPENSSL_DEP) +src/core/tsi/gts_transport_security.cc: $(OPENSSL_DEP) +src/core/tsi/ssl_transport_security.cc: $(OPENSSL_DEP) +src/core/tsi/transport_security.cc: $(OPENSSL_DEP) +src/core/tsi/transport_security_adapter.cc: $(OPENSSL_DEP) +src/core/tsi/transport_security_grpc.cc: $(OPENSSL_DEP) src/cpp/client/cronet_credentials.cc: $(OPENSSL_DEP) src/cpp/client/secure_credentials.cc: $(OPENSSL_DEP) src/cpp/common/auth_property_iterator.cc: $(OPENSSL_DEP) diff --git a/binding.gyp b/binding.gyp index 476e6fd3c4..2608fb1a7e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -593,52 +593,52 @@ 'dependencies': [ ], 'sources': [ - 'src/core/lib/profiling/basic_timers.c', - 'src/core/lib/profiling/stap_timers.c', - 'src/core/lib/support/alloc.c', - 'src/core/lib/support/arena.c', - 'src/core/lib/support/atm.c', - 'src/core/lib/support/avl.c', - 'src/core/lib/support/backoff.c', - 'src/core/lib/support/cmdline.c', - 'src/core/lib/support/cpu_iphone.c', - 'src/core/lib/support/cpu_linux.c', - 'src/core/lib/support/cpu_posix.c', - 'src/core/lib/support/cpu_windows.c', - 'src/core/lib/support/env_linux.c', - 'src/core/lib/support/env_posix.c', - 'src/core/lib/support/env_windows.c', - 'src/core/lib/support/histogram.c', - 'src/core/lib/support/host_port.c', - 'src/core/lib/support/log.c', - 'src/core/lib/support/log_android.c', - 'src/core/lib/support/log_linux.c', - 'src/core/lib/support/log_posix.c', - 'src/core/lib/support/log_windows.c', - 'src/core/lib/support/mpscq.c', - 'src/core/lib/support/murmur_hash.c', - 'src/core/lib/support/stack_lockfree.c', - 'src/core/lib/support/string.c', - 'src/core/lib/support/string_posix.c', - 'src/core/lib/support/string_util_windows.c', - 'src/core/lib/support/string_windows.c', - 'src/core/lib/support/subprocess_posix.c', - 'src/core/lib/support/subprocess_windows.c', - 'src/core/lib/support/sync.c', - 'src/core/lib/support/sync_posix.c', - 'src/core/lib/support/sync_windows.c', - 'src/core/lib/support/thd.c', - 'src/core/lib/support/thd_posix.c', - 'src/core/lib/support/thd_windows.c', - 'src/core/lib/support/time.c', - 'src/core/lib/support/time_posix.c', - 'src/core/lib/support/time_precise.c', - 'src/core/lib/support/time_windows.c', - 'src/core/lib/support/tls_pthread.c', - 'src/core/lib/support/tmpfile_msys.c', - 'src/core/lib/support/tmpfile_posix.c', - 'src/core/lib/support/tmpfile_windows.c', - 'src/core/lib/support/wrap_memcpy.c', + 'src/core/lib/profiling/basic_timers.cc', + 'src/core/lib/profiling/stap_timers.cc', + 'src/core/lib/support/alloc.cc', + 'src/core/lib/support/arena.cc', + 'src/core/lib/support/atm.cc', + 'src/core/lib/support/avl.cc', + 'src/core/lib/support/backoff.cc', + 'src/core/lib/support/cmdline.cc', + 'src/core/lib/support/cpu_iphone.cc', + 'src/core/lib/support/cpu_linux.cc', + 'src/core/lib/support/cpu_posix.cc', + 'src/core/lib/support/cpu_windows.cc', + 'src/core/lib/support/env_linux.cc', + 'src/core/lib/support/env_posix.cc', + 'src/core/lib/support/env_windows.cc', + 'src/core/lib/support/histogram.cc', + 'src/core/lib/support/host_port.cc', + 'src/core/lib/support/log.cc', + 'src/core/lib/support/log_android.cc', + 'src/core/lib/support/log_linux.cc', + 'src/core/lib/support/log_posix.cc', + 'src/core/lib/support/log_windows.cc', + 'src/core/lib/support/mpscq.cc', + 'src/core/lib/support/murmur_hash.cc', + 'src/core/lib/support/stack_lockfree.cc', + 'src/core/lib/support/string.cc', + 'src/core/lib/support/string_posix.cc', + 'src/core/lib/support/string_util_windows.cc', + 'src/core/lib/support/string_windows.cc', + 'src/core/lib/support/subprocess_posix.cc', + 'src/core/lib/support/subprocess_windows.cc', + 'src/core/lib/support/sync.cc', + 'src/core/lib/support/sync_posix.cc', + 'src/core/lib/support/sync_windows.cc', + 'src/core/lib/support/thd.cc', + 'src/core/lib/support/thd_posix.cc', + 'src/core/lib/support/thd_windows.cc', + 'src/core/lib/support/time.cc', + 'src/core/lib/support/time_posix.cc', + 'src/core/lib/support/time_precise.cc', + 'src/core/lib/support/time_windows.cc', + 'src/core/lib/support/tls_pthread.cc', + 'src/core/lib/support/tmpfile_msys.cc', + 'src/core/lib/support/tmpfile_posix.cc', + 'src/core/lib/support/tmpfile_windows.cc', + 'src/core/lib/support/wrap_memcpy.cc', ], 'conditions': [ ['OS == "mac"', { @@ -656,266 +656,264 @@ 'gpr', ], 'sources': [ - 'src/core/lib/surface/init.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/lib/surface/init.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', - 'src/core/lib/http/httpcli_security_connector.c', - 'src/core/lib/security/context/security_context.c', - 'src/core/lib/security/credentials/composite/composite_credentials.c', - 'src/core/lib/security/credentials/credentials.c', - 'src/core/lib/security/credentials/credentials_metadata.c', - 'src/core/lib/security/credentials/fake/fake_credentials.c', - 'src/core/lib/security/credentials/google_default/credentials_generic.c', - 'src/core/lib/security/credentials/google_default/google_default_credentials.c', - 'src/core/lib/security/credentials/iam/iam_credentials.c', - 'src/core/lib/security/credentials/jwt/json_token.c', - 'src/core/lib/security/credentials/jwt/jwt_credentials.c', - 'src/core/lib/security/credentials/jwt/jwt_verifier.c', - 'src/core/lib/security/credentials/oauth2/oauth2_credentials.c', - 'src/core/lib/security/credentials/plugin/plugin_credentials.c', - 'src/core/lib/security/credentials/ssl/ssl_credentials.c', - 'src/core/lib/security/transport/client_auth_filter.c', - 'src/core/lib/security/transport/lb_targets_info.c', - 'src/core/lib/security/transport/secure_endpoint.c', - 'src/core/lib/security/transport/security_connector.c', - 'src/core/lib/security/transport/security_handshaker.c', - 'src/core/lib/security/transport/server_auth_filter.c', - 'src/core/lib/security/transport/tsi_error.c', - 'src/core/lib/security/util/json_util.c', - 'src/core/lib/surface/init_secure.c', - 'src/core/tsi/fake_transport_security.c', - 'src/core/tsi/gts_transport_security.c', - 'src/core/tsi/ssl_transport_security.c', - 'src/core/tsi/transport_security_grpc.c', - 'src/core/tsi/transport_security.c', - 'src/core/tsi/transport_security_adapter.c', - 'src/core/ext/transport/chttp2/server/chttp2_server.c', - 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/chttp2/client/chttp2_connector.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c', - 'src/core/ext/transport/inproc/inproc_plugin.c', - 'src/core/ext/transport/inproc/inproc_transport.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', + 'src/core/lib/http/httpcli_security_connector.cc', + 'src/core/lib/security/context/security_context.cc', + 'src/core/lib/security/credentials/composite/composite_credentials.cc', + 'src/core/lib/security/credentials/credentials.cc', + 'src/core/lib/security/credentials/credentials_metadata.cc', + 'src/core/lib/security/credentials/fake/fake_credentials.cc', + 'src/core/lib/security/credentials/google_default/credentials_generic.cc', + 'src/core/lib/security/credentials/google_default/google_default_credentials.cc', + 'src/core/lib/security/credentials/iam/iam_credentials.cc', + 'src/core/lib/security/credentials/jwt/json_token.cc', + 'src/core/lib/security/credentials/jwt/jwt_credentials.cc', + 'src/core/lib/security/credentials/jwt/jwt_verifier.cc', + 'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc', + 'src/core/lib/security/credentials/plugin/plugin_credentials.cc', + 'src/core/lib/security/credentials/ssl/ssl_credentials.cc', + 'src/core/lib/security/transport/client_auth_filter.cc', + 'src/core/lib/security/transport/lb_targets_info.cc', + 'src/core/lib/security/transport/secure_endpoint.cc', + 'src/core/lib/security/transport/security_connector.cc', + 'src/core/lib/security/transport/security_handshaker.cc', + 'src/core/lib/security/transport/server_auth_filter.cc', + 'src/core/lib/security/transport/tsi_error.cc', + 'src/core/lib/security/util/json_util.cc', + 'src/core/lib/surface/init_secure.cc', + 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/gts_transport_security.cc', + 'src/core/tsi/ssl_transport_security.cc', + 'src/core/tsi/transport_security_grpc.cc', + 'src/core/tsi/transport_security.cc', + 'src/core/tsi/transport_security_adapter.cc', + 'src/core/ext/transport/chttp2/server/chttp2_server.cc', + 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', + 'src/core/ext/transport/inproc/inproc_plugin.cc', + 'src/core/ext/transport/inproc/inproc_transport.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c', - 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c', - 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c', - 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_filter.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.c', - 'src/core/ext/census/base_resources.c', - 'src/core/ext/census/context.c', - 'src/core/ext/census/gen/census.pb.c', - 'src/core/ext/census/gen/trace_context.pb.c', - 'src/core/ext/census/grpc_context.c', - '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', - 'src/core/ext/census/resource.c', - 'src/core/ext/census/trace_context.c', - 'src/core/ext/census/tracing.c', - 'src/core/ext/filters/max_age/max_age_filter.c', - 'src/core/ext/filters/message_size/message_size_filter.c', - 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', - 'src/core/ext/filters/workarounds/workaround_utils.c', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', + 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', + 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', + 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', + 'src/core/ext/census/base_resources.cc', + 'src/core/ext/census/context.cc', + 'src/core/ext/census/gen/census.pb.cc', + 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/grpc_context.cc', + 'src/core/ext/census/grpc_filter.cc', + 'src/core/ext/census/grpc_plugin.cc', + 'src/core/ext/census/initialize.cc', + 'src/core/ext/census/intrusive_hash_map.cc', + 'src/core/ext/census/mlog.cc', + 'src/core/ext/census/operation.cc', + 'src/core/ext/census/placeholders.cc', + 'src/core/ext/census/resource.cc', + 'src/core/ext/census/trace_context.cc', + 'src/core/ext/census/tracing.cc', + 'src/core/ext/filters/max_age/max_age_filter.cc', + 'src/core/ext/filters/message_size/message_size_filter.cc', + 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc', + 'src/core/ext/filters/workarounds/workaround_utils.cc', 'src/core/plugin_registry/grpc_plugin_registry.cc', ], 'conditions': [ diff --git a/build.yaml b/build.yaml index 9b5aacc4c8..4fc7a85712 100644 --- a/build.yaml +++ b/build.yaml @@ -39,73 +39,73 @@ filegroups: - src/core/ext/census/trace_string.h - src/core/ext/census/tracing.h src: - - src/core/ext/census/base_resources.c - - src/core/ext/census/context.c - - src/core/ext/census/gen/census.pb.c - - src/core/ext/census/gen/trace_context.pb.c - - src/core/ext/census/grpc_context.c - - 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 - - src/core/ext/census/resource.c - - src/core/ext/census/trace_context.c - - src/core/ext/census/tracing.c + - src/core/ext/census/base_resources.cc + - src/core/ext/census/context.cc + - src/core/ext/census/gen/census.pb.cc + - src/core/ext/census/gen/trace_context.pb.cc + - src/core/ext/census/grpc_context.cc + - src/core/ext/census/grpc_filter.cc + - src/core/ext/census/grpc_plugin.cc + - src/core/ext/census/initialize.cc + - src/core/ext/census/intrusive_hash_map.cc + - src/core/ext/census/mlog.cc + - src/core/ext/census/operation.cc + - src/core/ext/census/placeholders.cc + - src/core/ext/census/resource.cc + - src/core/ext/census/trace_context.cc + - src/core/ext/census/tracing.cc plugin: census_grpc_plugin uses: - grpc_base - nanopb - name: gpr_base src: - - src/core/lib/profiling/basic_timers.c - - src/core/lib/profiling/stap_timers.c - - src/core/lib/support/alloc.c - - src/core/lib/support/arena.c - - src/core/lib/support/atm.c - - src/core/lib/support/avl.c - - src/core/lib/support/backoff.c - - src/core/lib/support/cmdline.c - - src/core/lib/support/cpu_iphone.c - - src/core/lib/support/cpu_linux.c - - src/core/lib/support/cpu_posix.c - - src/core/lib/support/cpu_windows.c - - src/core/lib/support/env_linux.c - - src/core/lib/support/env_posix.c - - src/core/lib/support/env_windows.c - - src/core/lib/support/histogram.c - - src/core/lib/support/host_port.c - - src/core/lib/support/log.c - - src/core/lib/support/log_android.c - - src/core/lib/support/log_linux.c - - src/core/lib/support/log_posix.c - - src/core/lib/support/log_windows.c - - src/core/lib/support/mpscq.c - - src/core/lib/support/murmur_hash.c - - src/core/lib/support/stack_lockfree.c - - src/core/lib/support/string.c - - src/core/lib/support/string_posix.c - - src/core/lib/support/string_util_windows.c - - src/core/lib/support/string_windows.c - - src/core/lib/support/subprocess_posix.c - - src/core/lib/support/subprocess_windows.c - - src/core/lib/support/sync.c - - src/core/lib/support/sync_posix.c - - src/core/lib/support/sync_windows.c - - src/core/lib/support/thd.c - - src/core/lib/support/thd_posix.c - - src/core/lib/support/thd_windows.c - - src/core/lib/support/time.c - - src/core/lib/support/time_posix.c - - src/core/lib/support/time_precise.c - - src/core/lib/support/time_windows.c - - src/core/lib/support/tls_pthread.c - - src/core/lib/support/tmpfile_msys.c - - src/core/lib/support/tmpfile_posix.c - - src/core/lib/support/tmpfile_windows.c - - src/core/lib/support/wrap_memcpy.c + - src/core/lib/profiling/basic_timers.cc + - src/core/lib/profiling/stap_timers.cc + - src/core/lib/support/alloc.cc + - src/core/lib/support/arena.cc + - src/core/lib/support/atm.cc + - src/core/lib/support/avl.cc + - src/core/lib/support/backoff.cc + - src/core/lib/support/cmdline.cc + - src/core/lib/support/cpu_iphone.cc + - src/core/lib/support/cpu_linux.cc + - src/core/lib/support/cpu_posix.cc + - src/core/lib/support/cpu_windows.cc + - src/core/lib/support/env_linux.cc + - src/core/lib/support/env_posix.cc + - src/core/lib/support/env_windows.cc + - src/core/lib/support/histogram.cc + - src/core/lib/support/host_port.cc + - src/core/lib/support/log.cc + - src/core/lib/support/log_android.cc + - src/core/lib/support/log_linux.cc + - src/core/lib/support/log_posix.cc + - src/core/lib/support/log_windows.cc + - src/core/lib/support/mpscq.cc + - src/core/lib/support/murmur_hash.cc + - src/core/lib/support/stack_lockfree.cc + - src/core/lib/support/string.cc + - src/core/lib/support/string_posix.cc + - src/core/lib/support/string_util_windows.cc + - src/core/lib/support/string_windows.cc + - src/core/lib/support/subprocess_posix.cc + - src/core/lib/support/subprocess_windows.cc + - src/core/lib/support/sync.cc + - src/core/lib/support/sync_posix.cc + - src/core/lib/support/sync_windows.cc + - src/core/lib/support/thd.cc + - src/core/lib/support/thd_posix.cc + - src/core/lib/support/thd_windows.cc + - src/core/lib/support/time.cc + - src/core/lib/support/time_posix.cc + - src/core/lib/support/time_precise.cc + - src/core/lib/support/time_windows.cc + - src/core/lib/support/tls_pthread.cc + - src/core/lib/support/tmpfile_msys.cc + - src/core/lib/support/tmpfile_posix.cc + - src/core/lib/support/tmpfile_windows.cc + - src/core/lib/support/wrap_memcpy.cc uses: - gpr_base_headers - name: gpr_base_headers @@ -185,137 +185,135 @@ filegroups: - grpc++_codegen_base - name: grpc_base src: - - src/core/lib/channel/channel_args.c - - src/core/lib/channel/channel_stack.c - - src/core/lib/channel/channel_stack_builder.c - - src/core/lib/channel/connected_channel.c - - src/core/lib/channel/handshaker.c - - src/core/lib/channel/handshaker_factory.c - - src/core/lib/channel/handshaker_registry.c - - src/core/lib/compression/compression.c - - src/core/lib/compression/message_compress.c - - src/core/lib/compression/stream_compression.c - - src/core/lib/compression/stream_compression_gzip.c - - src/core/lib/compression/stream_compression_identity.c - - src/core/lib/debug/stats.c - - src/core/lib/debug/stats_data.c - - src/core/lib/http/format_request.c - - src/core/lib/http/httpcli.c - - src/core/lib/http/parser.c - - src/core/lib/iomgr/call_combiner.c - - src/core/lib/iomgr/closure.c - - src/core/lib/iomgr/combiner.c - - src/core/lib/iomgr/endpoint.c - - src/core/lib/iomgr/endpoint_pair_posix.c - - src/core/lib/iomgr/endpoint_pair_uv.c - - src/core/lib/iomgr/endpoint_pair_windows.c - - src/core/lib/iomgr/error.c - - src/core/lib/iomgr/ev_epoll1_linux.c - - src/core/lib/iomgr/ev_epollex_linux.c - - src/core/lib/iomgr/ev_epollsig_linux.c - - src/core/lib/iomgr/ev_poll_posix.c - - src/core/lib/iomgr/ev_posix.c - - src/core/lib/iomgr/ev_windows.c - - src/core/lib/iomgr/exec_ctx.c - - src/core/lib/iomgr/executor.c - - src/core/lib/iomgr/gethostname_fallback.c - - src/core/lib/iomgr/gethostname_host_name_max.c - - src/core/lib/iomgr/gethostname_sysconf.c - - src/core/lib/iomgr/iocp_windows.c - - src/core/lib/iomgr/iomgr.c - - src/core/lib/iomgr/iomgr_posix.c - - src/core/lib/iomgr/iomgr_uv.c - - src/core/lib/iomgr/iomgr_windows.c - - src/core/lib/iomgr/is_epollexclusive_available.c - - src/core/lib/iomgr/load_file.c - - src/core/lib/iomgr/lockfree_event.c - - src/core/lib/iomgr/network_status_tracker.c - - src/core/lib/iomgr/polling_entity.c - - src/core/lib/iomgr/pollset_set_uv.c - - src/core/lib/iomgr/pollset_set_windows.c - - src/core/lib/iomgr/pollset_uv.c - - src/core/lib/iomgr/pollset_windows.c - - src/core/lib/iomgr/resolve_address_posix.c - - src/core/lib/iomgr/resolve_address_uv.c - - src/core/lib/iomgr/resolve_address_windows.c - - src/core/lib/iomgr/resource_quota.c - - src/core/lib/iomgr/sockaddr_utils.c - - src/core/lib/iomgr/socket_factory_posix.c - - src/core/lib/iomgr/socket_mutator.c - - src/core/lib/iomgr/socket_utils_common_posix.c - - src/core/lib/iomgr/socket_utils_linux.c - - src/core/lib/iomgr/socket_utils_posix.c - - src/core/lib/iomgr/socket_utils_uv.c - - src/core/lib/iomgr/socket_utils_windows.c - - src/core/lib/iomgr/socket_windows.c - - src/core/lib/iomgr/tcp_client_posix.c - - src/core/lib/iomgr/tcp_client_uv.c - - src/core/lib/iomgr/tcp_client_windows.c - - src/core/lib/iomgr/tcp_posix.c - - src/core/lib/iomgr/tcp_server_posix.c - - src/core/lib/iomgr/tcp_server_utils_posix_common.c - - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c - - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c - - src/core/lib/iomgr/tcp_server_uv.c - - src/core/lib/iomgr/tcp_server_windows.c - - src/core/lib/iomgr/tcp_uv.c - - src/core/lib/iomgr/tcp_windows.c - - src/core/lib/iomgr/time_averaged_stats.c - - src/core/lib/iomgr/timer_generic.c - - src/core/lib/iomgr/timer_heap.c - - src/core/lib/iomgr/timer_manager.c - - src/core/lib/iomgr/timer_uv.c - - src/core/lib/iomgr/udp_server.c - - src/core/lib/iomgr/unix_sockets_posix.c - - src/core/lib/iomgr/unix_sockets_posix_noop.c - - src/core/lib/iomgr/wakeup_fd_cv.c - - src/core/lib/iomgr/wakeup_fd_eventfd.c - - src/core/lib/iomgr/wakeup_fd_nospecial.c - - src/core/lib/iomgr/wakeup_fd_pipe.c - - src/core/lib/iomgr/wakeup_fd_posix.c - - src/core/lib/json/json.c - - src/core/lib/json/json_reader.c - - src/core/lib/json/json_string.c - - src/core/lib/json/json_writer.c - - src/core/lib/slice/b64.c - - src/core/lib/slice/percent_encoding.c - - src/core/lib/slice/slice.c - - src/core/lib/slice/slice_buffer.c - - src/core/lib/slice/slice_hash_table.c - - src/core/lib/slice/slice_intern.c - - src/core/lib/slice/slice_string_helpers.c - - src/core/lib/surface/alarm.c - - src/core/lib/surface/api_trace.c - - src/core/lib/surface/byte_buffer.c - - src/core/lib/surface/byte_buffer_reader.c - - src/core/lib/surface/call.c - - src/core/lib/surface/call_details.c - - src/core/lib/surface/call_log_batch.c - - src/core/lib/surface/channel.c - - src/core/lib/surface/channel_init.c - - src/core/lib/surface/channel_ping.c - - src/core/lib/surface/channel_stack_type.c - - src/core/lib/surface/completion_queue.c - - src/core/lib/surface/completion_queue_factory.c - - src/core/lib/surface/event_string.c + - src/core/lib/channel/channel_args.cc + - src/core/lib/channel/channel_stack.cc + - src/core/lib/channel/channel_stack_builder.cc + - src/core/lib/channel/connected_channel.cc + - src/core/lib/channel/handshaker.cc + - src/core/lib/channel/handshaker_factory.cc + - src/core/lib/channel/handshaker_registry.cc + - src/core/lib/compression/compression.cc + - src/core/lib/compression/message_compress.cc + - src/core/lib/compression/stream_compression.cc + - src/core/lib/debug/stats.cc + - src/core/lib/debug/stats_data.cc + - src/core/lib/http/format_request.cc + - src/core/lib/http/httpcli.cc + - src/core/lib/http/parser.cc + - src/core/lib/iomgr/call_combiner.cc + - src/core/lib/iomgr/closure.cc + - src/core/lib/iomgr/combiner.cc + - src/core/lib/iomgr/endpoint.cc + - src/core/lib/iomgr/endpoint_pair_posix.cc + - src/core/lib/iomgr/endpoint_pair_uv.cc + - src/core/lib/iomgr/endpoint_pair_windows.cc + - src/core/lib/iomgr/error.cc + - src/core/lib/iomgr/ev_epoll1_linux.cc + - src/core/lib/iomgr/ev_epollex_linux.cc + - src/core/lib/iomgr/ev_epollsig_linux.cc + - src/core/lib/iomgr/ev_poll_posix.cc + - src/core/lib/iomgr/ev_posix.cc + - src/core/lib/iomgr/ev_windows.cc + - src/core/lib/iomgr/exec_ctx.cc + - src/core/lib/iomgr/executor.cc + - src/core/lib/iomgr/gethostname_fallback.cc + - src/core/lib/iomgr/gethostname_host_name_max.cc + - src/core/lib/iomgr/gethostname_sysconf.cc + - src/core/lib/iomgr/iocp_windows.cc + - src/core/lib/iomgr/iomgr.cc + - src/core/lib/iomgr/iomgr_posix.cc + - src/core/lib/iomgr/iomgr_uv.cc + - src/core/lib/iomgr/iomgr_windows.cc + - src/core/lib/iomgr/is_epollexclusive_available.cc + - src/core/lib/iomgr/load_file.cc + - src/core/lib/iomgr/lockfree_event.cc + - src/core/lib/iomgr/network_status_tracker.cc + - src/core/lib/iomgr/polling_entity.cc + - src/core/lib/iomgr/pollset_set_uv.cc + - src/core/lib/iomgr/pollset_set_windows.cc + - src/core/lib/iomgr/pollset_uv.cc + - src/core/lib/iomgr/pollset_windows.cc + - src/core/lib/iomgr/resolve_address_posix.cc + - src/core/lib/iomgr/resolve_address_uv.cc + - src/core/lib/iomgr/resolve_address_windows.cc + - src/core/lib/iomgr/resource_quota.cc + - src/core/lib/iomgr/sockaddr_utils.cc + - src/core/lib/iomgr/socket_factory_posix.cc + - src/core/lib/iomgr/socket_mutator.cc + - src/core/lib/iomgr/socket_utils_common_posix.cc + - src/core/lib/iomgr/socket_utils_linux.cc + - src/core/lib/iomgr/socket_utils_posix.cc + - src/core/lib/iomgr/socket_utils_uv.cc + - src/core/lib/iomgr/socket_utils_windows.cc + - src/core/lib/iomgr/socket_windows.cc + - src/core/lib/iomgr/tcp_client_posix.cc + - src/core/lib/iomgr/tcp_client_uv.cc + - src/core/lib/iomgr/tcp_client_windows.cc + - src/core/lib/iomgr/tcp_posix.cc + - src/core/lib/iomgr/tcp_server_posix.cc + - src/core/lib/iomgr/tcp_server_utils_posix_common.cc + - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc + - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc + - src/core/lib/iomgr/tcp_server_uv.cc + - src/core/lib/iomgr/tcp_server_windows.cc + - src/core/lib/iomgr/tcp_uv.cc + - src/core/lib/iomgr/tcp_windows.cc + - src/core/lib/iomgr/time_averaged_stats.cc + - src/core/lib/iomgr/timer_generic.cc + - src/core/lib/iomgr/timer_heap.cc + - src/core/lib/iomgr/timer_manager.cc + - src/core/lib/iomgr/timer_uv.cc + - src/core/lib/iomgr/udp_server.cc + - src/core/lib/iomgr/unix_sockets_posix.cc + - src/core/lib/iomgr/unix_sockets_posix_noop.cc + - src/core/lib/iomgr/wakeup_fd_cv.cc + - src/core/lib/iomgr/wakeup_fd_eventfd.cc + - src/core/lib/iomgr/wakeup_fd_nospecial.cc + - src/core/lib/iomgr/wakeup_fd_pipe.cc + - src/core/lib/iomgr/wakeup_fd_posix.cc + - src/core/lib/json/json.cc + - src/core/lib/json/json_reader.cc + - src/core/lib/json/json_string.cc + - src/core/lib/json/json_writer.cc + - src/core/lib/slice/b64.cc + - src/core/lib/slice/percent_encoding.cc + - src/core/lib/slice/slice.cc + - src/core/lib/slice/slice_buffer.cc + - src/core/lib/slice/slice_hash_table.cc + - src/core/lib/slice/slice_intern.cc + - src/core/lib/slice/slice_string_helpers.cc + - src/core/lib/surface/alarm.cc + - src/core/lib/surface/api_trace.cc + - src/core/lib/surface/byte_buffer.cc + - src/core/lib/surface/byte_buffer_reader.cc + - src/core/lib/surface/call.cc + - src/core/lib/surface/call_details.cc + - src/core/lib/surface/call_log_batch.cc + - src/core/lib/surface/channel.cc + - src/core/lib/surface/channel_init.cc + - src/core/lib/surface/channel_ping.cc + - src/core/lib/surface/channel_stack_type.cc + - src/core/lib/surface/completion_queue.cc + - src/core/lib/surface/completion_queue_factory.cc + - src/core/lib/surface/event_string.cc - src/core/lib/surface/lame_client.cc - - src/core/lib/surface/metadata_array.c - - src/core/lib/surface/server.c - - src/core/lib/surface/validate_metadata.c - - src/core/lib/surface/version.c - - src/core/lib/transport/bdp_estimator.c - - src/core/lib/transport/byte_stream.c - - src/core/lib/transport/connectivity_state.c - - src/core/lib/transport/error_utils.c - - src/core/lib/transport/metadata.c - - src/core/lib/transport/metadata_batch.c - - src/core/lib/transport/pid_controller.c - - src/core/lib/transport/service_config.c - - src/core/lib/transport/static_metadata.c - - src/core/lib/transport/status_conversion.c - - src/core/lib/transport/timeout_encoding.c - - src/core/lib/transport/transport.c - - src/core/lib/transport/transport_op_string.c + - src/core/lib/surface/metadata_array.cc + - src/core/lib/surface/server.cc + - src/core/lib/surface/validate_metadata.cc + - src/core/lib/surface/version.cc + - src/core/lib/transport/bdp_estimator.cc + - src/core/lib/transport/byte_stream.cc + - src/core/lib/transport/connectivity_state.cc + - src/core/lib/transport/error_utils.cc + - src/core/lib/transport/metadata.cc + - src/core/lib/transport/metadata_batch.cc + - src/core/lib/transport/pid_controller.cc + - src/core/lib/transport/service_config.cc + - src/core/lib/transport/static_metadata.cc + - src/core/lib/transport/status_conversion.cc + - src/core/lib/transport/timeout_encoding.cc + - src/core/lib/transport/transport.cc + - src/core/lib/transport/transport_op_string.cc deps: - gpr filegroups: @@ -480,26 +478,26 @@ filegroups: - src/core/ext/filters/client_channel/subchannel_index.h - src/core/ext/filters/client_channel/uri_parser.h src: - - src/core/ext/filters/client_channel/channel_connectivity.c - - src/core/ext/filters/client_channel/client_channel.c - - src/core/ext/filters/client_channel/client_channel_factory.c - - src/core/ext/filters/client_channel/client_channel_plugin.c - - src/core/ext/filters/client_channel/connector.c - - src/core/ext/filters/client_channel/http_connect_handshaker.c - - src/core/ext/filters/client_channel/http_proxy.c - - src/core/ext/filters/client_channel/lb_policy.c - - src/core/ext/filters/client_channel/lb_policy_factory.c - - src/core/ext/filters/client_channel/lb_policy_registry.c - - src/core/ext/filters/client_channel/parse_address.c - - src/core/ext/filters/client_channel/proxy_mapper.c - - src/core/ext/filters/client_channel/proxy_mapper_registry.c - - src/core/ext/filters/client_channel/resolver.c - - src/core/ext/filters/client_channel/resolver_factory.c - - src/core/ext/filters/client_channel/resolver_registry.c - - src/core/ext/filters/client_channel/retry_throttle.c - - src/core/ext/filters/client_channel/subchannel.c - - src/core/ext/filters/client_channel/subchannel_index.c - - src/core/ext/filters/client_channel/uri_parser.c + - src/core/ext/filters/client_channel/channel_connectivity.cc + - src/core/ext/filters/client_channel/client_channel.cc + - src/core/ext/filters/client_channel/client_channel_factory.cc + - src/core/ext/filters/client_channel/client_channel_plugin.cc + - src/core/ext/filters/client_channel/connector.cc + - src/core/ext/filters/client_channel/http_connect_handshaker.cc + - src/core/ext/filters/client_channel/http_proxy.cc + - src/core/ext/filters/client_channel/lb_policy.cc + - src/core/ext/filters/client_channel/lb_policy_factory.cc + - src/core/ext/filters/client_channel/lb_policy_registry.cc + - src/core/ext/filters/client_channel/parse_address.cc + - src/core/ext/filters/client_channel/proxy_mapper.cc + - src/core/ext/filters/client_channel/proxy_mapper_registry.cc + - src/core/ext/filters/client_channel/resolver.cc + - src/core/ext/filters/client_channel/resolver_factory.cc + - src/core/ext/filters/client_channel/resolver_registry.cc + - src/core/ext/filters/client_channel/retry_throttle.cc + - src/core/ext/filters/client_channel/subchannel.cc + - src/core/ext/filters/client_channel/subchannel_index.cc + - src/core/ext/filters/client_channel/uri_parser.cc plugin: grpc_client_channel uses: - grpc_base @@ -521,7 +519,7 @@ filegroups: headers: - src/core/ext/filters/deadline/deadline_filter.h src: - - src/core/ext/filters/deadline/deadline_filter.c + - src/core/ext/filters/deadline/deadline_filter.cc plugin: grpc_deadline_filter uses: - grpc_base @@ -531,10 +529,10 @@ filegroups: - src/core/ext/filters/http/message_compress/message_compress_filter.h - src/core/ext/filters/http/server/http_server_filter.h src: - - src/core/ext/filters/http/client/http_client_filter.c - - src/core/ext/filters/http/http_filters_plugin.c - - src/core/ext/filters/http/message_compress/message_compress_filter.c - - src/core/ext/filters/http/server/http_server_filter.c + - src/core/ext/filters/http/client/http_client_filter.cc + - src/core/ext/filters/http/http_filters_plugin.cc + - src/core/ext/filters/http/message_compress/message_compress_filter.cc + - src/core/ext/filters/http/server/http_server_filter.cc plugin: grpc_http_filters uses: - grpc_base @@ -547,12 +545,12 @@ filegroups: - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h src: - - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c + - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc plugin: grpc_lb_policy_grpclb uses: - grpc_base @@ -568,12 +566,12 @@ filegroups: - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h src: - - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c - - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c + - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc plugin: grpc_lb_policy_grpclb uses: - grpc_base @@ -583,14 +581,14 @@ filegroups: - grpc_resolver_fake - name: grpc_lb_policy_pick_first src: - - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c + - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc plugin: grpc_lb_policy_pick_first uses: - grpc_base - grpc_client_channel - name: grpc_lb_policy_round_robin src: - - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c + - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc plugin: grpc_lb_policy_round_robin uses: - grpc_base @@ -599,7 +597,7 @@ filegroups: headers: - src/core/ext/filters/max_age/max_age_filter.h src: - - src/core/ext/filters/max_age/max_age_filter.c + - src/core/ext/filters/max_age/max_age_filter.cc plugin: grpc_max_age_filter uses: - grpc_base @@ -607,7 +605,7 @@ filegroups: headers: - src/core/ext/filters/message_size/message_size_filter.h src: - - src/core/ext/filters/message_size/message_size_filter.c + - src/core/ext/filters/message_size/message_size_filter.cc plugin: grpc_message_size_filter uses: - grpc_base @@ -616,17 +614,17 @@ filegroups: - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h src: - - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c - - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c - - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c - - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c + - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc + - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc + - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc + - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc plugin: grpc_resolver_dns_ares uses: - grpc_base - grpc_client_channel - name: grpc_resolver_dns_native src: - - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c + - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc plugin: grpc_resolver_dns_native uses: - grpc_base @@ -635,14 +633,14 @@ filegroups: headers: - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h src: - - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c + - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc plugin: grpc_resolver_fake uses: - grpc_base - grpc_client_channel - name: grpc_resolver_sockaddr src: - - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c + - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc plugin: grpc_resolver_sockaddr uses: - grpc_base @@ -671,30 +669,30 @@ filegroups: - src/core/lib/security/transport/tsi_error.h - src/core/lib/security/util/json_util.h src: - - src/core/lib/http/httpcli_security_connector.c - - src/core/lib/security/context/security_context.c - - src/core/lib/security/credentials/composite/composite_credentials.c - - src/core/lib/security/credentials/credentials.c - - src/core/lib/security/credentials/credentials_metadata.c - - src/core/lib/security/credentials/fake/fake_credentials.c - - src/core/lib/security/credentials/google_default/credentials_generic.c - - src/core/lib/security/credentials/google_default/google_default_credentials.c - - src/core/lib/security/credentials/iam/iam_credentials.c - - src/core/lib/security/credentials/jwt/json_token.c - - src/core/lib/security/credentials/jwt/jwt_credentials.c - - src/core/lib/security/credentials/jwt/jwt_verifier.c - - src/core/lib/security/credentials/oauth2/oauth2_credentials.c - - src/core/lib/security/credentials/plugin/plugin_credentials.c - - src/core/lib/security/credentials/ssl/ssl_credentials.c - - src/core/lib/security/transport/client_auth_filter.c - - src/core/lib/security/transport/lb_targets_info.c - - src/core/lib/security/transport/secure_endpoint.c - - src/core/lib/security/transport/security_connector.c - - src/core/lib/security/transport/security_handshaker.c - - src/core/lib/security/transport/server_auth_filter.c - - src/core/lib/security/transport/tsi_error.c - - src/core/lib/security/util/json_util.c - - src/core/lib/surface/init_secure.c + - src/core/lib/http/httpcli_security_connector.cc + - src/core/lib/security/context/security_context.cc + - src/core/lib/security/credentials/composite/composite_credentials.cc + - src/core/lib/security/credentials/credentials.cc + - src/core/lib/security/credentials/credentials_metadata.cc + - src/core/lib/security/credentials/fake/fake_credentials.cc + - src/core/lib/security/credentials/google_default/credentials_generic.cc + - src/core/lib/security/credentials/google_default/google_default_credentials.cc + - src/core/lib/security/credentials/iam/iam_credentials.cc + - src/core/lib/security/credentials/jwt/json_token.cc + - src/core/lib/security/credentials/jwt/jwt_credentials.cc + - src/core/lib/security/credentials/jwt/jwt_verifier.cc + - src/core/lib/security/credentials/oauth2/oauth2_credentials.cc + - src/core/lib/security/credentials/plugin/plugin_credentials.cc + - src/core/lib/security/credentials/ssl/ssl_credentials.cc + - src/core/lib/security/transport/client_auth_filter.cc + - src/core/lib/security/transport/lb_targets_info.cc + - src/core/lib/security/transport/secure_endpoint.cc + - src/core/lib/security/transport/security_connector.cc + - src/core/lib/security/transport/security_handshaker.cc + - src/core/lib/security/transport/server_auth_filter.cc + - src/core/lib/security/transport/tsi_error.cc + - src/core/lib/security/util/json_util.cc + - src/core/lib/surface/init_secure.cc secure: true uses: - grpc_base @@ -704,7 +702,7 @@ filegroups: headers: - src/core/ext/filters/workarounds/workaround_utils.h src: - - src/core/ext/filters/workarounds/workaround_utils.c + - src/core/ext/filters/workarounds/workaround_utils.cc uses: - grpc_base - name: grpc_server_load_reporting @@ -712,8 +710,8 @@ filegroups: - src/core/ext/filters/load_reporting/server_load_reporting_filter.h - src/core/ext/filters/load_reporting/server_load_reporting_plugin.h src: - - src/core/ext/filters/load_reporting/server_load_reporting_filter.c - - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c + - src/core/ext/filters/load_reporting/server_load_reporting_filter.cc + - src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc plugin: grpc_server_load_reporting_plugin uses: - grpc_base @@ -736,21 +734,21 @@ filegroups: - test/core/util/slice_splitter.h - test/core/util/trickle_endpoint.h src: - - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c - - test/core/end2end/cq_verifier.c - - test/core/end2end/fixtures/http_proxy_fixture.c - - test/core/end2end/fixtures/proxy.c - - test/core/iomgr/endpoint_tests.c - - test/core/util/debugger_macros.c - - test/core/util/grpc_profiler.c - - test/core/util/memory_counters.c - - test/core/util/mock_endpoint.c - - test/core/util/parse_hexstring.c - - test/core/util/passthru_endpoint.c - - test/core/util/port.c - - test/core/util/port_server_client.c - - test/core/util/slice_splitter.c - - test/core/util/trickle_endpoint.c + - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc + - test/core/end2end/cq_verifier.cc + - test/core/end2end/fixtures/http_proxy_fixture.cc + - test/core/end2end/fixtures/proxy.cc + - test/core/iomgr/endpoint_tests.cc + - test/core/util/debugger_macros.cc + - test/core/util/grpc_profiler.cc + - test/core/util/memory_counters.cc + - test/core/util/mock_endpoint.cc + - test/core/util/parse_hexstring.cc + - test/core/util/passthru_endpoint.cc + - test/core/util/port.cc + - test/core/util/port_server_client.cc + - test/core/util/slice_splitter.cc + - test/core/util/trickle_endpoint.cc deps: - gpr_test_util - gpr @@ -760,7 +758,7 @@ filegroups: - grpc_transport_chttp2 - name: grpc_trace src: - - src/core/lib/debug/trace.c + - src/core/lib/debug/trace.cc deps: - gpr filegroups: @@ -792,28 +790,28 @@ filegroups: - src/core/ext/transport/chttp2/transport/stream_map.h - src/core/ext/transport/chttp2/transport/varint.h src: - - src/core/ext/transport/chttp2/transport/bin_decoder.c - - src/core/ext/transport/chttp2/transport/bin_encoder.c - - src/core/ext/transport/chttp2/transport/chttp2_plugin.c - - src/core/ext/transport/chttp2/transport/chttp2_transport.c - - src/core/ext/transport/chttp2/transport/flow_control.c - - src/core/ext/transport/chttp2/transport/frame_data.c - - src/core/ext/transport/chttp2/transport/frame_goaway.c - - src/core/ext/transport/chttp2/transport/frame_ping.c - - src/core/ext/transport/chttp2/transport/frame_rst_stream.c - - src/core/ext/transport/chttp2/transport/frame_settings.c - - src/core/ext/transport/chttp2/transport/frame_window_update.c - - src/core/ext/transport/chttp2/transport/hpack_encoder.c - - src/core/ext/transport/chttp2/transport/hpack_parser.c - - src/core/ext/transport/chttp2/transport/hpack_table.c - - src/core/ext/transport/chttp2/transport/http2_settings.c - - src/core/ext/transport/chttp2/transport/huffsyms.c - - src/core/ext/transport/chttp2/transport/incoming_metadata.c - - src/core/ext/transport/chttp2/transport/parsing.c - - src/core/ext/transport/chttp2/transport/stream_lists.c - - src/core/ext/transport/chttp2/transport/stream_map.c - - src/core/ext/transport/chttp2/transport/varint.c - - src/core/ext/transport/chttp2/transport/writing.c + - src/core/ext/transport/chttp2/transport/bin_decoder.cc + - src/core/ext/transport/chttp2/transport/bin_encoder.cc + - src/core/ext/transport/chttp2/transport/chttp2_plugin.cc + - src/core/ext/transport/chttp2/transport/chttp2_transport.cc + - src/core/ext/transport/chttp2/transport/flow_control.cc + - src/core/ext/transport/chttp2/transport/frame_data.cc + - src/core/ext/transport/chttp2/transport/frame_goaway.cc + - src/core/ext/transport/chttp2/transport/frame_ping.cc + - src/core/ext/transport/chttp2/transport/frame_rst_stream.cc + - src/core/ext/transport/chttp2/transport/frame_settings.cc + - src/core/ext/transport/chttp2/transport/frame_window_update.cc + - src/core/ext/transport/chttp2/transport/hpack_encoder.cc + - src/core/ext/transport/chttp2/transport/hpack_parser.cc + - src/core/ext/transport/chttp2/transport/hpack_table.cc + - src/core/ext/transport/chttp2/transport/http2_settings.cc + - src/core/ext/transport/chttp2/transport/huffsyms.cc + - src/core/ext/transport/chttp2/transport/incoming_metadata.cc + - src/core/ext/transport/chttp2/transport/parsing.cc + - src/core/ext/transport/chttp2/transport/stream_lists.cc + - src/core/ext/transport/chttp2/transport/stream_map.cc + - src/core/ext/transport/chttp2/transport/varint.cc + - src/core/ext/transport/chttp2/transport/writing.cc plugin: grpc_chttp2_plugin uses: - grpc_base @@ -823,22 +821,22 @@ filegroups: headers: - src/core/ext/transport/chttp2/alpn/alpn.h src: - - src/core/ext/transport/chttp2/alpn/alpn.c + - src/core/ext/transport/chttp2/alpn/alpn.cc deps: - gpr - name: grpc_transport_chttp2_client_connector headers: - src/core/ext/transport/chttp2/client/chttp2_connector.h src: - - src/core/ext/transport/chttp2/client/chttp2_connector.c + - src/core/ext/transport/chttp2/client/chttp2_connector.cc uses: - grpc_transport_chttp2 - grpc_base - grpc_client_channel - name: grpc_transport_chttp2_client_insecure src: - - src/core/ext/transport/chttp2/client/insecure/channel_create.c - - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c + - src/core/ext/transport/chttp2/client/insecure/channel_create.cc + - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc uses: - grpc_transport_chttp2_client_connector - grpc_transport_chttp2 @@ -846,7 +844,7 @@ filegroups: - grpc_client_channel - name: grpc_transport_chttp2_client_secure src: - - src/core/ext/transport/chttp2/client/secure/secure_channel_create.c + - src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc uses: - grpc_transport_chttp2 - grpc_base @@ -857,21 +855,21 @@ filegroups: headers: - src/core/ext/transport/chttp2/server/chttp2_server.h src: - - src/core/ext/transport/chttp2/server/chttp2_server.c + - src/core/ext/transport/chttp2/server/chttp2_server.cc uses: - grpc_transport_chttp2 - grpc_base - name: grpc_transport_chttp2_server_insecure src: - - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c - - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c + - src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc + - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc uses: - grpc_transport_chttp2 - grpc_base - grpc_transport_chttp2_server - name: grpc_transport_chttp2_server_secure src: - - src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c + - src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc uses: - grpc_transport_chttp2 - grpc_base @@ -886,17 +884,17 @@ filegroups: - src/core/ext/transport/cronet/transport/cronet_transport.h - third_party/objective_c/Cronet/bidirectional_stream_c.h src: - - src/core/ext/transport/cronet/client/secure/cronet_channel_create.c - - src/core/ext/transport/cronet/transport/cronet_api_dummy.c - - src/core/ext/transport/cronet/transport/cronet_transport.c + - src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc + - src/core/ext/transport/cronet/transport/cronet_api_dummy.cc + - src/core/ext/transport/cronet/transport/cronet_transport.cc filegroups: - grpc_base - grpc_transport_chttp2 - grpc_http_filters - name: grpc_transport_inproc src: - - src/core/ext/transport/inproc/inproc_plugin.c - - src/core/ext/transport/inproc/inproc_transport.c + - src/core/ext/transport/inproc/inproc_plugin.cc + - src/core/ext/transport/inproc/inproc_transport.cc plugin: grpc_inproc_plugin uses: - grpc_transport_inproc_headers @@ -910,7 +908,7 @@ filegroups: headers: - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h src: - - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c + - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc plugin: grpc_workaround_cronet_compression_filter uses: - grpc_base @@ -944,10 +942,10 @@ filegroups: - src/core/tsi/ssl_types.h - src/core/tsi/transport_security_grpc.h src: - - src/core/tsi/fake_transport_security.c - - src/core/tsi/gts_transport_security.c - - src/core/tsi/ssl_transport_security.c - - src/core/tsi/transport_security_grpc.c + - src/core/tsi/fake_transport_security.cc + - src/core/tsi/gts_transport_security.cc + - src/core/tsi/ssl_transport_security.cc + - src/core/tsi/transport_security_grpc.cc deps: - gpr plugin: grpc_tsi_gts @@ -962,8 +960,8 @@ filegroups: - src/core/tsi/transport_security_adapter.h - src/core/tsi/transport_security_interface.h src: - - src/core/tsi/transport_security.c - - src/core/tsi/transport_security_adapter.c + - src/core/tsi/transport_security.cc + - src/core/tsi/transport_security_adapter.cc deps: - gpr secure: true @@ -1152,7 +1150,7 @@ libs: build: all language: c src: - - src/core/lib/surface/init.c + - src/core/lib/surface/init.cc baselib: true deps_linkage: static dll: true @@ -1188,7 +1186,7 @@ libs: build: all language: c src: - - src/core/lib/surface/init.c + - src/core/lib/surface/init.cc baselib: true deps_linkage: static dll: true @@ -1256,8 +1254,8 @@ libs: build: all language: c src: - - src/core/lib/surface/init.c - - src/core/lib/surface/init_unsecure.c + - src/core/lib/surface/init.cc + - src/core/lib/surface/init_unsecure.cc baselib: true deps_linkage: static dll: true diff --git a/config.m4 b/config.m4 index edd4c64f26..ff4dcec4a4 100644 --- a/config.m4 +++ b/config.m4 @@ -39,312 +39,310 @@ if test "$PHP_GRPC" != "no"; then src/php/ext/grpc/server.c \ src/php/ext/grpc/server_credentials.c \ src/php/ext/grpc/timeval.c \ - src/core/lib/profiling/basic_timers.c \ - src/core/lib/profiling/stap_timers.c \ - src/core/lib/support/alloc.c \ - src/core/lib/support/arena.c \ - src/core/lib/support/atm.c \ - src/core/lib/support/avl.c \ - src/core/lib/support/backoff.c \ - src/core/lib/support/cmdline.c \ - src/core/lib/support/cpu_iphone.c \ - src/core/lib/support/cpu_linux.c \ - src/core/lib/support/cpu_posix.c \ - src/core/lib/support/cpu_windows.c \ - src/core/lib/support/env_linux.c \ - src/core/lib/support/env_posix.c \ - src/core/lib/support/env_windows.c \ - src/core/lib/support/histogram.c \ - src/core/lib/support/host_port.c \ - src/core/lib/support/log.c \ - src/core/lib/support/log_android.c \ - src/core/lib/support/log_linux.c \ - src/core/lib/support/log_posix.c \ - src/core/lib/support/log_windows.c \ - src/core/lib/support/mpscq.c \ - src/core/lib/support/murmur_hash.c \ - src/core/lib/support/stack_lockfree.c \ - src/core/lib/support/string.c \ - src/core/lib/support/string_posix.c \ - src/core/lib/support/string_util_windows.c \ - src/core/lib/support/string_windows.c \ - src/core/lib/support/subprocess_posix.c \ - src/core/lib/support/subprocess_windows.c \ - src/core/lib/support/sync.c \ - src/core/lib/support/sync_posix.c \ - src/core/lib/support/sync_windows.c \ - src/core/lib/support/thd.c \ - src/core/lib/support/thd_posix.c \ - src/core/lib/support/thd_windows.c \ - src/core/lib/support/time.c \ - src/core/lib/support/time_posix.c \ - src/core/lib/support/time_precise.c \ - src/core/lib/support/time_windows.c \ - src/core/lib/support/tls_pthread.c \ - src/core/lib/support/tmpfile_msys.c \ - src/core/lib/support/tmpfile_posix.c \ - src/core/lib/support/tmpfile_windows.c \ - src/core/lib/support/wrap_memcpy.c \ - src/core/lib/surface/init.c \ - src/core/lib/channel/channel_args.c \ - src/core/lib/channel/channel_stack.c \ - src/core/lib/channel/channel_stack_builder.c \ - src/core/lib/channel/connected_channel.c \ - src/core/lib/channel/handshaker.c \ - src/core/lib/channel/handshaker_factory.c \ - src/core/lib/channel/handshaker_registry.c \ - src/core/lib/compression/compression.c \ - src/core/lib/compression/message_compress.c \ - src/core/lib/compression/stream_compression.c \ - src/core/lib/compression/stream_compression_gzip.c \ - src/core/lib/compression/stream_compression_identity.c \ - src/core/lib/debug/stats.c \ - src/core/lib/debug/stats_data.c \ - src/core/lib/http/format_request.c \ - src/core/lib/http/httpcli.c \ - src/core/lib/http/parser.c \ - src/core/lib/iomgr/call_combiner.c \ - src/core/lib/iomgr/closure.c \ - src/core/lib/iomgr/combiner.c \ - src/core/lib/iomgr/endpoint.c \ - src/core/lib/iomgr/endpoint_pair_posix.c \ - src/core/lib/iomgr/endpoint_pair_uv.c \ - src/core/lib/iomgr/endpoint_pair_windows.c \ - src/core/lib/iomgr/error.c \ - src/core/lib/iomgr/ev_epoll1_linux.c \ - src/core/lib/iomgr/ev_epollex_linux.c \ - src/core/lib/iomgr/ev_epollsig_linux.c \ - src/core/lib/iomgr/ev_poll_posix.c \ - src/core/lib/iomgr/ev_posix.c \ - src/core/lib/iomgr/ev_windows.c \ - src/core/lib/iomgr/exec_ctx.c \ - src/core/lib/iomgr/executor.c \ - src/core/lib/iomgr/gethostname_fallback.c \ - src/core/lib/iomgr/gethostname_host_name_max.c \ - src/core/lib/iomgr/gethostname_sysconf.c \ - src/core/lib/iomgr/iocp_windows.c \ - src/core/lib/iomgr/iomgr.c \ - src/core/lib/iomgr/iomgr_posix.c \ - src/core/lib/iomgr/iomgr_uv.c \ - src/core/lib/iomgr/iomgr_windows.c \ - src/core/lib/iomgr/is_epollexclusive_available.c \ - src/core/lib/iomgr/load_file.c \ - src/core/lib/iomgr/lockfree_event.c \ - src/core/lib/iomgr/network_status_tracker.c \ - src/core/lib/iomgr/polling_entity.c \ - src/core/lib/iomgr/pollset_set_uv.c \ - src/core/lib/iomgr/pollset_set_windows.c \ - src/core/lib/iomgr/pollset_uv.c \ - src/core/lib/iomgr/pollset_windows.c \ - src/core/lib/iomgr/resolve_address_posix.c \ - src/core/lib/iomgr/resolve_address_uv.c \ - src/core/lib/iomgr/resolve_address_windows.c \ - src/core/lib/iomgr/resource_quota.c \ - src/core/lib/iomgr/sockaddr_utils.c \ - src/core/lib/iomgr/socket_factory_posix.c \ - src/core/lib/iomgr/socket_mutator.c \ - src/core/lib/iomgr/socket_utils_common_posix.c \ - src/core/lib/iomgr/socket_utils_linux.c \ - src/core/lib/iomgr/socket_utils_posix.c \ - src/core/lib/iomgr/socket_utils_uv.c \ - src/core/lib/iomgr/socket_utils_windows.c \ - src/core/lib/iomgr/socket_windows.c \ - src/core/lib/iomgr/tcp_client_posix.c \ - src/core/lib/iomgr/tcp_client_uv.c \ - src/core/lib/iomgr/tcp_client_windows.c \ - src/core/lib/iomgr/tcp_posix.c \ - src/core/lib/iomgr/tcp_server_posix.c \ - src/core/lib/iomgr/tcp_server_utils_posix_common.c \ - src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ - src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ - src/core/lib/iomgr/tcp_server_uv.c \ - src/core/lib/iomgr/tcp_server_windows.c \ - src/core/lib/iomgr/tcp_uv.c \ - src/core/lib/iomgr/tcp_windows.c \ - src/core/lib/iomgr/time_averaged_stats.c \ - src/core/lib/iomgr/timer_generic.c \ - src/core/lib/iomgr/timer_heap.c \ - src/core/lib/iomgr/timer_manager.c \ - src/core/lib/iomgr/timer_uv.c \ - src/core/lib/iomgr/udp_server.c \ - src/core/lib/iomgr/unix_sockets_posix.c \ - src/core/lib/iomgr/unix_sockets_posix_noop.c \ - src/core/lib/iomgr/wakeup_fd_cv.c \ - src/core/lib/iomgr/wakeup_fd_eventfd.c \ - src/core/lib/iomgr/wakeup_fd_nospecial.c \ - src/core/lib/iomgr/wakeup_fd_pipe.c \ - src/core/lib/iomgr/wakeup_fd_posix.c \ - src/core/lib/json/json.c \ - src/core/lib/json/json_reader.c \ - src/core/lib/json/json_string.c \ - src/core/lib/json/json_writer.c \ - src/core/lib/slice/b64.c \ - src/core/lib/slice/percent_encoding.c \ - src/core/lib/slice/slice.c \ - src/core/lib/slice/slice_buffer.c \ - src/core/lib/slice/slice_hash_table.c \ - src/core/lib/slice/slice_intern.c \ - src/core/lib/slice/slice_string_helpers.c \ - src/core/lib/surface/alarm.c \ - src/core/lib/surface/api_trace.c \ - src/core/lib/surface/byte_buffer.c \ - src/core/lib/surface/byte_buffer_reader.c \ - src/core/lib/surface/call.c \ - src/core/lib/surface/call_details.c \ - src/core/lib/surface/call_log_batch.c \ - src/core/lib/surface/channel.c \ - src/core/lib/surface/channel_init.c \ - src/core/lib/surface/channel_ping.c \ - src/core/lib/surface/channel_stack_type.c \ - src/core/lib/surface/completion_queue.c \ - src/core/lib/surface/completion_queue_factory.c \ - src/core/lib/surface/event_string.c \ + src/core/lib/profiling/basic_timers.cc \ + src/core/lib/profiling/stap_timers.cc \ + src/core/lib/support/alloc.cc \ + src/core/lib/support/arena.cc \ + src/core/lib/support/atm.cc \ + src/core/lib/support/avl.cc \ + src/core/lib/support/backoff.cc \ + src/core/lib/support/cmdline.cc \ + src/core/lib/support/cpu_iphone.cc \ + src/core/lib/support/cpu_linux.cc \ + src/core/lib/support/cpu_posix.cc \ + src/core/lib/support/cpu_windows.cc \ + src/core/lib/support/env_linux.cc \ + src/core/lib/support/env_posix.cc \ + src/core/lib/support/env_windows.cc \ + src/core/lib/support/histogram.cc \ + src/core/lib/support/host_port.cc \ + src/core/lib/support/log.cc \ + src/core/lib/support/log_android.cc \ + src/core/lib/support/log_linux.cc \ + src/core/lib/support/log_posix.cc \ + src/core/lib/support/log_windows.cc \ + src/core/lib/support/mpscq.cc \ + src/core/lib/support/murmur_hash.cc \ + src/core/lib/support/stack_lockfree.cc \ + src/core/lib/support/string.cc \ + src/core/lib/support/string_posix.cc \ + src/core/lib/support/string_util_windows.cc \ + src/core/lib/support/string_windows.cc \ + src/core/lib/support/subprocess_posix.cc \ + src/core/lib/support/subprocess_windows.cc \ + src/core/lib/support/sync.cc \ + src/core/lib/support/sync_posix.cc \ + src/core/lib/support/sync_windows.cc \ + src/core/lib/support/thd.cc \ + src/core/lib/support/thd_posix.cc \ + src/core/lib/support/thd_windows.cc \ + src/core/lib/support/time.cc \ + src/core/lib/support/time_posix.cc \ + src/core/lib/support/time_precise.cc \ + src/core/lib/support/time_windows.cc \ + src/core/lib/support/tls_pthread.cc \ + src/core/lib/support/tmpfile_msys.cc \ + src/core/lib/support/tmpfile_posix.cc \ + src/core/lib/support/tmpfile_windows.cc \ + src/core/lib/support/wrap_memcpy.cc \ + src/core/lib/surface/init.cc \ + src/core/lib/channel/channel_args.cc \ + src/core/lib/channel/channel_stack.cc \ + src/core/lib/channel/channel_stack_builder.cc \ + src/core/lib/channel/connected_channel.cc \ + src/core/lib/channel/handshaker.cc \ + src/core/lib/channel/handshaker_factory.cc \ + src/core/lib/channel/handshaker_registry.cc \ + src/core/lib/compression/compression.cc \ + src/core/lib/compression/message_compress.cc \ + src/core/lib/compression/stream_compression.cc \ + src/core/lib/debug/stats.cc \ + src/core/lib/debug/stats_data.cc \ + src/core/lib/http/format_request.cc \ + src/core/lib/http/httpcli.cc \ + src/core/lib/http/parser.cc \ + src/core/lib/iomgr/call_combiner.cc \ + src/core/lib/iomgr/closure.cc \ + src/core/lib/iomgr/combiner.cc \ + src/core/lib/iomgr/endpoint.cc \ + src/core/lib/iomgr/endpoint_pair_posix.cc \ + src/core/lib/iomgr/endpoint_pair_uv.cc \ + src/core/lib/iomgr/endpoint_pair_windows.cc \ + src/core/lib/iomgr/error.cc \ + src/core/lib/iomgr/ev_epoll1_linux.cc \ + src/core/lib/iomgr/ev_epollex_linux.cc \ + src/core/lib/iomgr/ev_epollsig_linux.cc \ + src/core/lib/iomgr/ev_poll_posix.cc \ + src/core/lib/iomgr/ev_posix.cc \ + src/core/lib/iomgr/ev_windows.cc \ + src/core/lib/iomgr/exec_ctx.cc \ + src/core/lib/iomgr/executor.cc \ + src/core/lib/iomgr/gethostname_fallback.cc \ + src/core/lib/iomgr/gethostname_host_name_max.cc \ + src/core/lib/iomgr/gethostname_sysconf.cc \ + src/core/lib/iomgr/iocp_windows.cc \ + src/core/lib/iomgr/iomgr.cc \ + src/core/lib/iomgr/iomgr_posix.cc \ + src/core/lib/iomgr/iomgr_uv.cc \ + src/core/lib/iomgr/iomgr_windows.cc \ + src/core/lib/iomgr/is_epollexclusive_available.cc \ + src/core/lib/iomgr/load_file.cc \ + src/core/lib/iomgr/lockfree_event.cc \ + src/core/lib/iomgr/network_status_tracker.cc \ + src/core/lib/iomgr/polling_entity.cc \ + src/core/lib/iomgr/pollset_set_uv.cc \ + src/core/lib/iomgr/pollset_set_windows.cc \ + src/core/lib/iomgr/pollset_uv.cc \ + src/core/lib/iomgr/pollset_windows.cc \ + src/core/lib/iomgr/resolve_address_posix.cc \ + src/core/lib/iomgr/resolve_address_uv.cc \ + src/core/lib/iomgr/resolve_address_windows.cc \ + src/core/lib/iomgr/resource_quota.cc \ + src/core/lib/iomgr/sockaddr_utils.cc \ + src/core/lib/iomgr/socket_factory_posix.cc \ + src/core/lib/iomgr/socket_mutator.cc \ + src/core/lib/iomgr/socket_utils_common_posix.cc \ + src/core/lib/iomgr/socket_utils_linux.cc \ + src/core/lib/iomgr/socket_utils_posix.cc \ + src/core/lib/iomgr/socket_utils_uv.cc \ + src/core/lib/iomgr/socket_utils_windows.cc \ + src/core/lib/iomgr/socket_windows.cc \ + src/core/lib/iomgr/tcp_client_posix.cc \ + src/core/lib/iomgr/tcp_client_uv.cc \ + src/core/lib/iomgr/tcp_client_windows.cc \ + src/core/lib/iomgr/tcp_posix.cc \ + src/core/lib/iomgr/tcp_server_posix.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ + src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ + src/core/lib/iomgr/tcp_server_uv.cc \ + src/core/lib/iomgr/tcp_server_windows.cc \ + src/core/lib/iomgr/tcp_uv.cc \ + src/core/lib/iomgr/tcp_windows.cc \ + src/core/lib/iomgr/time_averaged_stats.cc \ + src/core/lib/iomgr/timer_generic.cc \ + src/core/lib/iomgr/timer_heap.cc \ + src/core/lib/iomgr/timer_manager.cc \ + src/core/lib/iomgr/timer_uv.cc \ + src/core/lib/iomgr/udp_server.cc \ + src/core/lib/iomgr/unix_sockets_posix.cc \ + src/core/lib/iomgr/unix_sockets_posix_noop.cc \ + src/core/lib/iomgr/wakeup_fd_cv.cc \ + src/core/lib/iomgr/wakeup_fd_eventfd.cc \ + src/core/lib/iomgr/wakeup_fd_nospecial.cc \ + src/core/lib/iomgr/wakeup_fd_pipe.cc \ + src/core/lib/iomgr/wakeup_fd_posix.cc \ + src/core/lib/json/json.cc \ + src/core/lib/json/json_reader.cc \ + src/core/lib/json/json_string.cc \ + src/core/lib/json/json_writer.cc \ + src/core/lib/slice/b64.cc \ + src/core/lib/slice/percent_encoding.cc \ + src/core/lib/slice/slice.cc \ + src/core/lib/slice/slice_buffer.cc \ + src/core/lib/slice/slice_hash_table.cc \ + src/core/lib/slice/slice_intern.cc \ + src/core/lib/slice/slice_string_helpers.cc \ + src/core/lib/surface/alarm.cc \ + src/core/lib/surface/api_trace.cc \ + src/core/lib/surface/byte_buffer.cc \ + src/core/lib/surface/byte_buffer_reader.cc \ + src/core/lib/surface/call.cc \ + src/core/lib/surface/call_details.cc \ + src/core/lib/surface/call_log_batch.cc \ + src/core/lib/surface/channel.cc \ + src/core/lib/surface/channel_init.cc \ + src/core/lib/surface/channel_ping.cc \ + src/core/lib/surface/channel_stack_type.cc \ + src/core/lib/surface/completion_queue.cc \ + src/core/lib/surface/completion_queue_factory.cc \ + src/core/lib/surface/event_string.cc \ src/core/lib/surface/lame_client.cc \ - src/core/lib/surface/metadata_array.c \ - src/core/lib/surface/server.c \ - src/core/lib/surface/validate_metadata.c \ - src/core/lib/surface/version.c \ - src/core/lib/transport/bdp_estimator.c \ - src/core/lib/transport/byte_stream.c \ - src/core/lib/transport/connectivity_state.c \ - src/core/lib/transport/error_utils.c \ - src/core/lib/transport/metadata.c \ - src/core/lib/transport/metadata_batch.c \ - src/core/lib/transport/pid_controller.c \ - src/core/lib/transport/service_config.c \ - src/core/lib/transport/static_metadata.c \ - src/core/lib/transport/status_conversion.c \ - src/core/lib/transport/timeout_encoding.c \ - src/core/lib/transport/transport.c \ - src/core/lib/transport/transport_op_string.c \ - src/core/lib/debug/trace.c \ - src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \ - src/core/ext/transport/chttp2/transport/bin_decoder.c \ - src/core/ext/transport/chttp2/transport/bin_encoder.c \ - src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ - src/core/ext/transport/chttp2/transport/chttp2_transport.c \ - src/core/ext/transport/chttp2/transport/flow_control.c \ - src/core/ext/transport/chttp2/transport/frame_data.c \ - src/core/ext/transport/chttp2/transport/frame_goaway.c \ - src/core/ext/transport/chttp2/transport/frame_ping.c \ - src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ - src/core/ext/transport/chttp2/transport/frame_settings.c \ - src/core/ext/transport/chttp2/transport/frame_window_update.c \ - src/core/ext/transport/chttp2/transport/hpack_encoder.c \ - src/core/ext/transport/chttp2/transport/hpack_parser.c \ - src/core/ext/transport/chttp2/transport/hpack_table.c \ - src/core/ext/transport/chttp2/transport/http2_settings.c \ - src/core/ext/transport/chttp2/transport/huffsyms.c \ - src/core/ext/transport/chttp2/transport/incoming_metadata.c \ - src/core/ext/transport/chttp2/transport/parsing.c \ - src/core/ext/transport/chttp2/transport/stream_lists.c \ - src/core/ext/transport/chttp2/transport/stream_map.c \ - src/core/ext/transport/chttp2/transport/varint.c \ - src/core/ext/transport/chttp2/transport/writing.c \ - src/core/ext/transport/chttp2/alpn/alpn.c \ - src/core/ext/filters/http/client/http_client_filter.c \ - src/core/ext/filters/http/http_filters_plugin.c \ - src/core/ext/filters/http/message_compress/message_compress_filter.c \ - src/core/ext/filters/http/server/http_server_filter.c \ - src/core/lib/http/httpcli_security_connector.c \ - src/core/lib/security/context/security_context.c \ - src/core/lib/security/credentials/composite/composite_credentials.c \ - src/core/lib/security/credentials/credentials.c \ - src/core/lib/security/credentials/credentials_metadata.c \ - src/core/lib/security/credentials/fake/fake_credentials.c \ - src/core/lib/security/credentials/google_default/credentials_generic.c \ - src/core/lib/security/credentials/google_default/google_default_credentials.c \ - src/core/lib/security/credentials/iam/iam_credentials.c \ - src/core/lib/security/credentials/jwt/json_token.c \ - src/core/lib/security/credentials/jwt/jwt_credentials.c \ - src/core/lib/security/credentials/jwt/jwt_verifier.c \ - src/core/lib/security/credentials/oauth2/oauth2_credentials.c \ - src/core/lib/security/credentials/plugin/plugin_credentials.c \ - src/core/lib/security/credentials/ssl/ssl_credentials.c \ - src/core/lib/security/transport/client_auth_filter.c \ - src/core/lib/security/transport/lb_targets_info.c \ - src/core/lib/security/transport/secure_endpoint.c \ - src/core/lib/security/transport/security_connector.c \ - src/core/lib/security/transport/security_handshaker.c \ - src/core/lib/security/transport/server_auth_filter.c \ - src/core/lib/security/transport/tsi_error.c \ - src/core/lib/security/util/json_util.c \ - src/core/lib/surface/init_secure.c \ - src/core/tsi/fake_transport_security.c \ - src/core/tsi/gts_transport_security.c \ - src/core/tsi/ssl_transport_security.c \ - src/core/tsi/transport_security_grpc.c \ - src/core/tsi/transport_security.c \ - src/core/tsi/transport_security_adapter.c \ - src/core/ext/transport/chttp2/server/chttp2_server.c \ - src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \ - src/core/ext/filters/client_channel/channel_connectivity.c \ - src/core/ext/filters/client_channel/client_channel.c \ - src/core/ext/filters/client_channel/client_channel_factory.c \ - src/core/ext/filters/client_channel/client_channel_plugin.c \ - src/core/ext/filters/client_channel/connector.c \ - src/core/ext/filters/client_channel/http_connect_handshaker.c \ - src/core/ext/filters/client_channel/http_proxy.c \ - src/core/ext/filters/client_channel/lb_policy.c \ - src/core/ext/filters/client_channel/lb_policy_factory.c \ - src/core/ext/filters/client_channel/lb_policy_registry.c \ - src/core/ext/filters/client_channel/parse_address.c \ - src/core/ext/filters/client_channel/proxy_mapper.c \ - src/core/ext/filters/client_channel/proxy_mapper_registry.c \ - src/core/ext/filters/client_channel/resolver.c \ - src/core/ext/filters/client_channel/resolver_factory.c \ - src/core/ext/filters/client_channel/resolver_registry.c \ - src/core/ext/filters/client_channel/retry_throttle.c \ - src/core/ext/filters/client_channel/subchannel.c \ - src/core/ext/filters/client_channel/subchannel_index.c \ - src/core/ext/filters/client_channel/uri_parser.c \ - src/core/ext/filters/deadline/deadline_filter.c \ - src/core/ext/transport/chttp2/client/chttp2_connector.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \ - src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create.c \ - src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \ - src/core/ext/transport/inproc/inproc_plugin.c \ - src/core/ext/transport/inproc/inproc_transport.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \ - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ + src/core/lib/surface/metadata_array.cc \ + src/core/lib/surface/server.cc \ + src/core/lib/surface/validate_metadata.cc \ + src/core/lib/surface/version.cc \ + src/core/lib/transport/bdp_estimator.cc \ + src/core/lib/transport/byte_stream.cc \ + src/core/lib/transport/connectivity_state.cc \ + src/core/lib/transport/error_utils.cc \ + src/core/lib/transport/metadata.cc \ + src/core/lib/transport/metadata_batch.cc \ + src/core/lib/transport/pid_controller.cc \ + src/core/lib/transport/service_config.cc \ + src/core/lib/transport/static_metadata.cc \ + src/core/lib/transport/status_conversion.cc \ + src/core/lib/transport/timeout_encoding.cc \ + src/core/lib/transport/transport.cc \ + src/core/lib/transport/transport_op_string.cc \ + src/core/lib/debug/trace.cc \ + src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc \ + src/core/ext/transport/chttp2/transport/bin_decoder.cc \ + src/core/ext/transport/chttp2/transport/bin_encoder.cc \ + src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ + src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ + src/core/ext/transport/chttp2/transport/flow_control.cc \ + src/core/ext/transport/chttp2/transport/frame_data.cc \ + src/core/ext/transport/chttp2/transport/frame_goaway.cc \ + src/core/ext/transport/chttp2/transport/frame_ping.cc \ + src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ + src/core/ext/transport/chttp2/transport/frame_settings.cc \ + src/core/ext/transport/chttp2/transport/frame_window_update.cc \ + src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ + src/core/ext/transport/chttp2/transport/hpack_parser.cc \ + src/core/ext/transport/chttp2/transport/hpack_table.cc \ + src/core/ext/transport/chttp2/transport/http2_settings.cc \ + src/core/ext/transport/chttp2/transport/huffsyms.cc \ + src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ + src/core/ext/transport/chttp2/transport/parsing.cc \ + src/core/ext/transport/chttp2/transport/stream_lists.cc \ + src/core/ext/transport/chttp2/transport/stream_map.cc \ + src/core/ext/transport/chttp2/transport/varint.cc \ + src/core/ext/transport/chttp2/transport/writing.cc \ + src/core/ext/transport/chttp2/alpn/alpn.cc \ + src/core/ext/filters/http/client/http_client_filter.cc \ + src/core/ext/filters/http/http_filters_plugin.cc \ + src/core/ext/filters/http/message_compress/message_compress_filter.cc \ + src/core/ext/filters/http/server/http_server_filter.cc \ + src/core/lib/http/httpcli_security_connector.cc \ + src/core/lib/security/context/security_context.cc \ + src/core/lib/security/credentials/composite/composite_credentials.cc \ + src/core/lib/security/credentials/credentials.cc \ + src/core/lib/security/credentials/credentials_metadata.cc \ + src/core/lib/security/credentials/fake/fake_credentials.cc \ + src/core/lib/security/credentials/google_default/credentials_generic.cc \ + src/core/lib/security/credentials/google_default/google_default_credentials.cc \ + src/core/lib/security/credentials/iam/iam_credentials.cc \ + src/core/lib/security/credentials/jwt/json_token.cc \ + src/core/lib/security/credentials/jwt/jwt_credentials.cc \ + src/core/lib/security/credentials/jwt/jwt_verifier.cc \ + src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \ + src/core/lib/security/credentials/plugin/plugin_credentials.cc \ + src/core/lib/security/credentials/ssl/ssl_credentials.cc \ + src/core/lib/security/transport/client_auth_filter.cc \ + src/core/lib/security/transport/lb_targets_info.cc \ + src/core/lib/security/transport/secure_endpoint.cc \ + src/core/lib/security/transport/security_connector.cc \ + src/core/lib/security/transport/security_handshaker.cc \ + src/core/lib/security/transport/server_auth_filter.cc \ + src/core/lib/security/transport/tsi_error.cc \ + src/core/lib/security/util/json_util.cc \ + src/core/lib/surface/init_secure.cc \ + src/core/tsi/fake_transport_security.cc \ + src/core/tsi/gts_transport_security.cc \ + src/core/tsi/ssl_transport_security.cc \ + src/core/tsi/transport_security_grpc.cc \ + src/core/tsi/transport_security.cc \ + src/core/tsi/transport_security_adapter.cc \ + src/core/ext/transport/chttp2/server/chttp2_server.cc \ + src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \ + src/core/ext/filters/client_channel/channel_connectivity.cc \ + src/core/ext/filters/client_channel/client_channel.cc \ + src/core/ext/filters/client_channel/client_channel_factory.cc \ + src/core/ext/filters/client_channel/client_channel_plugin.cc \ + src/core/ext/filters/client_channel/connector.cc \ + src/core/ext/filters/client_channel/http_connect_handshaker.cc \ + src/core/ext/filters/client_channel/http_proxy.cc \ + src/core/ext/filters/client_channel/lb_policy.cc \ + src/core/ext/filters/client_channel/lb_policy_factory.cc \ + src/core/ext/filters/client_channel/lb_policy_registry.cc \ + src/core/ext/filters/client_channel/parse_address.cc \ + src/core/ext/filters/client_channel/proxy_mapper.cc \ + src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ + src/core/ext/filters/client_channel/resolver.cc \ + src/core/ext/filters/client_channel/resolver_factory.cc \ + src/core/ext/filters/client_channel/resolver_registry.cc \ + src/core/ext/filters/client_channel/retry_throttle.cc \ + src/core/ext/filters/client_channel/subchannel.cc \ + src/core/ext/filters/client_channel/subchannel_index.cc \ + src/core/ext/filters/client_channel/uri_parser.cc \ + src/core/ext/filters/deadline/deadline_filter.cc \ + src/core/ext/transport/chttp2/client/chttp2_connector.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \ + src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create.cc \ + src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \ + src/core/ext/transport/inproc/inproc_plugin.cc \ + src/core/ext/transport/inproc/inproc_transport.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c \ - src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c \ - src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c \ - src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c \ - src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c \ - src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c \ - src/core/ext/filters/load_reporting/server_load_reporting_filter.c \ - src/core/ext/filters/load_reporting/server_load_reporting_plugin.c \ - src/core/ext/census/base_resources.c \ - src/core/ext/census/context.c \ - src/core/ext/census/gen/census.pb.c \ - src/core/ext/census/gen/trace_context.pb.c \ - src/core/ext/census/grpc_context.c \ - 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 \ - src/core/ext/census/resource.c \ - src/core/ext/census/trace_context.c \ - src/core/ext/census/tracing.c \ - src/core/ext/filters/max_age/max_age_filter.c \ - src/core/ext/filters/message_size/message_size_filter.c \ - src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ - src/core/ext/filters/workarounds/workaround_utils.c \ + src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ + src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ + src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ + src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ + src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \ + src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ + src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ + src/core/ext/census/base_resources.cc \ + src/core/ext/census/context.cc \ + src/core/ext/census/gen/census.pb.cc \ + src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/grpc_context.cc \ + src/core/ext/census/grpc_filter.cc \ + src/core/ext/census/grpc_plugin.cc \ + src/core/ext/census/initialize.cc \ + src/core/ext/census/intrusive_hash_map.cc \ + src/core/ext/census/mlog.cc \ + src/core/ext/census/operation.cc \ + src/core/ext/census/placeholders.cc \ + src/core/ext/census/resource.cc \ + src/core/ext/census/trace_context.cc \ + src/core/ext/census/tracing.cc \ + src/core/ext/filters/max_age/max_age_filter.cc \ + src/core/ext/filters/message_size/message_size_filter.cc \ + src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc \ + src/core/ext/filters/workarounds/workaround_utils.cc \ src/core/plugin_registry/grpc_plugin_registry.cc \ src/boringssl/err_data.c \ third_party/boringssl/crypto/aes/aes.c \ diff --git a/config.w32 b/config.w32 index 7247cfafdb..b710c88781 100644 --- a/config.w32 +++ b/config.w32 @@ -16,312 +16,310 @@ if (PHP_GRPC != "no") { "src\\php\\ext\\grpc\\server.c " + "src\\php\\ext\\grpc\\server_credentials.c " + "src\\php\\ext\\grpc\\timeval.c " + - "src\\core\\lib\\profiling\\basic_timers.c " + - "src\\core\\lib\\profiling\\stap_timers.c " + - "src\\core\\lib\\support\\alloc.c " + - "src\\core\\lib\\support\\arena.c " + - "src\\core\\lib\\support\\atm.c " + - "src\\core\\lib\\support\\avl.c " + - "src\\core\\lib\\support\\backoff.c " + - "src\\core\\lib\\support\\cmdline.c " + - "src\\core\\lib\\support\\cpu_iphone.c " + - "src\\core\\lib\\support\\cpu_linux.c " + - "src\\core\\lib\\support\\cpu_posix.c " + - "src\\core\\lib\\support\\cpu_windows.c " + - "src\\core\\lib\\support\\env_linux.c " + - "src\\core\\lib\\support\\env_posix.c " + - "src\\core\\lib\\support\\env_windows.c " + - "src\\core\\lib\\support\\histogram.c " + - "src\\core\\lib\\support\\host_port.c " + - "src\\core\\lib\\support\\log.c " + - "src\\core\\lib\\support\\log_android.c " + - "src\\core\\lib\\support\\log_linux.c " + - "src\\core\\lib\\support\\log_posix.c " + - "src\\core\\lib\\support\\log_windows.c " + - "src\\core\\lib\\support\\mpscq.c " + - "src\\core\\lib\\support\\murmur_hash.c " + - "src\\core\\lib\\support\\stack_lockfree.c " + - "src\\core\\lib\\support\\string.c " + - "src\\core\\lib\\support\\string_posix.c " + - "src\\core\\lib\\support\\string_util_windows.c " + - "src\\core\\lib\\support\\string_windows.c " + - "src\\core\\lib\\support\\subprocess_posix.c " + - "src\\core\\lib\\support\\subprocess_windows.c " + - "src\\core\\lib\\support\\sync.c " + - "src\\core\\lib\\support\\sync_posix.c " + - "src\\core\\lib\\support\\sync_windows.c " + - "src\\core\\lib\\support\\thd.c " + - "src\\core\\lib\\support\\thd_posix.c " + - "src\\core\\lib\\support\\thd_windows.c " + - "src\\core\\lib\\support\\time.c " + - "src\\core\\lib\\support\\time_posix.c " + - "src\\core\\lib\\support\\time_precise.c " + - "src\\core\\lib\\support\\time_windows.c " + - "src\\core\\lib\\support\\tls_pthread.c " + - "src\\core\\lib\\support\\tmpfile_msys.c " + - "src\\core\\lib\\support\\tmpfile_posix.c " + - "src\\core\\lib\\support\\tmpfile_windows.c " + - "src\\core\\lib\\support\\wrap_memcpy.c " + - "src\\core\\lib\\surface\\init.c " + - "src\\core\\lib\\channel\\channel_args.c " + - "src\\core\\lib\\channel\\channel_stack.c " + - "src\\core\\lib\\channel\\channel_stack_builder.c " + - "src\\core\\lib\\channel\\connected_channel.c " + - "src\\core\\lib\\channel\\handshaker.c " + - "src\\core\\lib\\channel\\handshaker_factory.c " + - "src\\core\\lib\\channel\\handshaker_registry.c " + - "src\\core\\lib\\compression\\compression.c " + - "src\\core\\lib\\compression\\message_compress.c " + - "src\\core\\lib\\compression\\stream_compression.c " + - "src\\core\\lib\\compression\\stream_compression_gzip.c " + - "src\\core\\lib\\compression\\stream_compression_identity.c " + - "src\\core\\lib\\debug\\stats.c " + - "src\\core\\lib\\debug\\stats_data.c " + - "src\\core\\lib\\http\\format_request.c " + - "src\\core\\lib\\http\\httpcli.c " + - "src\\core\\lib\\http\\parser.c " + - "src\\core\\lib\\iomgr\\call_combiner.c " + - "src\\core\\lib\\iomgr\\closure.c " + - "src\\core\\lib\\iomgr\\combiner.c " + - "src\\core\\lib\\iomgr\\endpoint.c " + - "src\\core\\lib\\iomgr\\endpoint_pair_posix.c " + - "src\\core\\lib\\iomgr\\endpoint_pair_uv.c " + - "src\\core\\lib\\iomgr\\endpoint_pair_windows.c " + - "src\\core\\lib\\iomgr\\error.c " + - "src\\core\\lib\\iomgr\\ev_epoll1_linux.c " + - "src\\core\\lib\\iomgr\\ev_epollex_linux.c " + - "src\\core\\lib\\iomgr\\ev_epollsig_linux.c " + - "src\\core\\lib\\iomgr\\ev_poll_posix.c " + - "src\\core\\lib\\iomgr\\ev_posix.c " + - "src\\core\\lib\\iomgr\\ev_windows.c " + - "src\\core\\lib\\iomgr\\exec_ctx.c " + - "src\\core\\lib\\iomgr\\executor.c " + - "src\\core\\lib\\iomgr\\gethostname_fallback.c " + - "src\\core\\lib\\iomgr\\gethostname_host_name_max.c " + - "src\\core\\lib\\iomgr\\gethostname_sysconf.c " + - "src\\core\\lib\\iomgr\\iocp_windows.c " + - "src\\core\\lib\\iomgr\\iomgr.c " + - "src\\core\\lib\\iomgr\\iomgr_posix.c " + - "src\\core\\lib\\iomgr\\iomgr_uv.c " + - "src\\core\\lib\\iomgr\\iomgr_windows.c " + - "src\\core\\lib\\iomgr\\is_epollexclusive_available.c " + - "src\\core\\lib\\iomgr\\load_file.c " + - "src\\core\\lib\\iomgr\\lockfree_event.c " + - "src\\core\\lib\\iomgr\\network_status_tracker.c " + - "src\\core\\lib\\iomgr\\polling_entity.c " + - "src\\core\\lib\\iomgr\\pollset_set_uv.c " + - "src\\core\\lib\\iomgr\\pollset_set_windows.c " + - "src\\core\\lib\\iomgr\\pollset_uv.c " + - "src\\core\\lib\\iomgr\\pollset_windows.c " + - "src\\core\\lib\\iomgr\\resolve_address_posix.c " + - "src\\core\\lib\\iomgr\\resolve_address_uv.c " + - "src\\core\\lib\\iomgr\\resolve_address_windows.c " + - "src\\core\\lib\\iomgr\\resource_quota.c " + - "src\\core\\lib\\iomgr\\sockaddr_utils.c " + - "src\\core\\lib\\iomgr\\socket_factory_posix.c " + - "src\\core\\lib\\iomgr\\socket_mutator.c " + - "src\\core\\lib\\iomgr\\socket_utils_common_posix.c " + - "src\\core\\lib\\iomgr\\socket_utils_linux.c " + - "src\\core\\lib\\iomgr\\socket_utils_posix.c " + - "src\\core\\lib\\iomgr\\socket_utils_uv.c " + - "src\\core\\lib\\iomgr\\socket_utils_windows.c " + - "src\\core\\lib\\iomgr\\socket_windows.c " + - "src\\core\\lib\\iomgr\\tcp_client_posix.c " + - "src\\core\\lib\\iomgr\\tcp_client_uv.c " + - "src\\core\\lib\\iomgr\\tcp_client_windows.c " + - "src\\core\\lib\\iomgr\\tcp_posix.c " + - "src\\core\\lib\\iomgr\\tcp_server_posix.c " + - "src\\core\\lib\\iomgr\\tcp_server_utils_posix_common.c " + - "src\\core\\lib\\iomgr\\tcp_server_utils_posix_ifaddrs.c " + - "src\\core\\lib\\iomgr\\tcp_server_utils_posix_noifaddrs.c " + - "src\\core\\lib\\iomgr\\tcp_server_uv.c " + - "src\\core\\lib\\iomgr\\tcp_server_windows.c " + - "src\\core\\lib\\iomgr\\tcp_uv.c " + - "src\\core\\lib\\iomgr\\tcp_windows.c " + - "src\\core\\lib\\iomgr\\time_averaged_stats.c " + - "src\\core\\lib\\iomgr\\timer_generic.c " + - "src\\core\\lib\\iomgr\\timer_heap.c " + - "src\\core\\lib\\iomgr\\timer_manager.c " + - "src\\core\\lib\\iomgr\\timer_uv.c " + - "src\\core\\lib\\iomgr\\udp_server.c " + - "src\\core\\lib\\iomgr\\unix_sockets_posix.c " + - "src\\core\\lib\\iomgr\\unix_sockets_posix_noop.c " + - "src\\core\\lib\\iomgr\\wakeup_fd_cv.c " + - "src\\core\\lib\\iomgr\\wakeup_fd_eventfd.c " + - "src\\core\\lib\\iomgr\\wakeup_fd_nospecial.c " + - "src\\core\\lib\\iomgr\\wakeup_fd_pipe.c " + - "src\\core\\lib\\iomgr\\wakeup_fd_posix.c " + - "src\\core\\lib\\json\\json.c " + - "src\\core\\lib\\json\\json_reader.c " + - "src\\core\\lib\\json\\json_string.c " + - "src\\core\\lib\\json\\json_writer.c " + - "src\\core\\lib\\slice\\b64.c " + - "src\\core\\lib\\slice\\percent_encoding.c " + - "src\\core\\lib\\slice\\slice.c " + - "src\\core\\lib\\slice\\slice_buffer.c " + - "src\\core\\lib\\slice\\slice_hash_table.c " + - "src\\core\\lib\\slice\\slice_intern.c " + - "src\\core\\lib\\slice\\slice_string_helpers.c " + - "src\\core\\lib\\surface\\alarm.c " + - "src\\core\\lib\\surface\\api_trace.c " + - "src\\core\\lib\\surface\\byte_buffer.c " + - "src\\core\\lib\\surface\\byte_buffer_reader.c " + - "src\\core\\lib\\surface\\call.c " + - "src\\core\\lib\\surface\\call_details.c " + - "src\\core\\lib\\surface\\call_log_batch.c " + - "src\\core\\lib\\surface\\channel.c " + - "src\\core\\lib\\surface\\channel_init.c " + - "src\\core\\lib\\surface\\channel_ping.c " + - "src\\core\\lib\\surface\\channel_stack_type.c " + - "src\\core\\lib\\surface\\completion_queue.c " + - "src\\core\\lib\\surface\\completion_queue_factory.c " + - "src\\core\\lib\\surface\\event_string.c " + + "src\\core\\lib\\profiling\\basic_timers.cc " + + "src\\core\\lib\\profiling\\stap_timers.cc " + + "src\\core\\lib\\support\\alloc.cc " + + "src\\core\\lib\\support\\arena.cc " + + "src\\core\\lib\\support\\atm.cc " + + "src\\core\\lib\\support\\avl.cc " + + "src\\core\\lib\\support\\backoff.cc " + + "src\\core\\lib\\support\\cmdline.cc " + + "src\\core\\lib\\support\\cpu_iphone.cc " + + "src\\core\\lib\\support\\cpu_linux.cc " + + "src\\core\\lib\\support\\cpu_posix.cc " + + "src\\core\\lib\\support\\cpu_windows.cc " + + "src\\core\\lib\\support\\env_linux.cc " + + "src\\core\\lib\\support\\env_posix.cc " + + "src\\core\\lib\\support\\env_windows.cc " + + "src\\core\\lib\\support\\histogram.cc " + + "src\\core\\lib\\support\\host_port.cc " + + "src\\core\\lib\\support\\log.cc " + + "src\\core\\lib\\support\\log_android.cc " + + "src\\core\\lib\\support\\log_linux.cc " + + "src\\core\\lib\\support\\log_posix.cc " + + "src\\core\\lib\\support\\log_windows.cc " + + "src\\core\\lib\\support\\mpscq.cc " + + "src\\core\\lib\\support\\murmur_hash.cc " + + "src\\core\\lib\\support\\stack_lockfree.cc " + + "src\\core\\lib\\support\\string.cc " + + "src\\core\\lib\\support\\string_posix.cc " + + "src\\core\\lib\\support\\string_util_windows.cc " + + "src\\core\\lib\\support\\string_windows.cc " + + "src\\core\\lib\\support\\subprocess_posix.cc " + + "src\\core\\lib\\support\\subprocess_windows.cc " + + "src\\core\\lib\\support\\sync.cc " + + "src\\core\\lib\\support\\sync_posix.cc " + + "src\\core\\lib\\support\\sync_windows.cc " + + "src\\core\\lib\\support\\thd.cc " + + "src\\core\\lib\\support\\thd_posix.cc " + + "src\\core\\lib\\support\\thd_windows.cc " + + "src\\core\\lib\\support\\time.cc " + + "src\\core\\lib\\support\\time_posix.cc " + + "src\\core\\lib\\support\\time_precise.cc " + + "src\\core\\lib\\support\\time_windows.cc " + + "src\\core\\lib\\support\\tls_pthread.cc " + + "src\\core\\lib\\support\\tmpfile_msys.cc " + + "src\\core\\lib\\support\\tmpfile_posix.cc " + + "src\\core\\lib\\support\\tmpfile_windows.cc " + + "src\\core\\lib\\support\\wrap_memcpy.cc " + + "src\\core\\lib\\surface\\init.cc " + + "src\\core\\lib\\channel\\channel_args.cc " + + "src\\core\\lib\\channel\\channel_stack.cc " + + "src\\core\\lib\\channel\\channel_stack_builder.cc " + + "src\\core\\lib\\channel\\connected_channel.cc " + + "src\\core\\lib\\channel\\handshaker.cc " + + "src\\core\\lib\\channel\\handshaker_factory.cc " + + "src\\core\\lib\\channel\\handshaker_registry.cc " + + "src\\core\\lib\\compression\\compression.cc " + + "src\\core\\lib\\compression\\message_compress.cc " + + "src\\core\\lib\\compression\\stream_compression.cc " + + "src\\core\\lib\\debug\\stats.cc " + + "src\\core\\lib\\debug\\stats_data.cc " + + "src\\core\\lib\\http\\format_request.cc " + + "src\\core\\lib\\http\\httpcli.cc " + + "src\\core\\lib\\http\\parser.cc " + + "src\\core\\lib\\iomgr\\call_combiner.cc " + + "src\\core\\lib\\iomgr\\closure.cc " + + "src\\core\\lib\\iomgr\\combiner.cc " + + "src\\core\\lib\\iomgr\\endpoint.cc " + + "src\\core\\lib\\iomgr\\endpoint_pair_posix.cc " + + "src\\core\\lib\\iomgr\\endpoint_pair_uv.cc " + + "src\\core\\lib\\iomgr\\endpoint_pair_windows.cc " + + "src\\core\\lib\\iomgr\\error.cc " + + "src\\core\\lib\\iomgr\\ev_epoll1_linux.cc " + + "src\\core\\lib\\iomgr\\ev_epollex_linux.cc " + + "src\\core\\lib\\iomgr\\ev_epollsig_linux.cc " + + "src\\core\\lib\\iomgr\\ev_poll_posix.cc " + + "src\\core\\lib\\iomgr\\ev_posix.cc " + + "src\\core\\lib\\iomgr\\ev_windows.cc " + + "src\\core\\lib\\iomgr\\exec_ctx.cc " + + "src\\core\\lib\\iomgr\\executor.cc " + + "src\\core\\lib\\iomgr\\gethostname_fallback.cc " + + "src\\core\\lib\\iomgr\\gethostname_host_name_max.cc " + + "src\\core\\lib\\iomgr\\gethostname_sysconf.cc " + + "src\\core\\lib\\iomgr\\iocp_windows.cc " + + "src\\core\\lib\\iomgr\\iomgr.cc " + + "src\\core\\lib\\iomgr\\iomgr_posix.cc " + + "src\\core\\lib\\iomgr\\iomgr_uv.cc " + + "src\\core\\lib\\iomgr\\iomgr_windows.cc " + + "src\\core\\lib\\iomgr\\is_epollexclusive_available.cc " + + "src\\core\\lib\\iomgr\\load_file.cc " + + "src\\core\\lib\\iomgr\\lockfree_event.cc " + + "src\\core\\lib\\iomgr\\network_status_tracker.cc " + + "src\\core\\lib\\iomgr\\polling_entity.cc " + + "src\\core\\lib\\iomgr\\pollset_set_uv.cc " + + "src\\core\\lib\\iomgr\\pollset_set_windows.cc " + + "src\\core\\lib\\iomgr\\pollset_uv.cc " + + "src\\core\\lib\\iomgr\\pollset_windows.cc " + + "src\\core\\lib\\iomgr\\resolve_address_posix.cc " + + "src\\core\\lib\\iomgr\\resolve_address_uv.cc " + + "src\\core\\lib\\iomgr\\resolve_address_windows.cc " + + "src\\core\\lib\\iomgr\\resource_quota.cc " + + "src\\core\\lib\\iomgr\\sockaddr_utils.cc " + + "src\\core\\lib\\iomgr\\socket_factory_posix.cc " + + "src\\core\\lib\\iomgr\\socket_mutator.cc " + + "src\\core\\lib\\iomgr\\socket_utils_common_posix.cc " + + "src\\core\\lib\\iomgr\\socket_utils_linux.cc " + + "src\\core\\lib\\iomgr\\socket_utils_posix.cc " + + "src\\core\\lib\\iomgr\\socket_utils_uv.cc " + + "src\\core\\lib\\iomgr\\socket_utils_windows.cc " + + "src\\core\\lib\\iomgr\\socket_windows.cc " + + "src\\core\\lib\\iomgr\\tcp_client_posix.cc " + + "src\\core\\lib\\iomgr\\tcp_client_uv.cc " + + "src\\core\\lib\\iomgr\\tcp_client_windows.cc " + + "src\\core\\lib\\iomgr\\tcp_posix.cc " + + "src\\core\\lib\\iomgr\\tcp_server_posix.cc " + + "src\\core\\lib\\iomgr\\tcp_server_utils_posix_common.cc " + + "src\\core\\lib\\iomgr\\tcp_server_utils_posix_ifaddrs.cc " + + "src\\core\\lib\\iomgr\\tcp_server_utils_posix_noifaddrs.cc " + + "src\\core\\lib\\iomgr\\tcp_server_uv.cc " + + "src\\core\\lib\\iomgr\\tcp_server_windows.cc " + + "src\\core\\lib\\iomgr\\tcp_uv.cc " + + "src\\core\\lib\\iomgr\\tcp_windows.cc " + + "src\\core\\lib\\iomgr\\time_averaged_stats.cc " + + "src\\core\\lib\\iomgr\\timer_generic.cc " + + "src\\core\\lib\\iomgr\\timer_heap.cc " + + "src\\core\\lib\\iomgr\\timer_manager.cc " + + "src\\core\\lib\\iomgr\\timer_uv.cc " + + "src\\core\\lib\\iomgr\\udp_server.cc " + + "src\\core\\lib\\iomgr\\unix_sockets_posix.cc " + + "src\\core\\lib\\iomgr\\unix_sockets_posix_noop.cc " + + "src\\core\\lib\\iomgr\\wakeup_fd_cv.cc " + + "src\\core\\lib\\iomgr\\wakeup_fd_eventfd.cc " + + "src\\core\\lib\\iomgr\\wakeup_fd_nospecial.cc " + + "src\\core\\lib\\iomgr\\wakeup_fd_pipe.cc " + + "src\\core\\lib\\iomgr\\wakeup_fd_posix.cc " + + "src\\core\\lib\\json\\json.cc " + + "src\\core\\lib\\json\\json_reader.cc " + + "src\\core\\lib\\json\\json_string.cc " + + "src\\core\\lib\\json\\json_writer.cc " + + "src\\core\\lib\\slice\\b64.cc " + + "src\\core\\lib\\slice\\percent_encoding.cc " + + "src\\core\\lib\\slice\\slice.cc " + + "src\\core\\lib\\slice\\slice_buffer.cc " + + "src\\core\\lib\\slice\\slice_hash_table.cc " + + "src\\core\\lib\\slice\\slice_intern.cc " + + "src\\core\\lib\\slice\\slice_string_helpers.cc " + + "src\\core\\lib\\surface\\alarm.cc " + + "src\\core\\lib\\surface\\api_trace.cc " + + "src\\core\\lib\\surface\\byte_buffer.cc " + + "src\\core\\lib\\surface\\byte_buffer_reader.cc " + + "src\\core\\lib\\surface\\call.cc " + + "src\\core\\lib\\surface\\call_details.cc " + + "src\\core\\lib\\surface\\call_log_batch.cc " + + "src\\core\\lib\\surface\\channel.cc " + + "src\\core\\lib\\surface\\channel_init.cc " + + "src\\core\\lib\\surface\\channel_ping.cc " + + "src\\core\\lib\\surface\\channel_stack_type.cc " + + "src\\core\\lib\\surface\\completion_queue.cc " + + "src\\core\\lib\\surface\\completion_queue_factory.cc " + + "src\\core\\lib\\surface\\event_string.cc " + "src\\core\\lib\\surface\\lame_client.cc " + - "src\\core\\lib\\surface\\metadata_array.c " + - "src\\core\\lib\\surface\\server.c " + - "src\\core\\lib\\surface\\validate_metadata.c " + - "src\\core\\lib\\surface\\version.c " + - "src\\core\\lib\\transport\\bdp_estimator.c " + - "src\\core\\lib\\transport\\byte_stream.c " + - "src\\core\\lib\\transport\\connectivity_state.c " + - "src\\core\\lib\\transport\\error_utils.c " + - "src\\core\\lib\\transport\\metadata.c " + - "src\\core\\lib\\transport\\metadata_batch.c " + - "src\\core\\lib\\transport\\pid_controller.c " + - "src\\core\\lib\\transport\\service_config.c " + - "src\\core\\lib\\transport\\static_metadata.c " + - "src\\core\\lib\\transport\\status_conversion.c " + - "src\\core\\lib\\transport\\timeout_encoding.c " + - "src\\core\\lib\\transport\\transport.c " + - "src\\core\\lib\\transport\\transport_op_string.c " + - "src\\core\\lib\\debug\\trace.c " + - "src\\core\\ext\\transport\\chttp2\\server\\secure\\server_secure_chttp2.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\bin_decoder.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\bin_encoder.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\chttp2_plugin.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\chttp2_transport.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\flow_control.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\frame_data.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\frame_goaway.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\frame_ping.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\frame_rst_stream.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\frame_settings.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\frame_window_update.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\hpack_parser.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\hpack_table.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\http2_settings.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\huffsyms.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\incoming_metadata.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\parsing.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\stream_lists.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\stream_map.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\varint.c " + - "src\\core\\ext\\transport\\chttp2\\transport\\writing.c " + - "src\\core\\ext\\transport\\chttp2\\alpn\\alpn.c " + - "src\\core\\ext\\filters\\http\\client\\http_client_filter.c " + - "src\\core\\ext\\filters\\http\\http_filters_plugin.c " + - "src\\core\\ext\\filters\\http\\message_compress\\message_compress_filter.c " + - "src\\core\\ext\\filters\\http\\server\\http_server_filter.c " + - "src\\core\\lib\\http\\httpcli_security_connector.c " + - "src\\core\\lib\\security\\context\\security_context.c " + - "src\\core\\lib\\security\\credentials\\composite\\composite_credentials.c " + - "src\\core\\lib\\security\\credentials\\credentials.c " + - "src\\core\\lib\\security\\credentials\\credentials_metadata.c " + - "src\\core\\lib\\security\\credentials\\fake\\fake_credentials.c " + - "src\\core\\lib\\security\\credentials\\google_default\\credentials_generic.c " + - "src\\core\\lib\\security\\credentials\\google_default\\google_default_credentials.c " + - "src\\core\\lib\\security\\credentials\\iam\\iam_credentials.c " + - "src\\core\\lib\\security\\credentials\\jwt\\json_token.c " + - "src\\core\\lib\\security\\credentials\\jwt\\jwt_credentials.c " + - "src\\core\\lib\\security\\credentials\\jwt\\jwt_verifier.c " + - "src\\core\\lib\\security\\credentials\\oauth2\\oauth2_credentials.c " + - "src\\core\\lib\\security\\credentials\\plugin\\plugin_credentials.c " + - "src\\core\\lib\\security\\credentials\\ssl\\ssl_credentials.c " + - "src\\core\\lib\\security\\transport\\client_auth_filter.c " + - "src\\core\\lib\\security\\transport\\lb_targets_info.c " + - "src\\core\\lib\\security\\transport\\secure_endpoint.c " + - "src\\core\\lib\\security\\transport\\security_connector.c " + - "src\\core\\lib\\security\\transport\\security_handshaker.c " + - "src\\core\\lib\\security\\transport\\server_auth_filter.c " + - "src\\core\\lib\\security\\transport\\tsi_error.c " + - "src\\core\\lib\\security\\util\\json_util.c " + - "src\\core\\lib\\surface\\init_secure.c " + - "src\\core\\tsi\\fake_transport_security.c " + - "src\\core\\tsi\\gts_transport_security.c " + - "src\\core\\tsi\\ssl_transport_security.c " + - "src\\core\\tsi\\transport_security_grpc.c " + - "src\\core\\tsi\\transport_security.c " + - "src\\core\\tsi\\transport_security_adapter.c " + - "src\\core\\ext\\transport\\chttp2\\server\\chttp2_server.c " + - "src\\core\\ext\\transport\\chttp2\\client\\secure\\secure_channel_create.c " + - "src\\core\\ext\\filters\\client_channel\\channel_connectivity.c " + - "src\\core\\ext\\filters\\client_channel\\client_channel.c " + - "src\\core\\ext\\filters\\client_channel\\client_channel_factory.c " + - "src\\core\\ext\\filters\\client_channel\\client_channel_plugin.c " + - "src\\core\\ext\\filters\\client_channel\\connector.c " + - "src\\core\\ext\\filters\\client_channel\\http_connect_handshaker.c " + - "src\\core\\ext\\filters\\client_channel\\http_proxy.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy_factory.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy_registry.c " + - "src\\core\\ext\\filters\\client_channel\\parse_address.c " + - "src\\core\\ext\\filters\\client_channel\\proxy_mapper.c " + - "src\\core\\ext\\filters\\client_channel\\proxy_mapper_registry.c " + - "src\\core\\ext\\filters\\client_channel\\resolver.c " + - "src\\core\\ext\\filters\\client_channel\\resolver_factory.c " + - "src\\core\\ext\\filters\\client_channel\\resolver_registry.c " + - "src\\core\\ext\\filters\\client_channel\\retry_throttle.c " + - "src\\core\\ext\\filters\\client_channel\\subchannel.c " + - "src\\core\\ext\\filters\\client_channel\\subchannel_index.c " + - "src\\core\\ext\\filters\\client_channel\\uri_parser.c " + - "src\\core\\ext\\filters\\deadline\\deadline_filter.c " + - "src\\core\\ext\\transport\\chttp2\\client\\chttp2_connector.c " + - "src\\core\\ext\\transport\\chttp2\\server\\insecure\\server_chttp2.c " + - "src\\core\\ext\\transport\\chttp2\\server\\insecure\\server_chttp2_posix.c " + - "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create.c " + - "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create_posix.c " + - "src\\core\\ext\\transport\\inproc\\inproc_plugin.c " + - "src\\core\\ext\\transport\\inproc\\inproc_transport.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\client_load_reporting_filter.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_channel_secure.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_client_stats.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\load_balancer_api.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\proto\\grpc\\lb\\v1\\load_balancer.pb.c " + + "src\\core\\lib\\surface\\metadata_array.cc " + + "src\\core\\lib\\surface\\server.cc " + + "src\\core\\lib\\surface\\validate_metadata.cc " + + "src\\core\\lib\\surface\\version.cc " + + "src\\core\\lib\\transport\\bdp_estimator.cc " + + "src\\core\\lib\\transport\\byte_stream.cc " + + "src\\core\\lib\\transport\\connectivity_state.cc " + + "src\\core\\lib\\transport\\error_utils.cc " + + "src\\core\\lib\\transport\\metadata.cc " + + "src\\core\\lib\\transport\\metadata_batch.cc " + + "src\\core\\lib\\transport\\pid_controller.cc " + + "src\\core\\lib\\transport\\service_config.cc " + + "src\\core\\lib\\transport\\static_metadata.cc " + + "src\\core\\lib\\transport\\status_conversion.cc " + + "src\\core\\lib\\transport\\timeout_encoding.cc " + + "src\\core\\lib\\transport\\transport.cc " + + "src\\core\\lib\\transport\\transport_op_string.cc " + + "src\\core\\lib\\debug\\trace.cc " + + "src\\core\\ext\\transport\\chttp2\\server\\secure\\server_secure_chttp2.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\bin_decoder.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\bin_encoder.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\chttp2_plugin.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\chttp2_transport.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\flow_control.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_data.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_goaway.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_ping.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_rst_stream.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_settings.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\frame_window_update.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_encoder.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_parser.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\hpack_table.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\http2_settings.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\huffsyms.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\incoming_metadata.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\parsing.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\stream_lists.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\stream_map.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\varint.cc " + + "src\\core\\ext\\transport\\chttp2\\transport\\writing.cc " + + "src\\core\\ext\\transport\\chttp2\\alpn\\alpn.cc " + + "src\\core\\ext\\filters\\http\\client\\http_client_filter.cc " + + "src\\core\\ext\\filters\\http\\http_filters_plugin.cc " + + "src\\core\\ext\\filters\\http\\message_compress\\message_compress_filter.cc " + + "src\\core\\ext\\filters\\http\\server\\http_server_filter.cc " + + "src\\core\\lib\\http\\httpcli_security_connector.cc " + + "src\\core\\lib\\security\\context\\security_context.cc " + + "src\\core\\lib\\security\\credentials\\composite\\composite_credentials.cc " + + "src\\core\\lib\\security\\credentials\\credentials.cc " + + "src\\core\\lib\\security\\credentials\\credentials_metadata.cc " + + "src\\core\\lib\\security\\credentials\\fake\\fake_credentials.cc " + + "src\\core\\lib\\security\\credentials\\google_default\\credentials_generic.cc " + + "src\\core\\lib\\security\\credentials\\google_default\\google_default_credentials.cc " + + "src\\core\\lib\\security\\credentials\\iam\\iam_credentials.cc " + + "src\\core\\lib\\security\\credentials\\jwt\\json_token.cc " + + "src\\core\\lib\\security\\credentials\\jwt\\jwt_credentials.cc " + + "src\\core\\lib\\security\\credentials\\jwt\\jwt_verifier.cc " + + "src\\core\\lib\\security\\credentials\\oauth2\\oauth2_credentials.cc " + + "src\\core\\lib\\security\\credentials\\plugin\\plugin_credentials.cc " + + "src\\core\\lib\\security\\credentials\\ssl\\ssl_credentials.cc " + + "src\\core\\lib\\security\\transport\\client_auth_filter.cc " + + "src\\core\\lib\\security\\transport\\lb_targets_info.cc " + + "src\\core\\lib\\security\\transport\\secure_endpoint.cc " + + "src\\core\\lib\\security\\transport\\security_connector.cc " + + "src\\core\\lib\\security\\transport\\security_handshaker.cc " + + "src\\core\\lib\\security\\transport\\server_auth_filter.cc " + + "src\\core\\lib\\security\\transport\\tsi_error.cc " + + "src\\core\\lib\\security\\util\\json_util.cc " + + "src\\core\\lib\\surface\\init_secure.cc " + + "src\\core\\tsi\\fake_transport_security.cc " + + "src\\core\\tsi\\gts_transport_security.cc " + + "src\\core\\tsi\\ssl_transport_security.cc " + + "src\\core\\tsi\\transport_security_grpc.cc " + + "src\\core\\tsi\\transport_security.cc " + + "src\\core\\tsi\\transport_security_adapter.cc " + + "src\\core\\ext\\transport\\chttp2\\server\\chttp2_server.cc " + + "src\\core\\ext\\transport\\chttp2\\client\\secure\\secure_channel_create.cc " + + "src\\core\\ext\\filters\\client_channel\\channel_connectivity.cc " + + "src\\core\\ext\\filters\\client_channel\\client_channel.cc " + + "src\\core\\ext\\filters\\client_channel\\client_channel_factory.cc " + + "src\\core\\ext\\filters\\client_channel\\client_channel_plugin.cc " + + "src\\core\\ext\\filters\\client_channel\\connector.cc " + + "src\\core\\ext\\filters\\client_channel\\http_connect_handshaker.cc " + + "src\\core\\ext\\filters\\client_channel\\http_proxy.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy_factory.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy_registry.cc " + + "src\\core\\ext\\filters\\client_channel\\parse_address.cc " + + "src\\core\\ext\\filters\\client_channel\\proxy_mapper.cc " + + "src\\core\\ext\\filters\\client_channel\\proxy_mapper_registry.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver_factory.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver_registry.cc " + + "src\\core\\ext\\filters\\client_channel\\retry_throttle.cc " + + "src\\core\\ext\\filters\\client_channel\\subchannel.cc " + + "src\\core\\ext\\filters\\client_channel\\subchannel_index.cc " + + "src\\core\\ext\\filters\\client_channel\\uri_parser.cc " + + "src\\core\\ext\\filters\\deadline\\deadline_filter.cc " + + "src\\core\\ext\\transport\\chttp2\\client\\chttp2_connector.cc " + + "src\\core\\ext\\transport\\chttp2\\server\\insecure\\server_chttp2.cc " + + "src\\core\\ext\\transport\\chttp2\\server\\insecure\\server_chttp2_posix.cc " + + "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create.cc " + + "src\\core\\ext\\transport\\chttp2\\client\\insecure\\channel_create_posix.cc " + + "src\\core\\ext\\transport\\inproc\\inproc_plugin.cc " + + "src\\core\\ext\\transport\\inproc\\inproc_transport.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\client_load_reporting_filter.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_channel_secure.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_client_stats.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\load_balancer_api.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\proto\\grpc\\lb\\v1\\load_balancer.pb.cc " + "third_party\\nanopb\\pb_common.c " + "third_party\\nanopb\\pb_decode.c " + "third_party\\nanopb\\pb_encode.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\fake\\fake_resolver.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\pick_first\\pick_first.c " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\round_robin\\round_robin.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\dns_resolver_ares.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_posix.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_fallback.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\native\\dns_resolver.c " + - "src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr\\sockaddr_resolver.c " + - "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_filter.c " + - "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_plugin.c " + - "src\\core\\ext\\census\\base_resources.c " + - "src\\core\\ext\\census\\context.c " + - "src\\core\\ext\\census\\gen\\census.pb.c " + - "src\\core\\ext\\census\\gen\\trace_context.pb.c " + - "src\\core\\ext\\census\\grpc_context.c " + - "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 " + - "src\\core\\ext\\census\\resource.c " + - "src\\core\\ext\\census\\trace_context.c " + - "src\\core\\ext\\census\\tracing.c " + - "src\\core\\ext\\filters\\max_age\\max_age_filter.c " + - "src\\core\\ext\\filters\\message_size\\message_size_filter.c " + - "src\\core\\ext\\filters\\workarounds\\workaround_cronet_compression_filter.c " + - "src\\core\\ext\\filters\\workarounds\\workaround_utils.c " + + "src\\core\\ext\\filters\\client_channel\\resolver\\fake\\fake_resolver.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\pick_first\\pick_first.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\round_robin\\round_robin.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\dns_resolver_ares.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_ev_driver_posix.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\c_ares\\grpc_ares_wrapper_fallback.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\native\\dns_resolver.cc " + + "src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr\\sockaddr_resolver.cc " + + "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_filter.cc " + + "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_plugin.cc " + + "src\\core\\ext\\census\\base_resources.cc " + + "src\\core\\ext\\census\\context.cc " + + "src\\core\\ext\\census\\gen\\census.pb.cc " + + "src\\core\\ext\\census\\gen\\trace_context.pb.cc " + + "src\\core\\ext\\census\\grpc_context.cc " + + "src\\core\\ext\\census\\grpc_filter.cc " + + "src\\core\\ext\\census\\grpc_plugin.cc " + + "src\\core\\ext\\census\\initialize.cc " + + "src\\core\\ext\\census\\intrusive_hash_map.cc " + + "src\\core\\ext\\census\\mlog.cc " + + "src\\core\\ext\\census\\operation.cc " + + "src\\core\\ext\\census\\placeholders.cc " + + "src\\core\\ext\\census\\resource.cc " + + "src\\core\\ext\\census\\trace_context.cc " + + "src\\core\\ext\\census\\tracing.cc " + + "src\\core\\ext\\filters\\max_age\\max_age_filter.cc " + + "src\\core\\ext\\filters\\message_size\\message_size_filter.cc " + + "src\\core\\ext\\filters\\workarounds\\workaround_cronet_compression_filter.cc " + + "src\\core\\ext\\filters\\workarounds\\workaround_utils.cc " + "src\\core\\plugin_registry\\grpc_plugin_registry.cc " + "src\\boringssl\\err_data.c " + "third_party\\boringssl\\crypto\\aes\\aes.c " + diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 487d35606e..4f7b00dcfd 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -201,52 +201,52 @@ Pod::Spec.new do |s| 'src/core/lib/support/string_windows.h', 'src/core/lib/support/time_precise.h', 'src/core/lib/support/tmpfile.h', - 'src/core/lib/profiling/basic_timers.c', - 'src/core/lib/profiling/stap_timers.c', - 'src/core/lib/support/alloc.c', - 'src/core/lib/support/arena.c', - 'src/core/lib/support/atm.c', - 'src/core/lib/support/avl.c', - 'src/core/lib/support/backoff.c', - 'src/core/lib/support/cmdline.c', - 'src/core/lib/support/cpu_iphone.c', - 'src/core/lib/support/cpu_linux.c', - 'src/core/lib/support/cpu_posix.c', - 'src/core/lib/support/cpu_windows.c', - 'src/core/lib/support/env_linux.c', - 'src/core/lib/support/env_posix.c', - 'src/core/lib/support/env_windows.c', - 'src/core/lib/support/histogram.c', - 'src/core/lib/support/host_port.c', - 'src/core/lib/support/log.c', - 'src/core/lib/support/log_android.c', - 'src/core/lib/support/log_linux.c', - 'src/core/lib/support/log_posix.c', - 'src/core/lib/support/log_windows.c', - 'src/core/lib/support/mpscq.c', - 'src/core/lib/support/murmur_hash.c', - 'src/core/lib/support/stack_lockfree.c', - 'src/core/lib/support/string.c', - 'src/core/lib/support/string_posix.c', - 'src/core/lib/support/string_util_windows.c', - 'src/core/lib/support/string_windows.c', - 'src/core/lib/support/subprocess_posix.c', - 'src/core/lib/support/subprocess_windows.c', - 'src/core/lib/support/sync.c', - 'src/core/lib/support/sync_posix.c', - 'src/core/lib/support/sync_windows.c', - 'src/core/lib/support/thd.c', - 'src/core/lib/support/thd_posix.c', - 'src/core/lib/support/thd_windows.c', - 'src/core/lib/support/time.c', - 'src/core/lib/support/time_posix.c', - 'src/core/lib/support/time_precise.c', - 'src/core/lib/support/time_windows.c', - 'src/core/lib/support/tls_pthread.c', - 'src/core/lib/support/tmpfile_msys.c', - 'src/core/lib/support/tmpfile_posix.c', - 'src/core/lib/support/tmpfile_windows.c', - 'src/core/lib/support/wrap_memcpy.c', + 'src/core/lib/profiling/basic_timers.cc', + 'src/core/lib/profiling/stap_timers.cc', + 'src/core/lib/support/alloc.cc', + 'src/core/lib/support/arena.cc', + 'src/core/lib/support/atm.cc', + 'src/core/lib/support/avl.cc', + 'src/core/lib/support/backoff.cc', + 'src/core/lib/support/cmdline.cc', + 'src/core/lib/support/cpu_iphone.cc', + 'src/core/lib/support/cpu_linux.cc', + 'src/core/lib/support/cpu_posix.cc', + 'src/core/lib/support/cpu_windows.cc', + 'src/core/lib/support/env_linux.cc', + 'src/core/lib/support/env_posix.cc', + 'src/core/lib/support/env_windows.cc', + 'src/core/lib/support/histogram.cc', + 'src/core/lib/support/host_port.cc', + 'src/core/lib/support/log.cc', + 'src/core/lib/support/log_android.cc', + 'src/core/lib/support/log_linux.cc', + 'src/core/lib/support/log_posix.cc', + 'src/core/lib/support/log_windows.cc', + 'src/core/lib/support/mpscq.cc', + 'src/core/lib/support/murmur_hash.cc', + 'src/core/lib/support/stack_lockfree.cc', + 'src/core/lib/support/string.cc', + 'src/core/lib/support/string_posix.cc', + 'src/core/lib/support/string_util_windows.cc', + 'src/core/lib/support/string_windows.cc', + 'src/core/lib/support/subprocess_posix.cc', + 'src/core/lib/support/subprocess_windows.cc', + 'src/core/lib/support/sync.cc', + 'src/core/lib/support/sync_posix.cc', + 'src/core/lib/support/sync_windows.cc', + 'src/core/lib/support/thd.cc', + 'src/core/lib/support/thd_posix.cc', + 'src/core/lib/support/thd_windows.cc', + 'src/core/lib/support/time.cc', + 'src/core/lib/support/time_posix.cc', + 'src/core/lib/support/time_precise.cc', + 'src/core/lib/support/time_windows.cc', + 'src/core/lib/support/tls_pthread.cc', + 'src/core/lib/support/tmpfile_msys.cc', + 'src/core/lib/support/tmpfile_posix.cc', + 'src/core/lib/support/tmpfile_windows.cc', + 'src/core/lib/support/wrap_memcpy.cc', 'src/core/ext/transport/chttp2/transport/bin_decoder.h', 'src/core/ext/transport/chttp2/transport/bin_encoder.h', 'src/core/ext/transport/chttp2/transport/chttp2_transport.h', @@ -470,263 +470,261 @@ Pod::Spec.new do |s| 'src/core/ext/filters/message_size/message_size_filter.h', 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h', 'src/core/ext/filters/workarounds/workaround_utils.h', - 'src/core/lib/surface/init.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/lib/surface/init.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', - 'src/core/lib/http/httpcli_security_connector.c', - 'src/core/lib/security/context/security_context.c', - 'src/core/lib/security/credentials/composite/composite_credentials.c', - 'src/core/lib/security/credentials/credentials.c', - 'src/core/lib/security/credentials/credentials_metadata.c', - 'src/core/lib/security/credentials/fake/fake_credentials.c', - 'src/core/lib/security/credentials/google_default/credentials_generic.c', - 'src/core/lib/security/credentials/google_default/google_default_credentials.c', - 'src/core/lib/security/credentials/iam/iam_credentials.c', - 'src/core/lib/security/credentials/jwt/json_token.c', - 'src/core/lib/security/credentials/jwt/jwt_credentials.c', - 'src/core/lib/security/credentials/jwt/jwt_verifier.c', - 'src/core/lib/security/credentials/oauth2/oauth2_credentials.c', - 'src/core/lib/security/credentials/plugin/plugin_credentials.c', - 'src/core/lib/security/credentials/ssl/ssl_credentials.c', - 'src/core/lib/security/transport/client_auth_filter.c', - 'src/core/lib/security/transport/lb_targets_info.c', - 'src/core/lib/security/transport/secure_endpoint.c', - 'src/core/lib/security/transport/security_connector.c', - 'src/core/lib/security/transport/security_handshaker.c', - 'src/core/lib/security/transport/server_auth_filter.c', - 'src/core/lib/security/transport/tsi_error.c', - 'src/core/lib/security/util/json_util.c', - 'src/core/lib/surface/init_secure.c', - 'src/core/tsi/fake_transport_security.c', - 'src/core/tsi/gts_transport_security.c', - 'src/core/tsi/ssl_transport_security.c', - 'src/core/tsi/transport_security_grpc.c', - 'src/core/tsi/transport_security.c', - 'src/core/tsi/transport_security_adapter.c', - 'src/core/ext/transport/chttp2/server/chttp2_server.c', - 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/chttp2/client/chttp2_connector.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c', - 'src/core/ext/transport/inproc/inproc_plugin.c', - 'src/core/ext/transport/inproc/inproc_transport.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c', - 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c', - 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c', - 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_filter.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.c', - 'src/core/ext/census/base_resources.c', - 'src/core/ext/census/context.c', - 'src/core/ext/census/gen/census.pb.c', - 'src/core/ext/census/gen/trace_context.pb.c', - 'src/core/ext/census/grpc_context.c', - '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', - 'src/core/ext/census/resource.c', - 'src/core/ext/census/trace_context.c', - 'src/core/ext/census/tracing.c', - 'src/core/ext/filters/max_age/max_age_filter.c', - 'src/core/ext/filters/message_size/message_size_filter.c', - 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', - 'src/core/ext/filters/workarounds/workaround_utils.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', + 'src/core/lib/http/httpcli_security_connector.cc', + 'src/core/lib/security/context/security_context.cc', + 'src/core/lib/security/credentials/composite/composite_credentials.cc', + 'src/core/lib/security/credentials/credentials.cc', + 'src/core/lib/security/credentials/credentials_metadata.cc', + 'src/core/lib/security/credentials/fake/fake_credentials.cc', + 'src/core/lib/security/credentials/google_default/credentials_generic.cc', + 'src/core/lib/security/credentials/google_default/google_default_credentials.cc', + 'src/core/lib/security/credentials/iam/iam_credentials.cc', + 'src/core/lib/security/credentials/jwt/json_token.cc', + 'src/core/lib/security/credentials/jwt/jwt_credentials.cc', + 'src/core/lib/security/credentials/jwt/jwt_verifier.cc', + 'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc', + 'src/core/lib/security/credentials/plugin/plugin_credentials.cc', + 'src/core/lib/security/credentials/ssl/ssl_credentials.cc', + 'src/core/lib/security/transport/client_auth_filter.cc', + 'src/core/lib/security/transport/lb_targets_info.cc', + 'src/core/lib/security/transport/secure_endpoint.cc', + 'src/core/lib/security/transport/security_connector.cc', + 'src/core/lib/security/transport/security_handshaker.cc', + 'src/core/lib/security/transport/server_auth_filter.cc', + 'src/core/lib/security/transport/tsi_error.cc', + 'src/core/lib/security/util/json_util.cc', + 'src/core/lib/surface/init_secure.cc', + 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/gts_transport_security.cc', + 'src/core/tsi/ssl_transport_security.cc', + 'src/core/tsi/transport_security_grpc.cc', + 'src/core/tsi/transport_security.cc', + 'src/core/tsi/transport_security_adapter.cc', + 'src/core/ext/transport/chttp2/server/chttp2_server.cc', + 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', + 'src/core/ext/transport/inproc/inproc_plugin.cc', + 'src/core/ext/transport/inproc/inproc_transport.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', + 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', + 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', + 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', + 'src/core/ext/census/base_resources.cc', + 'src/core/ext/census/context.cc', + 'src/core/ext/census/gen/census.pb.cc', + 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/grpc_context.cc', + 'src/core/ext/census/grpc_filter.cc', + 'src/core/ext/census/grpc_plugin.cc', + 'src/core/ext/census/initialize.cc', + 'src/core/ext/census/intrusive_hash_map.cc', + 'src/core/ext/census/mlog.cc', + 'src/core/ext/census/operation.cc', + 'src/core/ext/census/placeholders.cc', + 'src/core/ext/census/resource.cc', + 'src/core/ext/census/trace_context.cc', + 'src/core/ext/census/tracing.cc', + 'src/core/ext/filters/max_age/max_age_filter.cc', + 'src/core/ext/filters/message_size/message_size_filter.cc', + 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc', + 'src/core/ext/filters/workarounds/workaround_utils.cc', 'src/core/plugin_registry/grpc_plugin_registry.cc' ss.private_header_files = 'src/core/lib/profiling/timers.h', diff --git a/grpc.gemspec b/grpc.gemspec index 93c0cb54fd..dc441c7eda 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -100,52 +100,52 @@ Gem::Specification.new do |s| s.files += %w( src/core/lib/support/string_windows.h ) s.files += %w( src/core/lib/support/time_precise.h ) s.files += %w( src/core/lib/support/tmpfile.h ) - s.files += %w( src/core/lib/profiling/basic_timers.c ) - s.files += %w( src/core/lib/profiling/stap_timers.c ) - s.files += %w( src/core/lib/support/alloc.c ) - s.files += %w( src/core/lib/support/arena.c ) - s.files += %w( src/core/lib/support/atm.c ) - s.files += %w( src/core/lib/support/avl.c ) - s.files += %w( src/core/lib/support/backoff.c ) - s.files += %w( src/core/lib/support/cmdline.c ) - s.files += %w( src/core/lib/support/cpu_iphone.c ) - s.files += %w( src/core/lib/support/cpu_linux.c ) - s.files += %w( src/core/lib/support/cpu_posix.c ) - s.files += %w( src/core/lib/support/cpu_windows.c ) - s.files += %w( src/core/lib/support/env_linux.c ) - s.files += %w( src/core/lib/support/env_posix.c ) - s.files += %w( src/core/lib/support/env_windows.c ) - s.files += %w( src/core/lib/support/histogram.c ) - s.files += %w( src/core/lib/support/host_port.c ) - s.files += %w( src/core/lib/support/log.c ) - s.files += %w( src/core/lib/support/log_android.c ) - s.files += %w( src/core/lib/support/log_linux.c ) - s.files += %w( src/core/lib/support/log_posix.c ) - s.files += %w( src/core/lib/support/log_windows.c ) - s.files += %w( src/core/lib/support/mpscq.c ) - s.files += %w( src/core/lib/support/murmur_hash.c ) - s.files += %w( src/core/lib/support/stack_lockfree.c ) - s.files += %w( src/core/lib/support/string.c ) - s.files += %w( src/core/lib/support/string_posix.c ) - s.files += %w( src/core/lib/support/string_util_windows.c ) - s.files += %w( src/core/lib/support/string_windows.c ) - s.files += %w( src/core/lib/support/subprocess_posix.c ) - s.files += %w( src/core/lib/support/subprocess_windows.c ) - s.files += %w( src/core/lib/support/sync.c ) - s.files += %w( src/core/lib/support/sync_posix.c ) - s.files += %w( src/core/lib/support/sync_windows.c ) - s.files += %w( src/core/lib/support/thd.c ) - s.files += %w( src/core/lib/support/thd_posix.c ) - s.files += %w( src/core/lib/support/thd_windows.c ) - s.files += %w( src/core/lib/support/time.c ) - s.files += %w( src/core/lib/support/time_posix.c ) - s.files += %w( src/core/lib/support/time_precise.c ) - s.files += %w( src/core/lib/support/time_windows.c ) - s.files += %w( src/core/lib/support/tls_pthread.c ) - s.files += %w( src/core/lib/support/tmpfile_msys.c ) - s.files += %w( src/core/lib/support/tmpfile_posix.c ) - s.files += %w( src/core/lib/support/tmpfile_windows.c ) - s.files += %w( src/core/lib/support/wrap_memcpy.c ) + s.files += %w( src/core/lib/profiling/basic_timers.cc ) + s.files += %w( src/core/lib/profiling/stap_timers.cc ) + s.files += %w( src/core/lib/support/alloc.cc ) + s.files += %w( src/core/lib/support/arena.cc ) + s.files += %w( src/core/lib/support/atm.cc ) + s.files += %w( src/core/lib/support/avl.cc ) + s.files += %w( src/core/lib/support/backoff.cc ) + s.files += %w( src/core/lib/support/cmdline.cc ) + s.files += %w( src/core/lib/support/cpu_iphone.cc ) + s.files += %w( src/core/lib/support/cpu_linux.cc ) + s.files += %w( src/core/lib/support/cpu_posix.cc ) + s.files += %w( src/core/lib/support/cpu_windows.cc ) + s.files += %w( src/core/lib/support/env_linux.cc ) + s.files += %w( src/core/lib/support/env_posix.cc ) + s.files += %w( src/core/lib/support/env_windows.cc ) + s.files += %w( src/core/lib/support/histogram.cc ) + s.files += %w( src/core/lib/support/host_port.cc ) + s.files += %w( src/core/lib/support/log.cc ) + s.files += %w( src/core/lib/support/log_android.cc ) + s.files += %w( src/core/lib/support/log_linux.cc ) + s.files += %w( src/core/lib/support/log_posix.cc ) + s.files += %w( src/core/lib/support/log_windows.cc ) + s.files += %w( src/core/lib/support/mpscq.cc ) + s.files += %w( src/core/lib/support/murmur_hash.cc ) + s.files += %w( src/core/lib/support/stack_lockfree.cc ) + s.files += %w( src/core/lib/support/string.cc ) + s.files += %w( src/core/lib/support/string_posix.cc ) + s.files += %w( src/core/lib/support/string_util_windows.cc ) + s.files += %w( src/core/lib/support/string_windows.cc ) + s.files += %w( src/core/lib/support/subprocess_posix.cc ) + s.files += %w( src/core/lib/support/subprocess_windows.cc ) + s.files += %w( src/core/lib/support/sync.cc ) + s.files += %w( src/core/lib/support/sync_posix.cc ) + s.files += %w( src/core/lib/support/sync_windows.cc ) + s.files += %w( src/core/lib/support/thd.cc ) + s.files += %w( src/core/lib/support/thd_posix.cc ) + s.files += %w( src/core/lib/support/thd_windows.cc ) + s.files += %w( src/core/lib/support/time.cc ) + s.files += %w( src/core/lib/support/time_posix.cc ) + s.files += %w( src/core/lib/support/time_precise.cc ) + s.files += %w( src/core/lib/support/time_windows.cc ) + s.files += %w( src/core/lib/support/tls_pthread.cc ) + s.files += %w( src/core/lib/support/tmpfile_msys.cc ) + s.files += %w( src/core/lib/support/tmpfile_posix.cc ) + s.files += %w( src/core/lib/support/tmpfile_windows.cc ) + s.files += %w( src/core/lib/support/wrap_memcpy.cc ) s.files += %w( include/grpc/impl/codegen/byte_buffer.h ) s.files += %w( include/grpc/impl/codegen/byte_buffer_reader.h ) s.files += %w( include/grpc/impl/codegen/compression_types.h ) @@ -407,266 +407,264 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/message_size/message_size_filter.h ) s.files += %w( src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h ) s.files += %w( src/core/ext/filters/workarounds/workaround_utils.h ) - s.files += %w( src/core/lib/surface/init.c ) - s.files += %w( src/core/lib/channel/channel_args.c ) - s.files += %w( src/core/lib/channel/channel_stack.c ) - s.files += %w( src/core/lib/channel/channel_stack_builder.c ) - s.files += %w( src/core/lib/channel/connected_channel.c ) - s.files += %w( src/core/lib/channel/handshaker.c ) - s.files += %w( src/core/lib/channel/handshaker_factory.c ) - s.files += %w( src/core/lib/channel/handshaker_registry.c ) - s.files += %w( src/core/lib/compression/compression.c ) - s.files += %w( src/core/lib/compression/message_compress.c ) - s.files += %w( src/core/lib/compression/stream_compression.c ) - s.files += %w( src/core/lib/compression/stream_compression_gzip.c ) - s.files += %w( src/core/lib/compression/stream_compression_identity.c ) - s.files += %w( src/core/lib/debug/stats.c ) - s.files += %w( src/core/lib/debug/stats_data.c ) - s.files += %w( src/core/lib/http/format_request.c ) - s.files += %w( src/core/lib/http/httpcli.c ) - s.files += %w( src/core/lib/http/parser.c ) - s.files += %w( src/core/lib/iomgr/call_combiner.c ) - s.files += %w( src/core/lib/iomgr/closure.c ) - s.files += %w( src/core/lib/iomgr/combiner.c ) - s.files += %w( src/core/lib/iomgr/endpoint.c ) - s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.c ) - s.files += %w( src/core/lib/iomgr/endpoint_pair_uv.c ) - s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.c ) - s.files += %w( src/core/lib/iomgr/error.c ) - s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.c ) - s.files += %w( src/core/lib/iomgr/ev_epollex_linux.c ) - s.files += %w( src/core/lib/iomgr/ev_epollsig_linux.c ) - s.files += %w( src/core/lib/iomgr/ev_poll_posix.c ) - s.files += %w( src/core/lib/iomgr/ev_posix.c ) - s.files += %w( src/core/lib/iomgr/ev_windows.c ) - s.files += %w( src/core/lib/iomgr/exec_ctx.c ) - s.files += %w( src/core/lib/iomgr/executor.c ) - s.files += %w( src/core/lib/iomgr/gethostname_fallback.c ) - s.files += %w( src/core/lib/iomgr/gethostname_host_name_max.c ) - s.files += %w( src/core/lib/iomgr/gethostname_sysconf.c ) - s.files += %w( src/core/lib/iomgr/iocp_windows.c ) - s.files += %w( src/core/lib/iomgr/iomgr.c ) - s.files += %w( src/core/lib/iomgr/iomgr_posix.c ) - s.files += %w( src/core/lib/iomgr/iomgr_uv.c ) - s.files += %w( src/core/lib/iomgr/iomgr_windows.c ) - s.files += %w( src/core/lib/iomgr/is_epollexclusive_available.c ) - s.files += %w( src/core/lib/iomgr/load_file.c ) - s.files += %w( src/core/lib/iomgr/lockfree_event.c ) - s.files += %w( src/core/lib/iomgr/network_status_tracker.c ) - s.files += %w( src/core/lib/iomgr/polling_entity.c ) - s.files += %w( src/core/lib/iomgr/pollset_set_uv.c ) - s.files += %w( src/core/lib/iomgr/pollset_set_windows.c ) - s.files += %w( src/core/lib/iomgr/pollset_uv.c ) - s.files += %w( src/core/lib/iomgr/pollset_windows.c ) - s.files += %w( src/core/lib/iomgr/resolve_address_posix.c ) - s.files += %w( src/core/lib/iomgr/resolve_address_uv.c ) - s.files += %w( src/core/lib/iomgr/resolve_address_windows.c ) - s.files += %w( src/core/lib/iomgr/resource_quota.c ) - s.files += %w( src/core/lib/iomgr/sockaddr_utils.c ) - s.files += %w( src/core/lib/iomgr/socket_factory_posix.c ) - s.files += %w( src/core/lib/iomgr/socket_mutator.c ) - s.files += %w( src/core/lib/iomgr/socket_utils_common_posix.c ) - s.files += %w( src/core/lib/iomgr/socket_utils_linux.c ) - s.files += %w( src/core/lib/iomgr/socket_utils_posix.c ) - s.files += %w( src/core/lib/iomgr/socket_utils_uv.c ) - s.files += %w( src/core/lib/iomgr/socket_utils_windows.c ) - s.files += %w( src/core/lib/iomgr/socket_windows.c ) - s.files += %w( src/core/lib/iomgr/tcp_client_posix.c ) - s.files += %w( src/core/lib/iomgr/tcp_client_uv.c ) - s.files += %w( src/core/lib/iomgr/tcp_client_windows.c ) - s.files += %w( src/core/lib/iomgr/tcp_posix.c ) - s.files += %w( src/core/lib/iomgr/tcp_server_posix.c ) - s.files += %w( src/core/lib/iomgr/tcp_server_utils_posix_common.c ) - s.files += %w( src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c ) - s.files += %w( src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c ) - s.files += %w( src/core/lib/iomgr/tcp_server_uv.c ) - s.files += %w( src/core/lib/iomgr/tcp_server_windows.c ) - s.files += %w( src/core/lib/iomgr/tcp_uv.c ) - s.files += %w( src/core/lib/iomgr/tcp_windows.c ) - s.files += %w( src/core/lib/iomgr/time_averaged_stats.c ) - s.files += %w( src/core/lib/iomgr/timer_generic.c ) - s.files += %w( src/core/lib/iomgr/timer_heap.c ) - s.files += %w( src/core/lib/iomgr/timer_manager.c ) - s.files += %w( src/core/lib/iomgr/timer_uv.c ) - s.files += %w( src/core/lib/iomgr/udp_server.c ) - s.files += %w( src/core/lib/iomgr/unix_sockets_posix.c ) - s.files += %w( src/core/lib/iomgr/unix_sockets_posix_noop.c ) - s.files += %w( src/core/lib/iomgr/wakeup_fd_cv.c ) - s.files += %w( src/core/lib/iomgr/wakeup_fd_eventfd.c ) - s.files += %w( src/core/lib/iomgr/wakeup_fd_nospecial.c ) - s.files += %w( src/core/lib/iomgr/wakeup_fd_pipe.c ) - s.files += %w( src/core/lib/iomgr/wakeup_fd_posix.c ) - s.files += %w( src/core/lib/json/json.c ) - s.files += %w( src/core/lib/json/json_reader.c ) - s.files += %w( src/core/lib/json/json_string.c ) - s.files += %w( src/core/lib/json/json_writer.c ) - s.files += %w( src/core/lib/slice/b64.c ) - s.files += %w( src/core/lib/slice/percent_encoding.c ) - s.files += %w( src/core/lib/slice/slice.c ) - s.files += %w( src/core/lib/slice/slice_buffer.c ) - s.files += %w( src/core/lib/slice/slice_hash_table.c ) - s.files += %w( src/core/lib/slice/slice_intern.c ) - s.files += %w( src/core/lib/slice/slice_string_helpers.c ) - s.files += %w( src/core/lib/surface/alarm.c ) - s.files += %w( src/core/lib/surface/api_trace.c ) - s.files += %w( src/core/lib/surface/byte_buffer.c ) - s.files += %w( src/core/lib/surface/byte_buffer_reader.c ) - s.files += %w( src/core/lib/surface/call.c ) - s.files += %w( src/core/lib/surface/call_details.c ) - s.files += %w( src/core/lib/surface/call_log_batch.c ) - s.files += %w( src/core/lib/surface/channel.c ) - s.files += %w( src/core/lib/surface/channel_init.c ) - s.files += %w( src/core/lib/surface/channel_ping.c ) - s.files += %w( src/core/lib/surface/channel_stack_type.c ) - s.files += %w( src/core/lib/surface/completion_queue.c ) - s.files += %w( src/core/lib/surface/completion_queue_factory.c ) - s.files += %w( src/core/lib/surface/event_string.c ) + s.files += %w( src/core/lib/surface/init.cc ) + s.files += %w( src/core/lib/channel/channel_args.cc ) + s.files += %w( src/core/lib/channel/channel_stack.cc ) + s.files += %w( src/core/lib/channel/channel_stack_builder.cc ) + s.files += %w( src/core/lib/channel/connected_channel.cc ) + s.files += %w( src/core/lib/channel/handshaker.cc ) + s.files += %w( src/core/lib/channel/handshaker_factory.cc ) + s.files += %w( src/core/lib/channel/handshaker_registry.cc ) + s.files += %w( src/core/lib/compression/compression.cc ) + s.files += %w( src/core/lib/compression/message_compress.cc ) + s.files += %w( src/core/lib/compression/stream_compression.cc ) + s.files += %w( src/core/lib/debug/stats.cc ) + s.files += %w( src/core/lib/debug/stats_data.cc ) + s.files += %w( src/core/lib/http/format_request.cc ) + s.files += %w( src/core/lib/http/httpcli.cc ) + s.files += %w( src/core/lib/http/parser.cc ) + s.files += %w( src/core/lib/iomgr/call_combiner.cc ) + s.files += %w( src/core/lib/iomgr/closure.cc ) + s.files += %w( src/core/lib/iomgr/combiner.cc ) + s.files += %w( src/core/lib/iomgr/endpoint.cc ) + s.files += %w( src/core/lib/iomgr/endpoint_pair_posix.cc ) + s.files += %w( src/core/lib/iomgr/endpoint_pair_uv.cc ) + s.files += %w( src/core/lib/iomgr/endpoint_pair_windows.cc ) + s.files += %w( src/core/lib/iomgr/error.cc ) + s.files += %w( src/core/lib/iomgr/ev_epoll1_linux.cc ) + s.files += %w( src/core/lib/iomgr/ev_epollex_linux.cc ) + s.files += %w( src/core/lib/iomgr/ev_epollsig_linux.cc ) + s.files += %w( src/core/lib/iomgr/ev_poll_posix.cc ) + s.files += %w( src/core/lib/iomgr/ev_posix.cc ) + s.files += %w( src/core/lib/iomgr/ev_windows.cc ) + s.files += %w( src/core/lib/iomgr/exec_ctx.cc ) + s.files += %w( src/core/lib/iomgr/executor.cc ) + s.files += %w( src/core/lib/iomgr/gethostname_fallback.cc ) + s.files += %w( src/core/lib/iomgr/gethostname_host_name_max.cc ) + s.files += %w( src/core/lib/iomgr/gethostname_sysconf.cc ) + s.files += %w( src/core/lib/iomgr/iocp_windows.cc ) + s.files += %w( src/core/lib/iomgr/iomgr.cc ) + s.files += %w( src/core/lib/iomgr/iomgr_posix.cc ) + s.files += %w( src/core/lib/iomgr/iomgr_uv.cc ) + s.files += %w( src/core/lib/iomgr/iomgr_windows.cc ) + s.files += %w( src/core/lib/iomgr/is_epollexclusive_available.cc ) + s.files += %w( src/core/lib/iomgr/load_file.cc ) + s.files += %w( src/core/lib/iomgr/lockfree_event.cc ) + s.files += %w( src/core/lib/iomgr/network_status_tracker.cc ) + s.files += %w( src/core/lib/iomgr/polling_entity.cc ) + s.files += %w( src/core/lib/iomgr/pollset_set_uv.cc ) + s.files += %w( src/core/lib/iomgr/pollset_set_windows.cc ) + s.files += %w( src/core/lib/iomgr/pollset_uv.cc ) + s.files += %w( src/core/lib/iomgr/pollset_windows.cc ) + s.files += %w( src/core/lib/iomgr/resolve_address_posix.cc ) + s.files += %w( src/core/lib/iomgr/resolve_address_uv.cc ) + s.files += %w( src/core/lib/iomgr/resolve_address_windows.cc ) + s.files += %w( src/core/lib/iomgr/resource_quota.cc ) + s.files += %w( src/core/lib/iomgr/sockaddr_utils.cc ) + s.files += %w( src/core/lib/iomgr/socket_factory_posix.cc ) + s.files += %w( src/core/lib/iomgr/socket_mutator.cc ) + s.files += %w( src/core/lib/iomgr/socket_utils_common_posix.cc ) + s.files += %w( src/core/lib/iomgr/socket_utils_linux.cc ) + s.files += %w( src/core/lib/iomgr/socket_utils_posix.cc ) + s.files += %w( src/core/lib/iomgr/socket_utils_uv.cc ) + s.files += %w( src/core/lib/iomgr/socket_utils_windows.cc ) + s.files += %w( src/core/lib/iomgr/socket_windows.cc ) + s.files += %w( src/core/lib/iomgr/tcp_client_posix.cc ) + s.files += %w( src/core/lib/iomgr/tcp_client_uv.cc ) + s.files += %w( src/core/lib/iomgr/tcp_client_windows.cc ) + s.files += %w( src/core/lib/iomgr/tcp_posix.cc ) + s.files += %w( src/core/lib/iomgr/tcp_server_posix.cc ) + s.files += %w( src/core/lib/iomgr/tcp_server_utils_posix_common.cc ) + s.files += %w( src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc ) + s.files += %w( src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc ) + s.files += %w( src/core/lib/iomgr/tcp_server_uv.cc ) + s.files += %w( src/core/lib/iomgr/tcp_server_windows.cc ) + s.files += %w( src/core/lib/iomgr/tcp_uv.cc ) + s.files += %w( src/core/lib/iomgr/tcp_windows.cc ) + s.files += %w( src/core/lib/iomgr/time_averaged_stats.cc ) + s.files += %w( src/core/lib/iomgr/timer_generic.cc ) + s.files += %w( src/core/lib/iomgr/timer_heap.cc ) + s.files += %w( src/core/lib/iomgr/timer_manager.cc ) + s.files += %w( src/core/lib/iomgr/timer_uv.cc ) + s.files += %w( src/core/lib/iomgr/udp_server.cc ) + s.files += %w( src/core/lib/iomgr/unix_sockets_posix.cc ) + s.files += %w( src/core/lib/iomgr/unix_sockets_posix_noop.cc ) + s.files += %w( src/core/lib/iomgr/wakeup_fd_cv.cc ) + s.files += %w( src/core/lib/iomgr/wakeup_fd_eventfd.cc ) + s.files += %w( src/core/lib/iomgr/wakeup_fd_nospecial.cc ) + s.files += %w( src/core/lib/iomgr/wakeup_fd_pipe.cc ) + s.files += %w( src/core/lib/iomgr/wakeup_fd_posix.cc ) + s.files += %w( src/core/lib/json/json.cc ) + s.files += %w( src/core/lib/json/json_reader.cc ) + s.files += %w( src/core/lib/json/json_string.cc ) + s.files += %w( src/core/lib/json/json_writer.cc ) + s.files += %w( src/core/lib/slice/b64.cc ) + s.files += %w( src/core/lib/slice/percent_encoding.cc ) + s.files += %w( src/core/lib/slice/slice.cc ) + s.files += %w( src/core/lib/slice/slice_buffer.cc ) + s.files += %w( src/core/lib/slice/slice_hash_table.cc ) + s.files += %w( src/core/lib/slice/slice_intern.cc ) + s.files += %w( src/core/lib/slice/slice_string_helpers.cc ) + s.files += %w( src/core/lib/surface/alarm.cc ) + s.files += %w( src/core/lib/surface/api_trace.cc ) + s.files += %w( src/core/lib/surface/byte_buffer.cc ) + s.files += %w( src/core/lib/surface/byte_buffer_reader.cc ) + s.files += %w( src/core/lib/surface/call.cc ) + s.files += %w( src/core/lib/surface/call_details.cc ) + s.files += %w( src/core/lib/surface/call_log_batch.cc ) + s.files += %w( src/core/lib/surface/channel.cc ) + s.files += %w( src/core/lib/surface/channel_init.cc ) + s.files += %w( src/core/lib/surface/channel_ping.cc ) + s.files += %w( src/core/lib/surface/channel_stack_type.cc ) + s.files += %w( src/core/lib/surface/completion_queue.cc ) + s.files += %w( src/core/lib/surface/completion_queue_factory.cc ) + s.files += %w( src/core/lib/surface/event_string.cc ) s.files += %w( src/core/lib/surface/lame_client.cc ) - s.files += %w( src/core/lib/surface/metadata_array.c ) - s.files += %w( src/core/lib/surface/server.c ) - s.files += %w( src/core/lib/surface/validate_metadata.c ) - s.files += %w( src/core/lib/surface/version.c ) - s.files += %w( src/core/lib/transport/bdp_estimator.c ) - s.files += %w( src/core/lib/transport/byte_stream.c ) - s.files += %w( src/core/lib/transport/connectivity_state.c ) - s.files += %w( src/core/lib/transport/error_utils.c ) - s.files += %w( src/core/lib/transport/metadata.c ) - s.files += %w( src/core/lib/transport/metadata_batch.c ) - s.files += %w( src/core/lib/transport/pid_controller.c ) - s.files += %w( src/core/lib/transport/service_config.c ) - s.files += %w( src/core/lib/transport/static_metadata.c ) - s.files += %w( src/core/lib/transport/status_conversion.c ) - s.files += %w( src/core/lib/transport/timeout_encoding.c ) - s.files += %w( src/core/lib/transport/transport.c ) - s.files += %w( src/core/lib/transport/transport_op_string.c ) - s.files += %w( src/core/lib/debug/trace.c ) - s.files += %w( src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/bin_decoder.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/bin_encoder.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/chttp2_plugin.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/chttp2_transport.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/flow_control.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/frame_data.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/frame_goaway.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/frame_ping.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/frame_rst_stream.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/frame_settings.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/frame_window_update.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/hpack_encoder.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parser.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/hpack_table.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/http2_settings.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/huffsyms.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/incoming_metadata.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/parsing.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/stream_lists.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/stream_map.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/varint.c ) - s.files += %w( src/core/ext/transport/chttp2/transport/writing.c ) - s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.c ) - s.files += %w( src/core/ext/filters/http/client/http_client_filter.c ) - s.files += %w( src/core/ext/filters/http/http_filters_plugin.c ) - s.files += %w( src/core/ext/filters/http/message_compress/message_compress_filter.c ) - s.files += %w( src/core/ext/filters/http/server/http_server_filter.c ) - s.files += %w( src/core/lib/http/httpcli_security_connector.c ) - s.files += %w( src/core/lib/security/context/security_context.c ) - s.files += %w( src/core/lib/security/credentials/composite/composite_credentials.c ) - s.files += %w( src/core/lib/security/credentials/credentials.c ) - s.files += %w( src/core/lib/security/credentials/credentials_metadata.c ) - s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.c ) - s.files += %w( src/core/lib/security/credentials/google_default/credentials_generic.c ) - s.files += %w( src/core/lib/security/credentials/google_default/google_default_credentials.c ) - s.files += %w( src/core/lib/security/credentials/iam/iam_credentials.c ) - s.files += %w( src/core/lib/security/credentials/jwt/json_token.c ) - s.files += %w( src/core/lib/security/credentials/jwt/jwt_credentials.c ) - s.files += %w( src/core/lib/security/credentials/jwt/jwt_verifier.c ) - s.files += %w( src/core/lib/security/credentials/oauth2/oauth2_credentials.c ) - s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.c ) - s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.c ) - s.files += %w( src/core/lib/security/transport/client_auth_filter.c ) - s.files += %w( src/core/lib/security/transport/lb_targets_info.c ) - s.files += %w( src/core/lib/security/transport/secure_endpoint.c ) - s.files += %w( src/core/lib/security/transport/security_connector.c ) - s.files += %w( src/core/lib/security/transport/security_handshaker.c ) - s.files += %w( src/core/lib/security/transport/server_auth_filter.c ) - s.files += %w( src/core/lib/security/transport/tsi_error.c ) - s.files += %w( src/core/lib/security/util/json_util.c ) - s.files += %w( src/core/lib/surface/init_secure.c ) - s.files += %w( src/core/tsi/fake_transport_security.c ) - s.files += %w( src/core/tsi/gts_transport_security.c ) - s.files += %w( src/core/tsi/ssl_transport_security.c ) - s.files += %w( src/core/tsi/transport_security_grpc.c ) - s.files += %w( src/core/tsi/transport_security.c ) - s.files += %w( src/core/tsi/transport_security_adapter.c ) - s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.c ) - s.files += %w( src/core/ext/transport/chttp2/client/secure/secure_channel_create.c ) - s.files += %w( src/core/ext/filters/client_channel/channel_connectivity.c ) - s.files += %w( src/core/ext/filters/client_channel/client_channel.c ) - s.files += %w( src/core/ext/filters/client_channel/client_channel_factory.c ) - s.files += %w( src/core/ext/filters/client_channel/client_channel_plugin.c ) - s.files += %w( src/core/ext/filters/client_channel/connector.c ) - s.files += %w( src/core/ext/filters/client_channel/http_connect_handshaker.c ) - s.files += %w( src/core/ext/filters/client_channel/http_proxy.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy_factory.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.c ) - s.files += %w( src/core/ext/filters/client_channel/parse_address.c ) - s.files += %w( src/core/ext/filters/client_channel/proxy_mapper.c ) - s.files += %w( src/core/ext/filters/client_channel/proxy_mapper_registry.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver_factory.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver_registry.c ) - s.files += %w( src/core/ext/filters/client_channel/retry_throttle.c ) - s.files += %w( src/core/ext/filters/client_channel/subchannel.c ) - s.files += %w( src/core/ext/filters/client_channel/subchannel_index.c ) - s.files += %w( src/core/ext/filters/client_channel/uri_parser.c ) - s.files += %w( src/core/ext/filters/deadline/deadline_filter.c ) - s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.c ) - s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2.c ) - s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c ) - s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.c ) - s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c ) - s.files += %w( src/core/ext/transport/inproc/inproc_plugin.c ) - s.files += %w( src/core/ext/transport/inproc/inproc_transport.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c ) + s.files += %w( src/core/lib/surface/metadata_array.cc ) + s.files += %w( src/core/lib/surface/server.cc ) + s.files += %w( src/core/lib/surface/validate_metadata.cc ) + s.files += %w( src/core/lib/surface/version.cc ) + s.files += %w( src/core/lib/transport/bdp_estimator.cc ) + s.files += %w( src/core/lib/transport/byte_stream.cc ) + s.files += %w( src/core/lib/transport/connectivity_state.cc ) + s.files += %w( src/core/lib/transport/error_utils.cc ) + s.files += %w( src/core/lib/transport/metadata.cc ) + s.files += %w( src/core/lib/transport/metadata_batch.cc ) + s.files += %w( src/core/lib/transport/pid_controller.cc ) + s.files += %w( src/core/lib/transport/service_config.cc ) + s.files += %w( src/core/lib/transport/static_metadata.cc ) + s.files += %w( src/core/lib/transport/status_conversion.cc ) + s.files += %w( src/core/lib/transport/timeout_encoding.cc ) + s.files += %w( src/core/lib/transport/transport.cc ) + s.files += %w( src/core/lib/transport/transport_op_string.cc ) + s.files += %w( src/core/lib/debug/trace.cc ) + s.files += %w( src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/bin_decoder.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/bin_encoder.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/chttp2_plugin.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/chttp2_transport.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/flow_control.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_data.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_goaway.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_ping.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_rst_stream.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_settings.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/frame_window_update.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/hpack_encoder.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/hpack_parser.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/hpack_table.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/http2_settings.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/huffsyms.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/incoming_metadata.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/parsing.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/stream_lists.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/stream_map.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/varint.cc ) + s.files += %w( src/core/ext/transport/chttp2/transport/writing.cc ) + s.files += %w( src/core/ext/transport/chttp2/alpn/alpn.cc ) + s.files += %w( src/core/ext/filters/http/client/http_client_filter.cc ) + s.files += %w( src/core/ext/filters/http/http_filters_plugin.cc ) + s.files += %w( src/core/ext/filters/http/message_compress/message_compress_filter.cc ) + s.files += %w( src/core/ext/filters/http/server/http_server_filter.cc ) + s.files += %w( src/core/lib/http/httpcli_security_connector.cc ) + s.files += %w( src/core/lib/security/context/security_context.cc ) + s.files += %w( src/core/lib/security/credentials/composite/composite_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/credentials.cc ) + s.files += %w( src/core/lib/security/credentials/credentials_metadata.cc ) + s.files += %w( src/core/lib/security/credentials/fake/fake_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/google_default/credentials_generic.cc ) + s.files += %w( src/core/lib/security/credentials/google_default/google_default_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/iam/iam_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/jwt/json_token.cc ) + s.files += %w( src/core/lib/security/credentials/jwt/jwt_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/jwt/jwt_verifier.cc ) + s.files += %w( src/core/lib/security/credentials/oauth2/oauth2_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/plugin/plugin_credentials.cc ) + s.files += %w( src/core/lib/security/credentials/ssl/ssl_credentials.cc ) + s.files += %w( src/core/lib/security/transport/client_auth_filter.cc ) + s.files += %w( src/core/lib/security/transport/lb_targets_info.cc ) + s.files += %w( src/core/lib/security/transport/secure_endpoint.cc ) + s.files += %w( src/core/lib/security/transport/security_connector.cc ) + s.files += %w( src/core/lib/security/transport/security_handshaker.cc ) + s.files += %w( src/core/lib/security/transport/server_auth_filter.cc ) + s.files += %w( src/core/lib/security/transport/tsi_error.cc ) + s.files += %w( src/core/lib/security/util/json_util.cc ) + s.files += %w( src/core/lib/surface/init_secure.cc ) + s.files += %w( src/core/tsi/fake_transport_security.cc ) + s.files += %w( src/core/tsi/gts_transport_security.cc ) + s.files += %w( src/core/tsi/ssl_transport_security.cc ) + s.files += %w( src/core/tsi/transport_security_grpc.cc ) + s.files += %w( src/core/tsi/transport_security.cc ) + s.files += %w( src/core/tsi/transport_security_adapter.cc ) + s.files += %w( src/core/ext/transport/chttp2/server/chttp2_server.cc ) + s.files += %w( src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc ) + s.files += %w( src/core/ext/filters/client_channel/channel_connectivity.cc ) + s.files += %w( src/core/ext/filters/client_channel/client_channel.cc ) + s.files += %w( src/core/ext/filters/client_channel/client_channel_factory.cc ) + s.files += %w( src/core/ext/filters/client_channel/client_channel_plugin.cc ) + s.files += %w( src/core/ext/filters/client_channel/connector.cc ) + s.files += %w( src/core/ext/filters/client_channel/http_connect_handshaker.cc ) + s.files += %w( src/core/ext/filters/client_channel/http_proxy.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy_factory.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy_registry.cc ) + s.files += %w( src/core/ext/filters/client_channel/parse_address.cc ) + s.files += %w( src/core/ext/filters/client_channel/proxy_mapper.cc ) + s.files += %w( src/core/ext/filters/client_channel/proxy_mapper_registry.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver_factory.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver_registry.cc ) + s.files += %w( src/core/ext/filters/client_channel/retry_throttle.cc ) + s.files += %w( src/core/ext/filters/client_channel/subchannel.cc ) + s.files += %w( src/core/ext/filters/client_channel/subchannel_index.cc ) + s.files += %w( src/core/ext/filters/client_channel/uri_parser.cc ) + s.files += %w( src/core/ext/filters/deadline/deadline_filter.cc ) + s.files += %w( src/core/ext/transport/chttp2/client/chttp2_connector.cc ) + s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc ) + s.files += %w( src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc ) + s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create.cc ) + s.files += %w( src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc ) + s.files += %w( src/core/ext/transport/inproc/inproc_plugin.cc ) + s.files += %w( src/core/ext/transport/inproc/inproc_transport.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc ) s.files += %w( third_party/nanopb/pb_common.c ) s.files += %w( third_party/nanopb/pb_decode.c ) s.files += %w( third_party/nanopb/pb_encode.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c ) - s.files += %w( src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c ) - s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_filter.c ) - s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_plugin.c ) - s.files += %w( src/core/ext/census/base_resources.c ) - s.files += %w( src/core/ext/census/context.c ) - s.files += %w( src/core/ext/census/gen/census.pb.c ) - s.files += %w( src/core/ext/census/gen/trace_context.pb.c ) - s.files += %w( src/core/ext/census/grpc_context.c ) - 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 ) - s.files += %w( src/core/ext/census/resource.c ) - s.files += %w( src/core/ext/census/trace_context.c ) - s.files += %w( src/core/ext/census/tracing.c ) - s.files += %w( src/core/ext/filters/max_age/max_age_filter.c ) - s.files += %w( src/core/ext/filters/message_size/message_size_filter.c ) - s.files += %w( src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c ) - s.files += %w( src/core/ext/filters/workarounds/workaround_utils.c ) + s.files += %w( src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc ) + s.files += %w( src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc ) + s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_filter.cc ) + s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc ) + s.files += %w( src/core/ext/census/base_resources.cc ) + s.files += %w( src/core/ext/census/context.cc ) + s.files += %w( src/core/ext/census/gen/census.pb.cc ) + s.files += %w( src/core/ext/census/gen/trace_context.pb.cc ) + s.files += %w( src/core/ext/census/grpc_context.cc ) + s.files += %w( src/core/ext/census/grpc_filter.cc ) + s.files += %w( src/core/ext/census/grpc_plugin.cc ) + s.files += %w( src/core/ext/census/initialize.cc ) + s.files += %w( src/core/ext/census/intrusive_hash_map.cc ) + s.files += %w( src/core/ext/census/mlog.cc ) + s.files += %w( src/core/ext/census/operation.cc ) + s.files += %w( src/core/ext/census/placeholders.cc ) + s.files += %w( src/core/ext/census/resource.cc ) + s.files += %w( src/core/ext/census/trace_context.cc ) + s.files += %w( src/core/ext/census/tracing.cc ) + s.files += %w( src/core/ext/filters/max_age/max_age_filter.cc ) + s.files += %w( src/core/ext/filters/message_size/message_size_filter.cc ) + s.files += %w( src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc ) + s.files += %w( src/core/ext/filters/workarounds/workaround_utils.cc ) s.files += %w( src/core/plugin_registry/grpc_plugin_registry.cc ) s.files += %w( third_party/boringssl/crypto/aes/internal.h ) s.files += %w( third_party/boringssl/crypto/asn1/asn1_locl.h ) diff --git a/grpc.gyp b/grpc.gyp index 9055dfe901..468fcde9a1 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -157,52 +157,52 @@ 'dependencies': [ ], 'sources': [ - 'src/core/lib/profiling/basic_timers.c', - 'src/core/lib/profiling/stap_timers.c', - 'src/core/lib/support/alloc.c', - 'src/core/lib/support/arena.c', - 'src/core/lib/support/atm.c', - 'src/core/lib/support/avl.c', - 'src/core/lib/support/backoff.c', - 'src/core/lib/support/cmdline.c', - 'src/core/lib/support/cpu_iphone.c', - 'src/core/lib/support/cpu_linux.c', - 'src/core/lib/support/cpu_posix.c', - 'src/core/lib/support/cpu_windows.c', - 'src/core/lib/support/env_linux.c', - 'src/core/lib/support/env_posix.c', - 'src/core/lib/support/env_windows.c', - 'src/core/lib/support/histogram.c', - 'src/core/lib/support/host_port.c', - 'src/core/lib/support/log.c', - 'src/core/lib/support/log_android.c', - 'src/core/lib/support/log_linux.c', - 'src/core/lib/support/log_posix.c', - 'src/core/lib/support/log_windows.c', - 'src/core/lib/support/mpscq.c', - 'src/core/lib/support/murmur_hash.c', - 'src/core/lib/support/stack_lockfree.c', - 'src/core/lib/support/string.c', - 'src/core/lib/support/string_posix.c', - 'src/core/lib/support/string_util_windows.c', - 'src/core/lib/support/string_windows.c', - 'src/core/lib/support/subprocess_posix.c', - 'src/core/lib/support/subprocess_windows.c', - 'src/core/lib/support/sync.c', - 'src/core/lib/support/sync_posix.c', - 'src/core/lib/support/sync_windows.c', - 'src/core/lib/support/thd.c', - 'src/core/lib/support/thd_posix.c', - 'src/core/lib/support/thd_windows.c', - 'src/core/lib/support/time.c', - 'src/core/lib/support/time_posix.c', - 'src/core/lib/support/time_precise.c', - 'src/core/lib/support/time_windows.c', - 'src/core/lib/support/tls_pthread.c', - 'src/core/lib/support/tmpfile_msys.c', - 'src/core/lib/support/tmpfile_posix.c', - 'src/core/lib/support/tmpfile_windows.c', - 'src/core/lib/support/wrap_memcpy.c', + 'src/core/lib/profiling/basic_timers.cc', + 'src/core/lib/profiling/stap_timers.cc', + 'src/core/lib/support/alloc.cc', + 'src/core/lib/support/arena.cc', + 'src/core/lib/support/atm.cc', + 'src/core/lib/support/avl.cc', + 'src/core/lib/support/backoff.cc', + 'src/core/lib/support/cmdline.cc', + 'src/core/lib/support/cpu_iphone.cc', + 'src/core/lib/support/cpu_linux.cc', + 'src/core/lib/support/cpu_posix.cc', + 'src/core/lib/support/cpu_windows.cc', + 'src/core/lib/support/env_linux.cc', + 'src/core/lib/support/env_posix.cc', + 'src/core/lib/support/env_windows.cc', + 'src/core/lib/support/histogram.cc', + 'src/core/lib/support/host_port.cc', + 'src/core/lib/support/log.cc', + 'src/core/lib/support/log_android.cc', + 'src/core/lib/support/log_linux.cc', + 'src/core/lib/support/log_posix.cc', + 'src/core/lib/support/log_windows.cc', + 'src/core/lib/support/mpscq.cc', + 'src/core/lib/support/murmur_hash.cc', + 'src/core/lib/support/stack_lockfree.cc', + 'src/core/lib/support/string.cc', + 'src/core/lib/support/string_posix.cc', + 'src/core/lib/support/string_util_windows.cc', + 'src/core/lib/support/string_windows.cc', + 'src/core/lib/support/subprocess_posix.cc', + 'src/core/lib/support/subprocess_windows.cc', + 'src/core/lib/support/sync.cc', + 'src/core/lib/support/sync_posix.cc', + 'src/core/lib/support/sync_windows.cc', + 'src/core/lib/support/thd.cc', + 'src/core/lib/support/thd_posix.cc', + 'src/core/lib/support/thd_windows.cc', + 'src/core/lib/support/time.cc', + 'src/core/lib/support/time_posix.cc', + 'src/core/lib/support/time_precise.cc', + 'src/core/lib/support/time_windows.cc', + 'src/core/lib/support/tls_pthread.cc', + 'src/core/lib/support/tmpfile_msys.cc', + 'src/core/lib/support/tmpfile_posix.cc', + 'src/core/lib/support/tmpfile_windows.cc', + 'src/core/lib/support/wrap_memcpy.cc', ], }, { @@ -222,266 +222,264 @@ 'gpr', ], 'sources': [ - 'src/core/lib/surface/init.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/lib/surface/init.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', - 'src/core/lib/http/httpcli_security_connector.c', - 'src/core/lib/security/context/security_context.c', - 'src/core/lib/security/credentials/composite/composite_credentials.c', - 'src/core/lib/security/credentials/credentials.c', - 'src/core/lib/security/credentials/credentials_metadata.c', - 'src/core/lib/security/credentials/fake/fake_credentials.c', - 'src/core/lib/security/credentials/google_default/credentials_generic.c', - 'src/core/lib/security/credentials/google_default/google_default_credentials.c', - 'src/core/lib/security/credentials/iam/iam_credentials.c', - 'src/core/lib/security/credentials/jwt/json_token.c', - 'src/core/lib/security/credentials/jwt/jwt_credentials.c', - 'src/core/lib/security/credentials/jwt/jwt_verifier.c', - 'src/core/lib/security/credentials/oauth2/oauth2_credentials.c', - 'src/core/lib/security/credentials/plugin/plugin_credentials.c', - 'src/core/lib/security/credentials/ssl/ssl_credentials.c', - 'src/core/lib/security/transport/client_auth_filter.c', - 'src/core/lib/security/transport/lb_targets_info.c', - 'src/core/lib/security/transport/secure_endpoint.c', - 'src/core/lib/security/transport/security_connector.c', - 'src/core/lib/security/transport/security_handshaker.c', - 'src/core/lib/security/transport/server_auth_filter.c', - 'src/core/lib/security/transport/tsi_error.c', - 'src/core/lib/security/util/json_util.c', - 'src/core/lib/surface/init_secure.c', - 'src/core/tsi/fake_transport_security.c', - 'src/core/tsi/gts_transport_security.c', - 'src/core/tsi/ssl_transport_security.c', - 'src/core/tsi/transport_security_grpc.c', - 'src/core/tsi/transport_security.c', - 'src/core/tsi/transport_security_adapter.c', - 'src/core/ext/transport/chttp2/server/chttp2_server.c', - 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/chttp2/client/chttp2_connector.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c', - 'src/core/ext/transport/inproc/inproc_plugin.c', - 'src/core/ext/transport/inproc/inproc_transport.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', + 'src/core/lib/http/httpcli_security_connector.cc', + 'src/core/lib/security/context/security_context.cc', + 'src/core/lib/security/credentials/composite/composite_credentials.cc', + 'src/core/lib/security/credentials/credentials.cc', + 'src/core/lib/security/credentials/credentials_metadata.cc', + 'src/core/lib/security/credentials/fake/fake_credentials.cc', + 'src/core/lib/security/credentials/google_default/credentials_generic.cc', + 'src/core/lib/security/credentials/google_default/google_default_credentials.cc', + 'src/core/lib/security/credentials/iam/iam_credentials.cc', + 'src/core/lib/security/credentials/jwt/json_token.cc', + 'src/core/lib/security/credentials/jwt/jwt_credentials.cc', + 'src/core/lib/security/credentials/jwt/jwt_verifier.cc', + 'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc', + 'src/core/lib/security/credentials/plugin/plugin_credentials.cc', + 'src/core/lib/security/credentials/ssl/ssl_credentials.cc', + 'src/core/lib/security/transport/client_auth_filter.cc', + 'src/core/lib/security/transport/lb_targets_info.cc', + 'src/core/lib/security/transport/secure_endpoint.cc', + 'src/core/lib/security/transport/security_connector.cc', + 'src/core/lib/security/transport/security_handshaker.cc', + 'src/core/lib/security/transport/server_auth_filter.cc', + 'src/core/lib/security/transport/tsi_error.cc', + 'src/core/lib/security/util/json_util.cc', + 'src/core/lib/surface/init_secure.cc', + 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/gts_transport_security.cc', + 'src/core/tsi/ssl_transport_security.cc', + 'src/core/tsi/transport_security_grpc.cc', + 'src/core/tsi/transport_security.cc', + 'src/core/tsi/transport_security_adapter.cc', + 'src/core/ext/transport/chttp2/server/chttp2_server.cc', + 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', + 'src/core/ext/transport/inproc/inproc_plugin.cc', + 'src/core/ext/transport/inproc/inproc_transport.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c', - 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c', - 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c', - 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_filter.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.c', - 'src/core/ext/census/base_resources.c', - 'src/core/ext/census/context.c', - 'src/core/ext/census/gen/census.pb.c', - 'src/core/ext/census/gen/trace_context.pb.c', - 'src/core/ext/census/grpc_context.c', - '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', - 'src/core/ext/census/resource.c', - 'src/core/ext/census/trace_context.c', - 'src/core/ext/census/tracing.c', - 'src/core/ext/filters/max_age/max_age_filter.c', - 'src/core/ext/filters/message_size/message_size_filter.c', - 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', - 'src/core/ext/filters/workarounds/workaround_utils.c', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', + 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', + 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', + 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', + 'src/core/ext/census/base_resources.cc', + 'src/core/ext/census/context.cc', + 'src/core/ext/census/gen/census.pb.cc', + 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/grpc_context.cc', + 'src/core/ext/census/grpc_filter.cc', + 'src/core/ext/census/grpc_plugin.cc', + 'src/core/ext/census/initialize.cc', + 'src/core/ext/census/intrusive_hash_map.cc', + 'src/core/ext/census/mlog.cc', + 'src/core/ext/census/operation.cc', + 'src/core/ext/census/placeholders.cc', + 'src/core/ext/census/resource.cc', + 'src/core/ext/census/trace_context.cc', + 'src/core/ext/census/tracing.cc', + 'src/core/ext/filters/max_age/max_age_filter.cc', + 'src/core/ext/filters/message_size/message_size_filter.cc', + 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc', + 'src/core/ext/filters/workarounds/workaround_utils.cc', 'src/core/plugin_registry/grpc_plugin_registry.cc', ], }, @@ -509,201 +507,199 @@ 'test/core/end2end/data/server1_key.c', 'test/core/end2end/data/test_root_cert.c', 'test/core/security/oauth2_utils.c', - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'test/core/end2end/cq_verifier.c', - 'test/core/end2end/fixtures/http_proxy_fixture.c', - 'test/core/end2end/fixtures/proxy.c', - 'test/core/iomgr/endpoint_tests.c', - 'test/core/util/debugger_macros.c', - 'test/core/util/grpc_profiler.c', - 'test/core/util/memory_counters.c', - 'test/core/util/mock_endpoint.c', - 'test/core/util/parse_hexstring.c', - 'test/core/util/passthru_endpoint.c', - 'test/core/util/port.c', - 'test/core/util/port_server_client.c', - 'test/core/util/slice_splitter.c', - 'test/core/util/trickle_endpoint.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'test/core/end2end/cq_verifier.cc', + 'test/core/end2end/fixtures/http_proxy_fixture.cc', + 'test/core/end2end/fixtures/proxy.cc', + 'test/core/iomgr/endpoint_tests.cc', + 'test/core/util/debugger_macros.cc', + 'test/core/util/grpc_profiler.cc', + 'test/core/util/memory_counters.cc', + 'test/core/util/mock_endpoint.cc', + 'test/core/util/parse_hexstring.cc', + 'test/core/util/passthru_endpoint.cc', + 'test/core/util/port.cc', + 'test/core/util/port_server_client.cc', + 'test/core/util/slice_splitter.cc', + 'test/core/util/trickle_endpoint.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', ], }, { @@ -715,201 +711,199 @@ 'grpc_unsecure', ], 'sources': [ - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'test/core/end2end/cq_verifier.c', - 'test/core/end2end/fixtures/http_proxy_fixture.c', - 'test/core/end2end/fixtures/proxy.c', - 'test/core/iomgr/endpoint_tests.c', - 'test/core/util/debugger_macros.c', - 'test/core/util/grpc_profiler.c', - 'test/core/util/memory_counters.c', - 'test/core/util/mock_endpoint.c', - 'test/core/util/parse_hexstring.c', - 'test/core/util/passthru_endpoint.c', - 'test/core/util/port.c', - 'test/core/util/port_server_client.c', - 'test/core/util/slice_splitter.c', - 'test/core/util/trickle_endpoint.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'test/core/end2end/cq_verifier.cc', + 'test/core/end2end/fixtures/http_proxy_fixture.cc', + 'test/core/end2end/fixtures/proxy.cc', + 'test/core/iomgr/endpoint_tests.cc', + 'test/core/util/debugger_macros.cc', + 'test/core/util/grpc_profiler.cc', + 'test/core/util/memory_counters.cc', + 'test/core/util/mock_endpoint.cc', + 'test/core/util/parse_hexstring.cc', + 'test/core/util/passthru_endpoint.cc', + 'test/core/util/port.cc', + 'test/core/util/port_server_client.cc', + 'test/core/util/slice_splitter.cc', + 'test/core/util/trickle_endpoint.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', ], }, { @@ -919,235 +913,233 @@ 'gpr', ], 'sources': [ - 'src/core/lib/surface/init.c', - 'src/core/lib/surface/init_unsecure.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/lib/surface/init.cc', + 'src/core/lib/surface/init_unsecure.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', - 'src/core/ext/transport/chttp2/server/chttp2_server.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c', - 'src/core/ext/transport/chttp2/client/chttp2_connector.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/inproc/inproc_plugin.c', - 'src/core/ext/transport/inproc/inproc_transport.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c', - 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c', - 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c', - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_filter.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', + 'src/core/ext/transport/chttp2/server/chttp2_server.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', + 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/inproc/inproc_plugin.cc', + 'src/core/ext/transport/inproc/inproc_transport.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', + 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', + 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', - 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c', - 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c', - 'src/core/ext/census/base_resources.c', - 'src/core/ext/census/context.c', - 'src/core/ext/census/gen/census.pb.c', - 'src/core/ext/census/gen/trace_context.pb.c', - 'src/core/ext/census/grpc_context.c', - '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', - 'src/core/ext/census/resource.c', - 'src/core/ext/census/trace_context.c', - 'src/core/ext/census/tracing.c', - 'src/core/ext/filters/max_age/max_age_filter.c', - 'src/core/ext/filters/message_size/message_size_filter.c', - 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', - 'src/core/ext/filters/workarounds/workaround_utils.c', + 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', + 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', + 'src/core/ext/census/base_resources.cc', + 'src/core/ext/census/context.cc', + 'src/core/ext/census/gen/census.pb.cc', + 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/grpc_context.cc', + 'src/core/ext/census/grpc_filter.cc', + 'src/core/ext/census/grpc_plugin.cc', + 'src/core/ext/census/initialize.cc', + 'src/core/ext/census/intrusive_hash_map.cc', + 'src/core/ext/census/mlog.cc', + 'src/core/ext/census/operation.cc', + 'src/core/ext/census/placeholders.cc', + 'src/core/ext/census/resource.cc', + 'src/core/ext/census/trace_context.cc', + 'src/core/ext/census/tracing.cc', + 'src/core/ext/filters/max_age/max_age_filter.cc', + 'src/core/ext/filters/message_size/message_size_filter.cc', + 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc', + 'src/core/ext/filters/workarounds/workaround_utils.cc', 'src/core/plugin_registry/grpc_unsecure_plugin_registry.cc', ], }, diff --git a/package.xml b/package.xml index 39d238e378..bbe2a10edd 100644 --- a/package.xml +++ b/package.xml @@ -112,52 +112,52 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -419,266 +419,264 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/core/ext/census/base_resources.c b/src/core/ext/census/base_resources.c deleted file mode 100644 index 1f2bb39fe0..0000000000 --- a/src/core/ext/census/base_resources.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/base_resources.h" - -#include -#include - -#include -#include - -#include "src/core/ext/census/resource.h" - -// Add base RPC resource definitions for use by RPC runtime. -// -// TODO(aveitch): All of these are currently hardwired definitions encoded in -// the code in this file. These should be converted to use an external -// configuration mechanism, in which these resources are defined in a text -// file, which is compiled to .pb format and read by still-to-be-written -// configuration functions. - -// Define all base resources. This should be called by census initialization. -void define_base_resources() { - google_census_Resource_BasicUnit numerator = - google_census_Resource_BasicUnit_SECS; - resource r = {(char *)"client_rpc_latency", // name - (char *)"Client RPC latency in seconds", // description - 0, // prefix - 1, // n_numerators - &numerator, // numerators - 0, // n_denominators - NULL}; // denominators - define_resource(&r); - r = (resource){(char *)"server_rpc_latency", // name - (char *)"Server RPC latency in seconds", // description - 0, // prefix - 1, // n_numerators - &numerator, // numerators - 0, // n_denominators - NULL}; // denominators - define_resource(&r); -} diff --git a/src/core/ext/census/base_resources.cc b/src/core/ext/census/base_resources.cc new file mode 100644 index 0000000000..1f2bb39fe0 --- /dev/null +++ b/src/core/ext/census/base_resources.cc @@ -0,0 +1,56 @@ +/* + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/base_resources.h" + +#include +#include + +#include +#include + +#include "src/core/ext/census/resource.h" + +// Add base RPC resource definitions for use by RPC runtime. +// +// TODO(aveitch): All of these are currently hardwired definitions encoded in +// the code in this file. These should be converted to use an external +// configuration mechanism, in which these resources are defined in a text +// file, which is compiled to .pb format and read by still-to-be-written +// configuration functions. + +// Define all base resources. This should be called by census initialization. +void define_base_resources() { + google_census_Resource_BasicUnit numerator = + google_census_Resource_BasicUnit_SECS; + resource r = {(char *)"client_rpc_latency", // name + (char *)"Client RPC latency in seconds", // description + 0, // prefix + 1, // n_numerators + &numerator, // numerators + 0, // n_denominators + NULL}; // denominators + define_resource(&r); + r = (resource){(char *)"server_rpc_latency", // name + (char *)"Server RPC latency in seconds", // description + 0, // prefix + 1, // n_numerators + &numerator, // numerators + 0, // n_denominators + NULL}; // denominators + define_resource(&r); +} diff --git a/src/core/ext/census/census_init.c b/src/core/ext/census/census_init.c deleted file mode 100644 index d7f719ff8c..0000000000 --- a/src/core/ext/census/census_init.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/census_interface.h" - -#include -#include "src/core/ext/census/census_rpc_stats.h" -#include "src/core/ext/census/census_tracing.h" - -void census_init(void) { - census_tracing_init(); - census_stats_store_init(); -} - -void census_shutdown(void) { - census_stats_store_shutdown(); - census_tracing_shutdown(); -} diff --git a/src/core/ext/census/census_init.cc b/src/core/ext/census/census_init.cc new file mode 100644 index 0000000000..d7f719ff8c --- /dev/null +++ b/src/core/ext/census/census_init.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/census_interface.h" + +#include +#include "src/core/ext/census/census_rpc_stats.h" +#include "src/core/ext/census/census_tracing.h" + +void census_init(void) { + census_tracing_init(); + census_stats_store_init(); +} + +void census_shutdown(void) { + census_stats_store_shutdown(); + census_tracing_shutdown(); +} diff --git a/src/core/ext/census/census_log.c b/src/core/ext/census/census_log.c deleted file mode 100644 index 100047f12b..0000000000 --- a/src/core/ext/census/census_log.c +++ /dev/null @@ -1,588 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Available log space is divided up in blocks of - CENSUS_LOG_2_MAX_RECORD_SIZE bytes. A block can be in one of the - following three data structures: - - Free blocks (free_block_list) - - Blocks with unread data (dirty_block_list) - - Blocks currently attached to cores (core_local_blocks[]) - - census_log_start_write() moves a block from core_local_blocks[] to the - end of dirty_block_list when block: - - is out-of-space OR - - has an incomplete record (an incomplete record occurs when a thread calls - census_log_start_write() and is context-switched before calling - census_log_end_write() - So, blocks in dirty_block_list are ordered, from oldest to newest, by time - when block is detached from the core. - - census_log_read_next() first iterates over dirty_block_list and then - core_local_blocks[]. It moves completely read blocks from dirty_block_list - to free_block_list. Blocks in core_local_blocks[] are not freed, even when - completely read. - - If log is configured to discard old records and free_block_list is empty, - census_log_start_write() iterates over dirty_block_list to allocate a - new block. It moves the oldest available block (no pending read/write) to - core_local_blocks[]. - - core_local_block_struct is used to implement a map from core id to the block - associated with that core. This mapping is advisory. It is possible that the - block returned by this mapping is no longer associated with that core. This - mapping is updated, lazily, by census_log_start_write(). - - Locking in block struct: - - Exclusive g_log.lock must be held before calling any functions operatong on - block structs except census_log_start_write() and - census_log_end_write(). - - Writes to a block are serialized via writer_lock. - census_log_start_write() acquires this lock and - census_log_end_write() releases it. On failure to acquire the lock, - writer allocates a new block for the current core and updates - core_local_block accordingly. - - Simultaneous read and write access is allowed. Reader can safely read up to - committed bytes (bytes_committed). - - reader_lock protects the block, currently being read, from getting recycled. - start_read() acquires reader_lock and end_read() releases the lock. - - Read/write access to a block is disabled via try_disable_access(). It returns - with both writer_lock and reader_lock held. These locks are subsequently - released by enable_access() to enable access to the block. - - A note on naming: Most function/struct names are prepended by cl_ - (shorthand for census_log). Further, functions that manipulate structures - include the name of the structure, which will be passed as the first - argument. E.g. cl_block_initialize() will initialize a cl_block. -*/ -#include "src/core/ext/census/census_log.h" -#include -#include -#include -#include -#include -#include -#include -#include - -/* End of platform specific code */ - -typedef struct census_log_block_list_struct { - struct census_log_block_list_struct *next; - struct census_log_block_list_struct *prev; - struct census_log_block *block; -} cl_block_list_struct; - -typedef struct census_log_block { - /* Pointer to underlying buffer */ - char *buffer; - gpr_atm writer_lock; - gpr_atm reader_lock; - /* Keeps completely written bytes. Declared atomic because accessed - simultaneously by reader and writer. */ - gpr_atm bytes_committed; - /* Bytes already read */ - int32_t bytes_read; - /* Links for list */ - cl_block_list_struct link; -/* We want this structure to be cacheline aligned. We assume the following - sizes for the various parts on 32/64bit systems: - type 32b size 64b size - char* 4 8 - 3x gpr_atm 12 24 - int32_t 4 8 (assumes padding) - cl_block_list_struct 12 24 - TOTAL 32 64 - - Depending on the size of our cacheline and the architecture, we - selectively add char buffering to this structure. The size is checked - via assert in census_log_initialize(). */ -#if defined(GPR_ARCH_64) -#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 64) -#else -#if defined(GPR_ARCH_32) -#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 32) -#else -#error "Unknown architecture" -#endif -#endif -#if CL_BLOCK_PAD_SIZE > 0 - char padding[CL_BLOCK_PAD_SIZE]; -#endif -} cl_block; - -/* A list of cl_blocks, doubly-linked through cl_block::link. */ -typedef struct census_log_block_list { - int32_t count; /* Number of items in list. */ - cl_block_list_struct ht; /* head/tail of linked list. */ -} cl_block_list; - -/* Cacheline aligned block pointers to avoid false sharing. Block pointer must - be initialized via set_block(), before calling other functions */ -typedef struct census_log_core_local_block { - gpr_atm block; -/* Ensure cachline alignment: we assume sizeof(gpr_atm) == 4 or 8 */ -#if defined(GPR_ARCH_64) -#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 8) -#else -#if defined(GPR_ARCH_32) -#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 4) -#else -#error "Unknown architecture" -#endif -#endif -#if CL_CORE_LOCAL_BLOCK_PAD_SIZE > 0 - char padding[CL_CORE_LOCAL_BLOCK_PAD_SIZE]; -#endif -} cl_core_local_block; - -struct census_log { - int discard_old_records; - /* Number of cores (aka hardware-contexts) */ - unsigned num_cores; - /* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */ - int32_t num_blocks; - cl_block *blocks; /* Block metadata. */ - cl_core_local_block *core_local_blocks; /* Keeps core to block mappings. */ - gpr_mu lock; - int initialized; /* has log been initialized? */ - /* Keeps the state of the reader iterator. A value of 0 indicates that - iterator has reached the end. census_log_init_reader() resets the - value to num_core to restart iteration. */ - uint32_t read_iterator_state; - /* Points to the block being read. If non-NULL, the block is locked for - reading (block_being_read_->reader_lock is held). */ - cl_block *block_being_read; - /* A non-zero value indicates that log is full. */ - gpr_atm is_full; - char *buffer; - cl_block_list free_block_list; - cl_block_list dirty_block_list; - gpr_atm out_of_space_count; -}; - -/* Single internal log */ -static struct census_log g_log; - -/* Functions that operate on an atomic memory location used as a lock */ - -/* Returns non-zero if lock is acquired */ -static int cl_try_lock(gpr_atm *lock) { return gpr_atm_acq_cas(lock, 0, 1); } - -static void cl_unlock(gpr_atm *lock) { gpr_atm_rel_store(lock, 0); } - -/* Functions that operate on cl_core_local_block's */ - -static void cl_core_local_block_set_block(cl_core_local_block *clb, - cl_block *block) { - gpr_atm_rel_store(&clb->block, (gpr_atm)block); -} - -static cl_block *cl_core_local_block_get_block(cl_core_local_block *clb) { - return (cl_block *)gpr_atm_acq_load(&clb->block); -} - -/* Functions that operate on cl_block_list_struct's */ - -static void cl_block_list_struct_initialize(cl_block_list_struct *bls, - cl_block *block) { - bls->next = bls->prev = bls; - bls->block = block; -} - -/* Functions that operate on cl_block_list's */ - -static void cl_block_list_initialize(cl_block_list *list) { - list->count = 0; - cl_block_list_struct_initialize(&list->ht, NULL); -} - -/* Returns head of *this, or NULL if empty. */ -static cl_block *cl_block_list_head(cl_block_list *list) { - return list->ht.next->block; -} - -/* Insert element *e after *pos. */ -static void cl_block_list_insert(cl_block_list *list, cl_block_list_struct *pos, - cl_block_list_struct *e) { - list->count++; - e->next = pos->next; - e->prev = pos; - e->next->prev = e; - e->prev->next = e; -} - -/* Insert block at the head of the list */ -static void cl_block_list_insert_at_head(cl_block_list *list, cl_block *block) { - cl_block_list_insert(list, &list->ht, &block->link); -} - -/* Insert block at the tail of the list */ -static void cl_block_list_insert_at_tail(cl_block_list *list, cl_block *block) { - cl_block_list_insert(list, list->ht.prev, &block->link); -} - -/* Removes block *b. Requires *b be in the list. */ -static void cl_block_list_remove(cl_block_list *list, cl_block *b) { - list->count--; - b->link.next->prev = b->link.prev; - b->link.prev->next = b->link.next; -} - -/* Functions that operate on cl_block's */ - -static void cl_block_initialize(cl_block *block, char *buffer) { - block->buffer = buffer; - gpr_atm_rel_store(&block->writer_lock, 0); - gpr_atm_rel_store(&block->reader_lock, 0); - gpr_atm_rel_store(&block->bytes_committed, 0); - block->bytes_read = 0; - cl_block_list_struct_initialize(&block->link, block); -} - -/* Guards against exposing partially written buffer to the reader. */ -static void cl_block_set_bytes_committed(cl_block *block, - int32_t bytes_committed) { - gpr_atm_rel_store(&block->bytes_committed, bytes_committed); -} - -static int32_t cl_block_get_bytes_committed(cl_block *block) { - return gpr_atm_acq_load(&block->bytes_committed); -} - -/* Tries to disable future read/write access to this block. Succeeds if: - - no in-progress write AND - - no in-progress read AND - - 'discard_data' set to true OR no unread data - On success, clears the block state and returns with writer_lock_ and - reader_lock_ held. These locks are released by a subsequent - cl_block_access_enable() call. */ -static int cl_block_try_disable_access(cl_block *block, int discard_data) { - if (!cl_try_lock(&block->writer_lock)) { - return 0; - } - if (!cl_try_lock(&block->reader_lock)) { - cl_unlock(&block->writer_lock); - return 0; - } - if (!discard_data && - (block->bytes_read != cl_block_get_bytes_committed(block))) { - cl_unlock(&block->reader_lock); - cl_unlock(&block->writer_lock); - return 0; - } - cl_block_set_bytes_committed(block, 0); - block->bytes_read = 0; - return 1; -} - -static void cl_block_enable_access(cl_block *block) { - cl_unlock(&block->reader_lock); - cl_unlock(&block->writer_lock); -} - -/* Returns with writer_lock held. */ -static void *cl_block_start_write(cl_block *block, size_t size) { - int32_t bytes_committed; - if (!cl_try_lock(&block->writer_lock)) { - return NULL; - } - bytes_committed = cl_block_get_bytes_committed(block); - if (bytes_committed + size > CENSUS_LOG_MAX_RECORD_SIZE) { - cl_unlock(&block->writer_lock); - return NULL; - } - return block->buffer + bytes_committed; -} - -/* Releases writer_lock and increments committed bytes by 'bytes_written'. - 'bytes_written' must be <= 'size' specified in the corresponding - StartWrite() call. This function is thread-safe. */ -static void cl_block_end_write(cl_block *block, size_t bytes_written) { - cl_block_set_bytes_committed( - block, cl_block_get_bytes_committed(block) + bytes_written); - cl_unlock(&block->writer_lock); -} - -/* Returns a pointer to the first unread byte in buffer. The number of bytes - available are returned in 'bytes_available'. Acquires reader lock that is - released by a subsequent cl_block_end_read() call. Returns NULL if: - - read in progress - - no data available */ -static void *cl_block_start_read(cl_block *block, size_t *bytes_available) { - void *record; - if (!cl_try_lock(&block->reader_lock)) { - return NULL; - } - /* bytes_committed may change from under us. Use bytes_available to update - bytes_read below. */ - *bytes_available = cl_block_get_bytes_committed(block) - block->bytes_read; - if (*bytes_available == 0) { - cl_unlock(&block->reader_lock); - return NULL; - } - record = block->buffer + block->bytes_read; - block->bytes_read += *bytes_available; - return record; -} - -static void cl_block_end_read(cl_block *block) { - cl_unlock(&block->reader_lock); -} - -/* Internal functions operating on g_log */ - -/* Allocates a new free block (or recycles an available dirty block if log is - configured to discard old records). Returns NULL if out-of-space. */ -static cl_block *cl_allocate_block(void) { - cl_block *block = cl_block_list_head(&g_log.free_block_list); - if (block != NULL) { - cl_block_list_remove(&g_log.free_block_list, block); - return block; - } - if (!g_log.discard_old_records) { - /* No free block and log is configured to keep old records. */ - return NULL; - } - /* Recycle dirty block. Start from the oldest. */ - for (block = cl_block_list_head(&g_log.dirty_block_list); block != NULL; - block = block->link.next->block) { - if (cl_block_try_disable_access(block, 1 /* discard data */)) { - cl_block_list_remove(&g_log.dirty_block_list, block); - return block; - } - } - return NULL; -} - -/* Allocates a new block and updates core id => block mapping. 'old_block' - points to the block that the caller thinks is attached to - 'core_id'. 'old_block' may be NULL. Returns non-zero if: - - allocated a new block OR - - 'core_id' => 'old_block' mapping changed (another thread allocated a - block before lock was acquired). */ -static int cl_allocate_core_local_block(int32_t core_id, cl_block *old_block) { - /* Now that we have the lock, check if core-local mapping has changed. */ - cl_core_local_block *core_local_block = &g_log.core_local_blocks[core_id]; - cl_block *block = cl_core_local_block_get_block(core_local_block); - if ((block != NULL) && (block != old_block)) { - return 1; - } - if (block != NULL) { - cl_core_local_block_set_block(core_local_block, NULL); - cl_block_list_insert_at_tail(&g_log.dirty_block_list, block); - } - block = cl_allocate_block(); - if (block == NULL) { - gpr_atm_rel_store(&g_log.is_full, 1); - return 0; - } - cl_core_local_block_set_block(core_local_block, block); - cl_block_enable_access(block); - return 1; -} - -static cl_block *cl_get_block(void *record) { - uintptr_t p = (uintptr_t)((char *)record - g_log.buffer); - uintptr_t index = p >> CENSUS_LOG_2_MAX_RECORD_SIZE; - return &g_log.blocks[index]; -} - -/* Gets the next block to read and tries to free 'prev' block (if not NULL). - Returns NULL if reached the end. */ -static cl_block *cl_next_block_to_read(cl_block *prev) { - cl_block *block = NULL; - if (g_log.read_iterator_state == g_log.num_cores) { - /* We are traversing dirty list; find the next dirty block. */ - if (prev != NULL) { - /* Try to free the previous block if there is no unread data. This block - may have unread data if previously incomplete record completed between - read_next() calls. */ - block = prev->link.next->block; - if (cl_block_try_disable_access(prev, 0 /* do not discard data */)) { - cl_block_list_remove(&g_log.dirty_block_list, prev); - cl_block_list_insert_at_head(&g_log.free_block_list, prev); - gpr_atm_rel_store(&g_log.is_full, 0); - } - } else { - block = cl_block_list_head(&g_log.dirty_block_list); - } - if (block != NULL) { - return block; - } - /* We are done with the dirty list; moving on to core-local blocks. */ - } - while (g_log.read_iterator_state > 0) { - g_log.read_iterator_state--; - block = cl_core_local_block_get_block( - &g_log.core_local_blocks[g_log.read_iterator_state]); - if (block != NULL) { - return block; - } - } - return NULL; -} - -/* External functions: primary stats_log interface */ -void census_log_initialize(size_t size_in_mb, int discard_old_records) { - int32_t ix; - /* Check cacheline alignment. */ - GPR_ASSERT(sizeof(cl_block) % GPR_CACHELINE_SIZE == 0); - GPR_ASSERT(sizeof(cl_core_local_block) % GPR_CACHELINE_SIZE == 0); - GPR_ASSERT(!g_log.initialized); - g_log.discard_old_records = discard_old_records; - g_log.num_cores = gpr_cpu_num_cores(); - /* Ensure at least as many blocks as there are cores. */ - g_log.num_blocks = GPR_MAX( - g_log.num_cores, (size_in_mb << 20) >> CENSUS_LOG_2_MAX_RECORD_SIZE); - gpr_mu_init(&g_log.lock); - g_log.read_iterator_state = 0; - g_log.block_being_read = NULL; - gpr_atm_rel_store(&g_log.is_full, 0); - g_log.core_local_blocks = (cl_core_local_block *)gpr_malloc_aligned( - g_log.num_cores * sizeof(cl_core_local_block), GPR_CACHELINE_SIZE_LOG); - memset(g_log.core_local_blocks, 0, - g_log.num_cores * sizeof(cl_core_local_block)); - g_log.blocks = (cl_block *)gpr_malloc_aligned( - g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); - memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); - g_log.buffer = gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); - memset(g_log.buffer, 0, g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); - cl_block_list_initialize(&g_log.free_block_list); - cl_block_list_initialize(&g_log.dirty_block_list); - for (ix = 0; ix < g_log.num_blocks; ++ix) { - cl_block *block = g_log.blocks + ix; - cl_block_initialize(block, - g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * ix)); - cl_block_try_disable_access(block, 1 /* discard data */); - cl_block_list_insert_at_tail(&g_log.free_block_list, block); - } - gpr_atm_rel_store(&g_log.out_of_space_count, 0); - g_log.initialized = 1; -} - -void census_log_shutdown(void) { - GPR_ASSERT(g_log.initialized); - gpr_mu_destroy(&g_log.lock); - gpr_free_aligned(g_log.core_local_blocks); - g_log.core_local_blocks = NULL; - gpr_free_aligned(g_log.blocks); - g_log.blocks = NULL; - gpr_free(g_log.buffer); - g_log.buffer = NULL; - g_log.initialized = 0; -} - -void *census_log_start_write(size_t size) { - /* Used to bound number of times block allocation is attempted. */ - int32_t attempts_remaining = g_log.num_blocks; - /* TODO(aveitch): move this inside the do loop when current_cpu is fixed */ - int32_t core_id = gpr_cpu_current_cpu(); - GPR_ASSERT(g_log.initialized); - if (size > CENSUS_LOG_MAX_RECORD_SIZE) { - return NULL; - } - do { - int allocated; - void *record = NULL; - cl_block *block = - cl_core_local_block_get_block(&g_log.core_local_blocks[core_id]); - if (block && (record = cl_block_start_write(block, size))) { - return record; - } - /* Need to allocate a new block. We are here if: - - No block associated with the core OR - - Write in-progress on the block OR - - block is out of space */ - if (gpr_atm_acq_load(&g_log.is_full)) { - gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); - return NULL; - } - gpr_mu_lock(&g_log.lock); - allocated = cl_allocate_core_local_block(core_id, block); - gpr_mu_unlock(&g_log.lock); - if (!allocated) { - gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); - return NULL; - } - } while (attempts_remaining--); - /* Give up. */ - gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); - return NULL; -} - -void census_log_end_write(void *record, size_t bytes_written) { - GPR_ASSERT(g_log.initialized); - cl_block_end_write(cl_get_block(record), bytes_written); -} - -void census_log_init_reader(void) { - GPR_ASSERT(g_log.initialized); - gpr_mu_lock(&g_log.lock); - /* If a block is locked for reading unlock it. */ - if (g_log.block_being_read != NULL) { - cl_block_end_read(g_log.block_being_read); - g_log.block_being_read = NULL; - } - g_log.read_iterator_state = g_log.num_cores; - gpr_mu_unlock(&g_log.lock); -} - -const void *census_log_read_next(size_t *bytes_available) { - GPR_ASSERT(g_log.initialized); - gpr_mu_lock(&g_log.lock); - if (g_log.block_being_read != NULL) { - cl_block_end_read(g_log.block_being_read); - } - do { - g_log.block_being_read = cl_next_block_to_read(g_log.block_being_read); - if (g_log.block_being_read != NULL) { - void *record = - cl_block_start_read(g_log.block_being_read, bytes_available); - if (record != NULL) { - gpr_mu_unlock(&g_log.lock); - return record; - } - } - } while (g_log.block_being_read != NULL); - gpr_mu_unlock(&g_log.lock); - return NULL; -} - -size_t census_log_remaining_space(void) { - size_t space; - GPR_ASSERT(g_log.initialized); - gpr_mu_lock(&g_log.lock); - if (g_log.discard_old_records) { - /* Remaining space is not meaningful; just return the entire log space. */ - space = g_log.num_blocks << CENSUS_LOG_2_MAX_RECORD_SIZE; - } else { - space = g_log.free_block_list.count * CENSUS_LOG_MAX_RECORD_SIZE; - } - gpr_mu_unlock(&g_log.lock); - return space; -} - -int census_log_out_of_space_count(void) { - GPR_ASSERT(g_log.initialized); - return gpr_atm_acq_load(&g_log.out_of_space_count); -} diff --git a/src/core/ext/census/census_log.cc b/src/core/ext/census/census_log.cc new file mode 100644 index 0000000000..100047f12b --- /dev/null +++ b/src/core/ext/census/census_log.cc @@ -0,0 +1,588 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Available log space is divided up in blocks of + CENSUS_LOG_2_MAX_RECORD_SIZE bytes. A block can be in one of the + following three data structures: + - Free blocks (free_block_list) + - Blocks with unread data (dirty_block_list) + - Blocks currently attached to cores (core_local_blocks[]) + + census_log_start_write() moves a block from core_local_blocks[] to the + end of dirty_block_list when block: + - is out-of-space OR + - has an incomplete record (an incomplete record occurs when a thread calls + census_log_start_write() and is context-switched before calling + census_log_end_write() + So, blocks in dirty_block_list are ordered, from oldest to newest, by time + when block is detached from the core. + + census_log_read_next() first iterates over dirty_block_list and then + core_local_blocks[]. It moves completely read blocks from dirty_block_list + to free_block_list. Blocks in core_local_blocks[] are not freed, even when + completely read. + + If log is configured to discard old records and free_block_list is empty, + census_log_start_write() iterates over dirty_block_list to allocate a + new block. It moves the oldest available block (no pending read/write) to + core_local_blocks[]. + + core_local_block_struct is used to implement a map from core id to the block + associated with that core. This mapping is advisory. It is possible that the + block returned by this mapping is no longer associated with that core. This + mapping is updated, lazily, by census_log_start_write(). + + Locking in block struct: + + Exclusive g_log.lock must be held before calling any functions operatong on + block structs except census_log_start_write() and + census_log_end_write(). + + Writes to a block are serialized via writer_lock. + census_log_start_write() acquires this lock and + census_log_end_write() releases it. On failure to acquire the lock, + writer allocates a new block for the current core and updates + core_local_block accordingly. + + Simultaneous read and write access is allowed. Reader can safely read up to + committed bytes (bytes_committed). + + reader_lock protects the block, currently being read, from getting recycled. + start_read() acquires reader_lock and end_read() releases the lock. + + Read/write access to a block is disabled via try_disable_access(). It returns + with both writer_lock and reader_lock held. These locks are subsequently + released by enable_access() to enable access to the block. + + A note on naming: Most function/struct names are prepended by cl_ + (shorthand for census_log). Further, functions that manipulate structures + include the name of the structure, which will be passed as the first + argument. E.g. cl_block_initialize() will initialize a cl_block. +*/ +#include "src/core/ext/census/census_log.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* End of platform specific code */ + +typedef struct census_log_block_list_struct { + struct census_log_block_list_struct *next; + struct census_log_block_list_struct *prev; + struct census_log_block *block; +} cl_block_list_struct; + +typedef struct census_log_block { + /* Pointer to underlying buffer */ + char *buffer; + gpr_atm writer_lock; + gpr_atm reader_lock; + /* Keeps completely written bytes. Declared atomic because accessed + simultaneously by reader and writer. */ + gpr_atm bytes_committed; + /* Bytes already read */ + int32_t bytes_read; + /* Links for list */ + cl_block_list_struct link; +/* We want this structure to be cacheline aligned. We assume the following + sizes for the various parts on 32/64bit systems: + type 32b size 64b size + char* 4 8 + 3x gpr_atm 12 24 + int32_t 4 8 (assumes padding) + cl_block_list_struct 12 24 + TOTAL 32 64 + + Depending on the size of our cacheline and the architecture, we + selectively add char buffering to this structure. The size is checked + via assert in census_log_initialize(). */ +#if defined(GPR_ARCH_64) +#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 64) +#else +#if defined(GPR_ARCH_32) +#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 32) +#else +#error "Unknown architecture" +#endif +#endif +#if CL_BLOCK_PAD_SIZE > 0 + char padding[CL_BLOCK_PAD_SIZE]; +#endif +} cl_block; + +/* A list of cl_blocks, doubly-linked through cl_block::link. */ +typedef struct census_log_block_list { + int32_t count; /* Number of items in list. */ + cl_block_list_struct ht; /* head/tail of linked list. */ +} cl_block_list; + +/* Cacheline aligned block pointers to avoid false sharing. Block pointer must + be initialized via set_block(), before calling other functions */ +typedef struct census_log_core_local_block { + gpr_atm block; +/* Ensure cachline alignment: we assume sizeof(gpr_atm) == 4 or 8 */ +#if defined(GPR_ARCH_64) +#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 8) +#else +#if defined(GPR_ARCH_32) +#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 4) +#else +#error "Unknown architecture" +#endif +#endif +#if CL_CORE_LOCAL_BLOCK_PAD_SIZE > 0 + char padding[CL_CORE_LOCAL_BLOCK_PAD_SIZE]; +#endif +} cl_core_local_block; + +struct census_log { + int discard_old_records; + /* Number of cores (aka hardware-contexts) */ + unsigned num_cores; + /* number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log */ + int32_t num_blocks; + cl_block *blocks; /* Block metadata. */ + cl_core_local_block *core_local_blocks; /* Keeps core to block mappings. */ + gpr_mu lock; + int initialized; /* has log been initialized? */ + /* Keeps the state of the reader iterator. A value of 0 indicates that + iterator has reached the end. census_log_init_reader() resets the + value to num_core to restart iteration. */ + uint32_t read_iterator_state; + /* Points to the block being read. If non-NULL, the block is locked for + reading (block_being_read_->reader_lock is held). */ + cl_block *block_being_read; + /* A non-zero value indicates that log is full. */ + gpr_atm is_full; + char *buffer; + cl_block_list free_block_list; + cl_block_list dirty_block_list; + gpr_atm out_of_space_count; +}; + +/* Single internal log */ +static struct census_log g_log; + +/* Functions that operate on an atomic memory location used as a lock */ + +/* Returns non-zero if lock is acquired */ +static int cl_try_lock(gpr_atm *lock) { return gpr_atm_acq_cas(lock, 0, 1); } + +static void cl_unlock(gpr_atm *lock) { gpr_atm_rel_store(lock, 0); } + +/* Functions that operate on cl_core_local_block's */ + +static void cl_core_local_block_set_block(cl_core_local_block *clb, + cl_block *block) { + gpr_atm_rel_store(&clb->block, (gpr_atm)block); +} + +static cl_block *cl_core_local_block_get_block(cl_core_local_block *clb) { + return (cl_block *)gpr_atm_acq_load(&clb->block); +} + +/* Functions that operate on cl_block_list_struct's */ + +static void cl_block_list_struct_initialize(cl_block_list_struct *bls, + cl_block *block) { + bls->next = bls->prev = bls; + bls->block = block; +} + +/* Functions that operate on cl_block_list's */ + +static void cl_block_list_initialize(cl_block_list *list) { + list->count = 0; + cl_block_list_struct_initialize(&list->ht, NULL); +} + +/* Returns head of *this, or NULL if empty. */ +static cl_block *cl_block_list_head(cl_block_list *list) { + return list->ht.next->block; +} + +/* Insert element *e after *pos. */ +static void cl_block_list_insert(cl_block_list *list, cl_block_list_struct *pos, + cl_block_list_struct *e) { + list->count++; + e->next = pos->next; + e->prev = pos; + e->next->prev = e; + e->prev->next = e; +} + +/* Insert block at the head of the list */ +static void cl_block_list_insert_at_head(cl_block_list *list, cl_block *block) { + cl_block_list_insert(list, &list->ht, &block->link); +} + +/* Insert block at the tail of the list */ +static void cl_block_list_insert_at_tail(cl_block_list *list, cl_block *block) { + cl_block_list_insert(list, list->ht.prev, &block->link); +} + +/* Removes block *b. Requires *b be in the list. */ +static void cl_block_list_remove(cl_block_list *list, cl_block *b) { + list->count--; + b->link.next->prev = b->link.prev; + b->link.prev->next = b->link.next; +} + +/* Functions that operate on cl_block's */ + +static void cl_block_initialize(cl_block *block, char *buffer) { + block->buffer = buffer; + gpr_atm_rel_store(&block->writer_lock, 0); + gpr_atm_rel_store(&block->reader_lock, 0); + gpr_atm_rel_store(&block->bytes_committed, 0); + block->bytes_read = 0; + cl_block_list_struct_initialize(&block->link, block); +} + +/* Guards against exposing partially written buffer to the reader. */ +static void cl_block_set_bytes_committed(cl_block *block, + int32_t bytes_committed) { + gpr_atm_rel_store(&block->bytes_committed, bytes_committed); +} + +static int32_t cl_block_get_bytes_committed(cl_block *block) { + return gpr_atm_acq_load(&block->bytes_committed); +} + +/* Tries to disable future read/write access to this block. Succeeds if: + - no in-progress write AND + - no in-progress read AND + - 'discard_data' set to true OR no unread data + On success, clears the block state and returns with writer_lock_ and + reader_lock_ held. These locks are released by a subsequent + cl_block_access_enable() call. */ +static int cl_block_try_disable_access(cl_block *block, int discard_data) { + if (!cl_try_lock(&block->writer_lock)) { + return 0; + } + if (!cl_try_lock(&block->reader_lock)) { + cl_unlock(&block->writer_lock); + return 0; + } + if (!discard_data && + (block->bytes_read != cl_block_get_bytes_committed(block))) { + cl_unlock(&block->reader_lock); + cl_unlock(&block->writer_lock); + return 0; + } + cl_block_set_bytes_committed(block, 0); + block->bytes_read = 0; + return 1; +} + +static void cl_block_enable_access(cl_block *block) { + cl_unlock(&block->reader_lock); + cl_unlock(&block->writer_lock); +} + +/* Returns with writer_lock held. */ +static void *cl_block_start_write(cl_block *block, size_t size) { + int32_t bytes_committed; + if (!cl_try_lock(&block->writer_lock)) { + return NULL; + } + bytes_committed = cl_block_get_bytes_committed(block); + if (bytes_committed + size > CENSUS_LOG_MAX_RECORD_SIZE) { + cl_unlock(&block->writer_lock); + return NULL; + } + return block->buffer + bytes_committed; +} + +/* Releases writer_lock and increments committed bytes by 'bytes_written'. + 'bytes_written' must be <= 'size' specified in the corresponding + StartWrite() call. This function is thread-safe. */ +static void cl_block_end_write(cl_block *block, size_t bytes_written) { + cl_block_set_bytes_committed( + block, cl_block_get_bytes_committed(block) + bytes_written); + cl_unlock(&block->writer_lock); +} + +/* Returns a pointer to the first unread byte in buffer. The number of bytes + available are returned in 'bytes_available'. Acquires reader lock that is + released by a subsequent cl_block_end_read() call. Returns NULL if: + - read in progress + - no data available */ +static void *cl_block_start_read(cl_block *block, size_t *bytes_available) { + void *record; + if (!cl_try_lock(&block->reader_lock)) { + return NULL; + } + /* bytes_committed may change from under us. Use bytes_available to update + bytes_read below. */ + *bytes_available = cl_block_get_bytes_committed(block) - block->bytes_read; + if (*bytes_available == 0) { + cl_unlock(&block->reader_lock); + return NULL; + } + record = block->buffer + block->bytes_read; + block->bytes_read += *bytes_available; + return record; +} + +static void cl_block_end_read(cl_block *block) { + cl_unlock(&block->reader_lock); +} + +/* Internal functions operating on g_log */ + +/* Allocates a new free block (or recycles an available dirty block if log is + configured to discard old records). Returns NULL if out-of-space. */ +static cl_block *cl_allocate_block(void) { + cl_block *block = cl_block_list_head(&g_log.free_block_list); + if (block != NULL) { + cl_block_list_remove(&g_log.free_block_list, block); + return block; + } + if (!g_log.discard_old_records) { + /* No free block and log is configured to keep old records. */ + return NULL; + } + /* Recycle dirty block. Start from the oldest. */ + for (block = cl_block_list_head(&g_log.dirty_block_list); block != NULL; + block = block->link.next->block) { + if (cl_block_try_disable_access(block, 1 /* discard data */)) { + cl_block_list_remove(&g_log.dirty_block_list, block); + return block; + } + } + return NULL; +} + +/* Allocates a new block and updates core id => block mapping. 'old_block' + points to the block that the caller thinks is attached to + 'core_id'. 'old_block' may be NULL. Returns non-zero if: + - allocated a new block OR + - 'core_id' => 'old_block' mapping changed (another thread allocated a + block before lock was acquired). */ +static int cl_allocate_core_local_block(int32_t core_id, cl_block *old_block) { + /* Now that we have the lock, check if core-local mapping has changed. */ + cl_core_local_block *core_local_block = &g_log.core_local_blocks[core_id]; + cl_block *block = cl_core_local_block_get_block(core_local_block); + if ((block != NULL) && (block != old_block)) { + return 1; + } + if (block != NULL) { + cl_core_local_block_set_block(core_local_block, NULL); + cl_block_list_insert_at_tail(&g_log.dirty_block_list, block); + } + block = cl_allocate_block(); + if (block == NULL) { + gpr_atm_rel_store(&g_log.is_full, 1); + return 0; + } + cl_core_local_block_set_block(core_local_block, block); + cl_block_enable_access(block); + return 1; +} + +static cl_block *cl_get_block(void *record) { + uintptr_t p = (uintptr_t)((char *)record - g_log.buffer); + uintptr_t index = p >> CENSUS_LOG_2_MAX_RECORD_SIZE; + return &g_log.blocks[index]; +} + +/* Gets the next block to read and tries to free 'prev' block (if not NULL). + Returns NULL if reached the end. */ +static cl_block *cl_next_block_to_read(cl_block *prev) { + cl_block *block = NULL; + if (g_log.read_iterator_state == g_log.num_cores) { + /* We are traversing dirty list; find the next dirty block. */ + if (prev != NULL) { + /* Try to free the previous block if there is no unread data. This block + may have unread data if previously incomplete record completed between + read_next() calls. */ + block = prev->link.next->block; + if (cl_block_try_disable_access(prev, 0 /* do not discard data */)) { + cl_block_list_remove(&g_log.dirty_block_list, prev); + cl_block_list_insert_at_head(&g_log.free_block_list, prev); + gpr_atm_rel_store(&g_log.is_full, 0); + } + } else { + block = cl_block_list_head(&g_log.dirty_block_list); + } + if (block != NULL) { + return block; + } + /* We are done with the dirty list; moving on to core-local blocks. */ + } + while (g_log.read_iterator_state > 0) { + g_log.read_iterator_state--; + block = cl_core_local_block_get_block( + &g_log.core_local_blocks[g_log.read_iterator_state]); + if (block != NULL) { + return block; + } + } + return NULL; +} + +/* External functions: primary stats_log interface */ +void census_log_initialize(size_t size_in_mb, int discard_old_records) { + int32_t ix; + /* Check cacheline alignment. */ + GPR_ASSERT(sizeof(cl_block) % GPR_CACHELINE_SIZE == 0); + GPR_ASSERT(sizeof(cl_core_local_block) % GPR_CACHELINE_SIZE == 0); + GPR_ASSERT(!g_log.initialized); + g_log.discard_old_records = discard_old_records; + g_log.num_cores = gpr_cpu_num_cores(); + /* Ensure at least as many blocks as there are cores. */ + g_log.num_blocks = GPR_MAX( + g_log.num_cores, (size_in_mb << 20) >> CENSUS_LOG_2_MAX_RECORD_SIZE); + gpr_mu_init(&g_log.lock); + g_log.read_iterator_state = 0; + g_log.block_being_read = NULL; + gpr_atm_rel_store(&g_log.is_full, 0); + g_log.core_local_blocks = (cl_core_local_block *)gpr_malloc_aligned( + g_log.num_cores * sizeof(cl_core_local_block), GPR_CACHELINE_SIZE_LOG); + memset(g_log.core_local_blocks, 0, + g_log.num_cores * sizeof(cl_core_local_block)); + g_log.blocks = (cl_block *)gpr_malloc_aligned( + g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); + memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); + g_log.buffer = gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + memset(g_log.buffer, 0, g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + cl_block_list_initialize(&g_log.free_block_list); + cl_block_list_initialize(&g_log.dirty_block_list); + for (ix = 0; ix < g_log.num_blocks; ++ix) { + cl_block *block = g_log.blocks + ix; + cl_block_initialize(block, + g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * ix)); + cl_block_try_disable_access(block, 1 /* discard data */); + cl_block_list_insert_at_tail(&g_log.free_block_list, block); + } + gpr_atm_rel_store(&g_log.out_of_space_count, 0); + g_log.initialized = 1; +} + +void census_log_shutdown(void) { + GPR_ASSERT(g_log.initialized); + gpr_mu_destroy(&g_log.lock); + gpr_free_aligned(g_log.core_local_blocks); + g_log.core_local_blocks = NULL; + gpr_free_aligned(g_log.blocks); + g_log.blocks = NULL; + gpr_free(g_log.buffer); + g_log.buffer = NULL; + g_log.initialized = 0; +} + +void *census_log_start_write(size_t size) { + /* Used to bound number of times block allocation is attempted. */ + int32_t attempts_remaining = g_log.num_blocks; + /* TODO(aveitch): move this inside the do loop when current_cpu is fixed */ + int32_t core_id = gpr_cpu_current_cpu(); + GPR_ASSERT(g_log.initialized); + if (size > CENSUS_LOG_MAX_RECORD_SIZE) { + return NULL; + } + do { + int allocated; + void *record = NULL; + cl_block *block = + cl_core_local_block_get_block(&g_log.core_local_blocks[core_id]); + if (block && (record = cl_block_start_write(block, size))) { + return record; + } + /* Need to allocate a new block. We are here if: + - No block associated with the core OR + - Write in-progress on the block OR + - block is out of space */ + if (gpr_atm_acq_load(&g_log.is_full)) { + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; + } + gpr_mu_lock(&g_log.lock); + allocated = cl_allocate_core_local_block(core_id, block); + gpr_mu_unlock(&g_log.lock); + if (!allocated) { + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; + } + } while (attempts_remaining--); + /* Give up. */ + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; +} + +void census_log_end_write(void *record, size_t bytes_written) { + GPR_ASSERT(g_log.initialized); + cl_block_end_write(cl_get_block(record), bytes_written); +} + +void census_log_init_reader(void) { + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + /* If a block is locked for reading unlock it. */ + if (g_log.block_being_read != NULL) { + cl_block_end_read(g_log.block_being_read); + g_log.block_being_read = NULL; + } + g_log.read_iterator_state = g_log.num_cores; + gpr_mu_unlock(&g_log.lock); +} + +const void *census_log_read_next(size_t *bytes_available) { + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + if (g_log.block_being_read != NULL) { + cl_block_end_read(g_log.block_being_read); + } + do { + g_log.block_being_read = cl_next_block_to_read(g_log.block_being_read); + if (g_log.block_being_read != NULL) { + void *record = + cl_block_start_read(g_log.block_being_read, bytes_available); + if (record != NULL) { + gpr_mu_unlock(&g_log.lock); + return record; + } + } + } while (g_log.block_being_read != NULL); + gpr_mu_unlock(&g_log.lock); + return NULL; +} + +size_t census_log_remaining_space(void) { + size_t space; + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + if (g_log.discard_old_records) { + /* Remaining space is not meaningful; just return the entire log space. */ + space = g_log.num_blocks << CENSUS_LOG_2_MAX_RECORD_SIZE; + } else { + space = g_log.free_block_list.count * CENSUS_LOG_MAX_RECORD_SIZE; + } + gpr_mu_unlock(&g_log.lock); + return space; +} + +int census_log_out_of_space_count(void) { + GPR_ASSERT(g_log.initialized); + return gpr_atm_acq_load(&g_log.out_of_space_count); +} diff --git a/src/core/ext/census/census_rpc_stats.c b/src/core/ext/census/census_rpc_stats.c deleted file mode 100644 index 0aca1f109e..0000000000 --- a/src/core/ext/census/census_rpc_stats.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include -#include "src/core/ext/census/census_interface.h" -#include "src/core/ext/census/census_rpc_stats.h" -#include "src/core/ext/census/census_tracing.h" -#include "src/core/ext/census/hash_table.h" -#include "src/core/ext/census/window_stats.h" -#include "src/core/lib/support/murmur_hash.h" -#include "src/core/lib/support/string.h" - -#define NUM_INTERVALS 3 -#define MINUTE_INTERVAL 0 -#define HOUR_INTERVAL 1 -#define TOTAL_INTERVAL 2 - -/* for easier typing */ -typedef census_per_method_rpc_stats per_method_stats; - -/* Ensure mu is only initialized once. */ -static gpr_once g_stats_store_mu_init = GPR_ONCE_INIT; -/* Guards two stats stores. */ -static gpr_mu g_mu; -static census_ht *g_client_stats_store = NULL; -static census_ht *g_server_stats_store = NULL; - -static void init_mutex(void) { gpr_mu_init(&g_mu); } - -static void init_mutex_once(void) { - gpr_once_init(&g_stats_store_mu_init, init_mutex); -} - -static int cmp_str_keys(const void *k1, const void *k2) { - return strcmp((const char *)k1, (const char *)k2); -} - -/* TODO(hongyu): replace it with cityhash64 */ -static uint64_t simple_hash(const void *k) { - size_t len = strlen(k); - uint64_t higher = gpr_murmur_hash3((const char *)k, len / 2, 0); - return higher << 32 | - gpr_murmur_hash3((const char *)k + len / 2, len - len / 2, 0); -} - -static void delete_stats(void *stats) { - census_window_stats_destroy((struct census_window_stats *)stats); -} - -static void delete_key(void *key) { gpr_free(key); } - -static const census_ht_option ht_opt = { - CENSUS_HT_POINTER /* key type */, 1999 /* n_of_buckets */, - simple_hash /* hash function */, cmp_str_keys /* key comparator */, - delete_stats /* data deleter */, delete_key /* key deleter */ -}; - -static void init_rpc_stats(void *stats) { - memset(stats, 0, sizeof(census_rpc_stats)); -} - -static void stat_add_proportion(double p, void *base, const void *addme) { - census_rpc_stats *b = (census_rpc_stats *)base; - census_rpc_stats *a = (census_rpc_stats *)addme; - b->cnt += p * a->cnt; - b->rpc_error_cnt += p * a->rpc_error_cnt; - b->app_error_cnt += p * a->app_error_cnt; - b->elapsed_time_ms += p * a->elapsed_time_ms; - b->api_request_bytes += p * a->api_request_bytes; - b->wire_request_bytes += p * a->wire_request_bytes; - b->api_response_bytes += p * a->api_response_bytes; - b->wire_response_bytes += p * a->wire_response_bytes; -} - -static void stat_add(void *base, const void *addme) { - stat_add_proportion(1.0, base, addme); -} - -static gpr_timespec min_hour_total_intervals[3] = { - {60, 0}, {3600, 0}, {36000000, 0}}; - -static const census_window_stats_stat_info window_stats_settings = { - sizeof(census_rpc_stats), init_rpc_stats, stat_add, stat_add_proportion}; - -census_rpc_stats *census_rpc_stats_create_empty(void) { - census_rpc_stats *ret = - (census_rpc_stats *)gpr_malloc(sizeof(census_rpc_stats)); - memset(ret, 0, sizeof(census_rpc_stats)); - return ret; -} - -void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats *data) { - int i = 0; - for (i = 0; i < data->num_entries; i++) { - if (data->stats[i].method != NULL) { - gpr_free((void *)data->stats[i].method); - } - } - if (data->stats != NULL) { - gpr_free(data->stats); - } - data->num_entries = 0; - data->stats = NULL; -} - -static void record_stats(census_ht *store, census_op_id op_id, - const census_rpc_stats *stats) { - gpr_mu_lock(&g_mu); - if (store != NULL) { - census_trace_obj *trace = NULL; - census_internal_lock_trace_store(); - trace = census_get_trace_obj_locked(op_id); - if (trace != NULL) { - const char *method_name = census_get_trace_method_name(trace); - struct census_window_stats *window_stats = NULL; - census_ht_key key; - key.ptr = (void *)method_name; - window_stats = census_ht_find(store, key); - census_internal_unlock_trace_store(); - if (window_stats == NULL) { - window_stats = census_window_stats_create(3, min_hour_total_intervals, - 30, &window_stats_settings); - key.ptr = gpr_strdup(key.ptr); - census_ht_insert(store, key, (void *)window_stats); - } - census_window_stats_add(window_stats, gpr_now(GPR_CLOCK_REALTIME), stats); - } else { - census_internal_unlock_trace_store(); - } - } - gpr_mu_unlock(&g_mu); -} - -void census_record_rpc_client_stats(census_op_id op_id, - const census_rpc_stats *stats) { - record_stats(g_client_stats_store, op_id, stats); -} - -void census_record_rpc_server_stats(census_op_id op_id, - const census_rpc_stats *stats) { - record_stats(g_server_stats_store, op_id, stats); -} - -/* Get stats from input stats store */ -static void get_stats(census_ht *store, census_aggregated_rpc_stats *data) { - GPR_ASSERT(data != NULL); - if (data->num_entries != 0) { - census_aggregated_rpc_stats_set_empty(data); - } - gpr_mu_lock(&g_mu); - if (store != NULL) { - size_t n; - unsigned i, j; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - census_ht_kv *kv = census_ht_get_all_elements(store, &n); - if (kv != NULL) { - data->num_entries = n; - data->stats = - (per_method_stats *)gpr_malloc(sizeof(per_method_stats) * n); - for (i = 0; i < n; i++) { - census_window_stats_sums sums[NUM_INTERVALS]; - for (j = 0; j < NUM_INTERVALS; j++) { - sums[j].statistic = (void *)census_rpc_stats_create_empty(); - } - data->stats[i].method = gpr_strdup(kv[i].k.ptr); - census_window_stats_get_sums(kv[i].v, now, sums); - data->stats[i].minute_stats = - *(census_rpc_stats *)sums[MINUTE_INTERVAL].statistic; - data->stats[i].hour_stats = - *(census_rpc_stats *)sums[HOUR_INTERVAL].statistic; - data->stats[i].total_stats = - *(census_rpc_stats *)sums[TOTAL_INTERVAL].statistic; - for (j = 0; j < NUM_INTERVALS; j++) { - gpr_free(sums[j].statistic); - } - } - gpr_free(kv); - } - } - gpr_mu_unlock(&g_mu); -} - -void census_get_client_stats(census_aggregated_rpc_stats *data) { - get_stats(g_client_stats_store, data); -} - -void census_get_server_stats(census_aggregated_rpc_stats *data) { - get_stats(g_server_stats_store, data); -} - -void census_stats_store_init(void) { - init_mutex_once(); - gpr_mu_lock(&g_mu); - if (g_client_stats_store == NULL && g_server_stats_store == NULL) { - g_client_stats_store = census_ht_create(&ht_opt); - g_server_stats_store = census_ht_create(&ht_opt); - } else { - gpr_log(GPR_ERROR, "Census stats store already initialized."); - } - gpr_mu_unlock(&g_mu); -} - -void census_stats_store_shutdown(void) { - init_mutex_once(); - gpr_mu_lock(&g_mu); - if (g_client_stats_store != NULL) { - census_ht_destroy(g_client_stats_store); - g_client_stats_store = NULL; - } else { - gpr_log(GPR_ERROR, "Census server stats store not initialized."); - } - if (g_server_stats_store != NULL) { - census_ht_destroy(g_server_stats_store); - g_server_stats_store = NULL; - } else { - gpr_log(GPR_ERROR, "Census client stats store not initialized."); - } - gpr_mu_unlock(&g_mu); -} diff --git a/src/core/ext/census/census_rpc_stats.cc b/src/core/ext/census/census_rpc_stats.cc new file mode 100644 index 0000000000..0aca1f109e --- /dev/null +++ b/src/core/ext/census/census_rpc_stats.cc @@ -0,0 +1,238 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include "src/core/ext/census/census_interface.h" +#include "src/core/ext/census/census_rpc_stats.h" +#include "src/core/ext/census/census_tracing.h" +#include "src/core/ext/census/hash_table.h" +#include "src/core/ext/census/window_stats.h" +#include "src/core/lib/support/murmur_hash.h" +#include "src/core/lib/support/string.h" + +#define NUM_INTERVALS 3 +#define MINUTE_INTERVAL 0 +#define HOUR_INTERVAL 1 +#define TOTAL_INTERVAL 2 + +/* for easier typing */ +typedef census_per_method_rpc_stats per_method_stats; + +/* Ensure mu is only initialized once. */ +static gpr_once g_stats_store_mu_init = GPR_ONCE_INIT; +/* Guards two stats stores. */ +static gpr_mu g_mu; +static census_ht *g_client_stats_store = NULL; +static census_ht *g_server_stats_store = NULL; + +static void init_mutex(void) { gpr_mu_init(&g_mu); } + +static void init_mutex_once(void) { + gpr_once_init(&g_stats_store_mu_init, init_mutex); +} + +static int cmp_str_keys(const void *k1, const void *k2) { + return strcmp((const char *)k1, (const char *)k2); +} + +/* TODO(hongyu): replace it with cityhash64 */ +static uint64_t simple_hash(const void *k) { + size_t len = strlen(k); + uint64_t higher = gpr_murmur_hash3((const char *)k, len / 2, 0); + return higher << 32 | + gpr_murmur_hash3((const char *)k + len / 2, len - len / 2, 0); +} + +static void delete_stats(void *stats) { + census_window_stats_destroy((struct census_window_stats *)stats); +} + +static void delete_key(void *key) { gpr_free(key); } + +static const census_ht_option ht_opt = { + CENSUS_HT_POINTER /* key type */, 1999 /* n_of_buckets */, + simple_hash /* hash function */, cmp_str_keys /* key comparator */, + delete_stats /* data deleter */, delete_key /* key deleter */ +}; + +static void init_rpc_stats(void *stats) { + memset(stats, 0, sizeof(census_rpc_stats)); +} + +static void stat_add_proportion(double p, void *base, const void *addme) { + census_rpc_stats *b = (census_rpc_stats *)base; + census_rpc_stats *a = (census_rpc_stats *)addme; + b->cnt += p * a->cnt; + b->rpc_error_cnt += p * a->rpc_error_cnt; + b->app_error_cnt += p * a->app_error_cnt; + b->elapsed_time_ms += p * a->elapsed_time_ms; + b->api_request_bytes += p * a->api_request_bytes; + b->wire_request_bytes += p * a->wire_request_bytes; + b->api_response_bytes += p * a->api_response_bytes; + b->wire_response_bytes += p * a->wire_response_bytes; +} + +static void stat_add(void *base, const void *addme) { + stat_add_proportion(1.0, base, addme); +} + +static gpr_timespec min_hour_total_intervals[3] = { + {60, 0}, {3600, 0}, {36000000, 0}}; + +static const census_window_stats_stat_info window_stats_settings = { + sizeof(census_rpc_stats), init_rpc_stats, stat_add, stat_add_proportion}; + +census_rpc_stats *census_rpc_stats_create_empty(void) { + census_rpc_stats *ret = + (census_rpc_stats *)gpr_malloc(sizeof(census_rpc_stats)); + memset(ret, 0, sizeof(census_rpc_stats)); + return ret; +} + +void census_aggregated_rpc_stats_set_empty(census_aggregated_rpc_stats *data) { + int i = 0; + for (i = 0; i < data->num_entries; i++) { + if (data->stats[i].method != NULL) { + gpr_free((void *)data->stats[i].method); + } + } + if (data->stats != NULL) { + gpr_free(data->stats); + } + data->num_entries = 0; + data->stats = NULL; +} + +static void record_stats(census_ht *store, census_op_id op_id, + const census_rpc_stats *stats) { + gpr_mu_lock(&g_mu); + if (store != NULL) { + census_trace_obj *trace = NULL; + census_internal_lock_trace_store(); + trace = census_get_trace_obj_locked(op_id); + if (trace != NULL) { + const char *method_name = census_get_trace_method_name(trace); + struct census_window_stats *window_stats = NULL; + census_ht_key key; + key.ptr = (void *)method_name; + window_stats = census_ht_find(store, key); + census_internal_unlock_trace_store(); + if (window_stats == NULL) { + window_stats = census_window_stats_create(3, min_hour_total_intervals, + 30, &window_stats_settings); + key.ptr = gpr_strdup(key.ptr); + census_ht_insert(store, key, (void *)window_stats); + } + census_window_stats_add(window_stats, gpr_now(GPR_CLOCK_REALTIME), stats); + } else { + census_internal_unlock_trace_store(); + } + } + gpr_mu_unlock(&g_mu); +} + +void census_record_rpc_client_stats(census_op_id op_id, + const census_rpc_stats *stats) { + record_stats(g_client_stats_store, op_id, stats); +} + +void census_record_rpc_server_stats(census_op_id op_id, + const census_rpc_stats *stats) { + record_stats(g_server_stats_store, op_id, stats); +} + +/* Get stats from input stats store */ +static void get_stats(census_ht *store, census_aggregated_rpc_stats *data) { + GPR_ASSERT(data != NULL); + if (data->num_entries != 0) { + census_aggregated_rpc_stats_set_empty(data); + } + gpr_mu_lock(&g_mu); + if (store != NULL) { + size_t n; + unsigned i, j; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + census_ht_kv *kv = census_ht_get_all_elements(store, &n); + if (kv != NULL) { + data->num_entries = n; + data->stats = + (per_method_stats *)gpr_malloc(sizeof(per_method_stats) * n); + for (i = 0; i < n; i++) { + census_window_stats_sums sums[NUM_INTERVALS]; + for (j = 0; j < NUM_INTERVALS; j++) { + sums[j].statistic = (void *)census_rpc_stats_create_empty(); + } + data->stats[i].method = gpr_strdup(kv[i].k.ptr); + census_window_stats_get_sums(kv[i].v, now, sums); + data->stats[i].minute_stats = + *(census_rpc_stats *)sums[MINUTE_INTERVAL].statistic; + data->stats[i].hour_stats = + *(census_rpc_stats *)sums[HOUR_INTERVAL].statistic; + data->stats[i].total_stats = + *(census_rpc_stats *)sums[TOTAL_INTERVAL].statistic; + for (j = 0; j < NUM_INTERVALS; j++) { + gpr_free(sums[j].statistic); + } + } + gpr_free(kv); + } + } + gpr_mu_unlock(&g_mu); +} + +void census_get_client_stats(census_aggregated_rpc_stats *data) { + get_stats(g_client_stats_store, data); +} + +void census_get_server_stats(census_aggregated_rpc_stats *data) { + get_stats(g_server_stats_store, data); +} + +void census_stats_store_init(void) { + init_mutex_once(); + gpr_mu_lock(&g_mu); + if (g_client_stats_store == NULL && g_server_stats_store == NULL) { + g_client_stats_store = census_ht_create(&ht_opt); + g_server_stats_store = census_ht_create(&ht_opt); + } else { + gpr_log(GPR_ERROR, "Census stats store already initialized."); + } + gpr_mu_unlock(&g_mu); +} + +void census_stats_store_shutdown(void) { + init_mutex_once(); + gpr_mu_lock(&g_mu); + if (g_client_stats_store != NULL) { + census_ht_destroy(g_client_stats_store); + g_client_stats_store = NULL; + } else { + gpr_log(GPR_ERROR, "Census server stats store not initialized."); + } + if (g_server_stats_store != NULL) { + census_ht_destroy(g_server_stats_store); + g_server_stats_store = NULL; + } else { + gpr_log(GPR_ERROR, "Census client stats store not initialized."); + } + gpr_mu_unlock(&g_mu); +} diff --git a/src/core/ext/census/census_tracing.c b/src/core/ext/census/census_tracing.c deleted file mode 100644 index 199b260dd1..0000000000 --- a/src/core/ext/census/census_tracing.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/census_tracing.h" -#include "src/core/ext/census/census_interface.h" - -#include -#include - -#include -#include -#include -#include -#include "src/core/ext/census/hash_table.h" -#include "src/core/lib/support/string.h" - -void census_trace_obj_destroy(census_trace_obj *obj) { - census_trace_annotation *p = obj->annotations; - while (p != NULL) { - census_trace_annotation *next = p->next; - gpr_free(p); - p = next; - } - gpr_free(obj->method); - gpr_free(obj); -} - -static void delete_trace_obj(void *obj) { - census_trace_obj_destroy((census_trace_obj *)obj); -} - -static const census_ht_option ht_opt = { - CENSUS_HT_UINT64 /* key type */, - 571 /* n_of_buckets */, - NULL /* hash */, - NULL /* compare_keys */, - delete_trace_obj /* delete data */, - NULL /* delete key */ -}; - -static gpr_once g_init_mutex_once = GPR_ONCE_INIT; -static gpr_mu g_mu; /* Guards following two static variables. */ -static census_ht *g_trace_store = NULL; -static uint64_t g_id = 0; - -static census_ht_key op_id_as_key(census_op_id *id) { - return *(census_ht_key *)id; -} - -static uint64_t op_id_2_uint64(census_op_id *id) { - uint64_t ret; - memcpy(&ret, id, sizeof(census_op_id)); - return ret; -} - -static void init_mutex(void) { gpr_mu_init(&g_mu); } - -static void init_mutex_once(void) { - gpr_once_init(&g_init_mutex_once, init_mutex); -} - -census_op_id census_tracing_start_op(void) { - gpr_mu_lock(&g_mu); - { - census_trace_obj *ret = gpr_malloc(sizeof(census_trace_obj)); - memset(ret, 0, sizeof(census_trace_obj)); - g_id++; - memcpy(&ret->id, &g_id, sizeof(census_op_id)); - ret->rpc_stats.cnt = 1; - ret->ts = gpr_now(GPR_CLOCK_REALTIME); - census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void *)ret); - gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); - gpr_mu_unlock(&g_mu); - return ret->id; - } -} - -int census_add_method_tag(census_op_id op_id, const char *method) { - int ret = 0; - census_trace_obj *trace = NULL; - gpr_mu_lock(&g_mu); - trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); - if (trace == NULL) { - ret = 1; - } else { - trace->method = gpr_strdup(method); - } - gpr_mu_unlock(&g_mu); - return ret; -} - -void census_tracing_print(census_op_id op_id, const char *anno_txt) { - census_trace_obj *trace = NULL; - gpr_mu_lock(&g_mu); - trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); - if (trace != NULL) { - census_trace_annotation *anno = gpr_malloc(sizeof(census_trace_annotation)); - anno->ts = gpr_now(GPR_CLOCK_REALTIME); - { - char *d = anno->txt; - const char *s = anno_txt; - int n = 0; - for (; n < CENSUS_MAX_ANNOTATION_LENGTH && *s != '\0'; ++n) { - *d++ = *s++; - } - *d = '\0'; - } - anno->next = trace->annotations; - trace->annotations = anno; - } - gpr_mu_unlock(&g_mu); -} - -void census_tracing_end_op(census_op_id op_id) { - census_trace_obj *trace = NULL; - gpr_mu_lock(&g_mu); - trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); - if (trace != NULL) { - trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros( - gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts)); - gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", - op_id_2_uint64(&op_id), trace->method, - trace->rpc_stats.elapsed_time_ms); - census_ht_erase(g_trace_store, op_id_as_key(&op_id)); - } - gpr_mu_unlock(&g_mu); -} - -void census_tracing_init(void) { - init_mutex_once(); - gpr_mu_lock(&g_mu); - if (g_trace_store == NULL) { - g_id = 1; - g_trace_store = census_ht_create(&ht_opt); - } else { - gpr_log(GPR_ERROR, "Census trace store already initialized."); - } - gpr_mu_unlock(&g_mu); -} - -void census_tracing_shutdown(void) { - gpr_mu_lock(&g_mu); - if (g_trace_store != NULL) { - census_ht_destroy(g_trace_store); - g_trace_store = NULL; - } else { - gpr_log(GPR_ERROR, "Census trace store is not initialized."); - } - gpr_mu_unlock(&g_mu); -} - -void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); } - -void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); } - -census_trace_obj *census_get_trace_obj_locked(census_op_id op_id) { - if (g_trace_store == NULL) { - gpr_log(GPR_ERROR, "Census trace store is not initialized."); - return NULL; - } - return (census_trace_obj *)census_ht_find(g_trace_store, - op_id_as_key(&op_id)); -} - -const char *census_get_trace_method_name(const census_trace_obj *trace) { - return trace->method; -} - -static census_trace_annotation *dup_annotation_chain( - census_trace_annotation *from) { - census_trace_annotation *ret = NULL; - census_trace_annotation **to = &ret; - for (; from != NULL; from = from->next) { - *to = gpr_malloc(sizeof(census_trace_annotation)); - memcpy(*to, from, sizeof(census_trace_annotation)); - to = &(*to)->next; - } - return ret; -} - -static census_trace_obj *trace_obj_dup(census_trace_obj *from) { - census_trace_obj *to = NULL; - GPR_ASSERT(from != NULL); - to = gpr_malloc(sizeof(census_trace_obj)); - to->id = from->id; - to->ts = from->ts; - to->rpc_stats = from->rpc_stats; - to->method = gpr_strdup(from->method); - to->annotations = dup_annotation_chain(from->annotations); - return to; -} - -census_trace_obj **census_get_active_ops(int *num_active_ops) { - census_trace_obj **ret = NULL; - gpr_mu_lock(&g_mu); - if (g_trace_store != NULL) { - size_t n = 0; - census_ht_kv *all_kvs = census_ht_get_all_elements(g_trace_store, &n); - *num_active_ops = (int)n; - if (n != 0) { - size_t i = 0; - ret = gpr_malloc(sizeof(census_trace_obj *) * n); - for (i = 0; i < n; i++) { - ret[i] = trace_obj_dup((census_trace_obj *)all_kvs[i].v); - } - } - gpr_free(all_kvs); - } - gpr_mu_unlock(&g_mu); - return ret; -} diff --git a/src/core/ext/census/census_tracing.cc b/src/core/ext/census/census_tracing.cc new file mode 100644 index 0000000000..199b260dd1 --- /dev/null +++ b/src/core/ext/census/census_tracing.cc @@ -0,0 +1,226 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/census_tracing.h" +#include "src/core/ext/census/census_interface.h" + +#include +#include + +#include +#include +#include +#include +#include "src/core/ext/census/hash_table.h" +#include "src/core/lib/support/string.h" + +void census_trace_obj_destroy(census_trace_obj *obj) { + census_trace_annotation *p = obj->annotations; + while (p != NULL) { + census_trace_annotation *next = p->next; + gpr_free(p); + p = next; + } + gpr_free(obj->method); + gpr_free(obj); +} + +static void delete_trace_obj(void *obj) { + census_trace_obj_destroy((census_trace_obj *)obj); +} + +static const census_ht_option ht_opt = { + CENSUS_HT_UINT64 /* key type */, + 571 /* n_of_buckets */, + NULL /* hash */, + NULL /* compare_keys */, + delete_trace_obj /* delete data */, + NULL /* delete key */ +}; + +static gpr_once g_init_mutex_once = GPR_ONCE_INIT; +static gpr_mu g_mu; /* Guards following two static variables. */ +static census_ht *g_trace_store = NULL; +static uint64_t g_id = 0; + +static census_ht_key op_id_as_key(census_op_id *id) { + return *(census_ht_key *)id; +} + +static uint64_t op_id_2_uint64(census_op_id *id) { + uint64_t ret; + memcpy(&ret, id, sizeof(census_op_id)); + return ret; +} + +static void init_mutex(void) { gpr_mu_init(&g_mu); } + +static void init_mutex_once(void) { + gpr_once_init(&g_init_mutex_once, init_mutex); +} + +census_op_id census_tracing_start_op(void) { + gpr_mu_lock(&g_mu); + { + census_trace_obj *ret = gpr_malloc(sizeof(census_trace_obj)); + memset(ret, 0, sizeof(census_trace_obj)); + g_id++; + memcpy(&ret->id, &g_id, sizeof(census_op_id)); + ret->rpc_stats.cnt = 1; + ret->ts = gpr_now(GPR_CLOCK_REALTIME); + census_ht_insert(g_trace_store, op_id_as_key(&ret->id), (void *)ret); + gpr_log(GPR_DEBUG, "Start tracing for id %lu", g_id); + gpr_mu_unlock(&g_mu); + return ret->id; + } +} + +int census_add_method_tag(census_op_id op_id, const char *method) { + int ret = 0; + census_trace_obj *trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace == NULL) { + ret = 1; + } else { + trace->method = gpr_strdup(method); + } + gpr_mu_unlock(&g_mu); + return ret; +} + +void census_tracing_print(census_op_id op_id, const char *anno_txt) { + census_trace_obj *trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace != NULL) { + census_trace_annotation *anno = gpr_malloc(sizeof(census_trace_annotation)); + anno->ts = gpr_now(GPR_CLOCK_REALTIME); + { + char *d = anno->txt; + const char *s = anno_txt; + int n = 0; + for (; n < CENSUS_MAX_ANNOTATION_LENGTH && *s != '\0'; ++n) { + *d++ = *s++; + } + *d = '\0'; + } + anno->next = trace->annotations; + trace->annotations = anno; + } + gpr_mu_unlock(&g_mu); +} + +void census_tracing_end_op(census_op_id op_id) { + census_trace_obj *trace = NULL; + gpr_mu_lock(&g_mu); + trace = census_ht_find(g_trace_store, op_id_as_key(&op_id)); + if (trace != NULL) { + trace->rpc_stats.elapsed_time_ms = gpr_timespec_to_micros( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), trace->ts)); + gpr_log(GPR_DEBUG, "End tracing for id %lu, method %s, latency %f us", + op_id_2_uint64(&op_id), trace->method, + trace->rpc_stats.elapsed_time_ms); + census_ht_erase(g_trace_store, op_id_as_key(&op_id)); + } + gpr_mu_unlock(&g_mu); +} + +void census_tracing_init(void) { + init_mutex_once(); + gpr_mu_lock(&g_mu); + if (g_trace_store == NULL) { + g_id = 1; + g_trace_store = census_ht_create(&ht_opt); + } else { + gpr_log(GPR_ERROR, "Census trace store already initialized."); + } + gpr_mu_unlock(&g_mu); +} + +void census_tracing_shutdown(void) { + gpr_mu_lock(&g_mu); + if (g_trace_store != NULL) { + census_ht_destroy(g_trace_store); + g_trace_store = NULL; + } else { + gpr_log(GPR_ERROR, "Census trace store is not initialized."); + } + gpr_mu_unlock(&g_mu); +} + +void census_internal_lock_trace_store(void) { gpr_mu_lock(&g_mu); } + +void census_internal_unlock_trace_store(void) { gpr_mu_unlock(&g_mu); } + +census_trace_obj *census_get_trace_obj_locked(census_op_id op_id) { + if (g_trace_store == NULL) { + gpr_log(GPR_ERROR, "Census trace store is not initialized."); + return NULL; + } + return (census_trace_obj *)census_ht_find(g_trace_store, + op_id_as_key(&op_id)); +} + +const char *census_get_trace_method_name(const census_trace_obj *trace) { + return trace->method; +} + +static census_trace_annotation *dup_annotation_chain( + census_trace_annotation *from) { + census_trace_annotation *ret = NULL; + census_trace_annotation **to = &ret; + for (; from != NULL; from = from->next) { + *to = gpr_malloc(sizeof(census_trace_annotation)); + memcpy(*to, from, sizeof(census_trace_annotation)); + to = &(*to)->next; + } + return ret; +} + +static census_trace_obj *trace_obj_dup(census_trace_obj *from) { + census_trace_obj *to = NULL; + GPR_ASSERT(from != NULL); + to = gpr_malloc(sizeof(census_trace_obj)); + to->id = from->id; + to->ts = from->ts; + to->rpc_stats = from->rpc_stats; + to->method = gpr_strdup(from->method); + to->annotations = dup_annotation_chain(from->annotations); + return to; +} + +census_trace_obj **census_get_active_ops(int *num_active_ops) { + census_trace_obj **ret = NULL; + gpr_mu_lock(&g_mu); + if (g_trace_store != NULL) { + size_t n = 0; + census_ht_kv *all_kvs = census_ht_get_all_elements(g_trace_store, &n); + *num_active_ops = (int)n; + if (n != 0) { + size_t i = 0; + ret = gpr_malloc(sizeof(census_trace_obj *) * n); + for (i = 0; i < n; i++) { + ret[i] = trace_obj_dup((census_trace_obj *)all_kvs[i].v); + } + } + gpr_free(all_kvs); + } + gpr_mu_unlock(&g_mu); + return ret; +} diff --git a/src/core/ext/census/context.c b/src/core/ext/census/context.c deleted file mode 100644 index 9b25a32e36..0000000000 --- a/src/core/ext/census/context.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include "src/core/lib/support/string.h" - -// Functions in this file support the public context API, including -// encoding/decoding as part of context propagation across RPC's. The overall -// requirements (in approximate priority order) for the -// context representation: -// 1. Efficient conversion to/from wire format -// 2. Minimal bytes used on-wire -// 3. Efficient context creation -// 4. Efficient lookup of tag value for a key -// 5. Efficient iteration over tags -// 6. Minimal memory footprint -// -// Notes on tradeoffs/decisions: -// * tag includes 1 byte length of key, as well as nil-terminating byte. These -// are to aid in efficient parsing and the ability to directly return key -// strings. This is more important than saving a single byte/tag on the wire. -// * The wire encoding uses only single byte values. This eliminates the need -// to handle endian-ness conversions. It also means there is a hard upper -// limit of 255 for both CENSUS_MAX_TAG_KV_LEN and CENSUS_MAX_PROPAGATED_TAGS. -// * Keep all tag information (keys/values/flags) in a single memory buffer, -// that can be directly copied to the wire. - -// min and max valid chars in tag keys and values. All printable ASCII is OK. -#define MIN_VALID_TAG_CHAR 32 // ' ' -#define MAX_VALID_TAG_CHAR 126 // '~' - -// Structure representing a set of tags. Essentially a count of number of tags -// present, and pointer to a chunk of memory that contains the per-tag details. -struct tag_set { - int ntags; // number of tags. - int ntags_alloc; // ntags + number of deleted tags (total number of tags - // in all of kvm). This will always be == ntags, except during the process - // of building a new tag set. - size_t kvm_size; // number of bytes allocated for key/value storage. - size_t kvm_used; // number of bytes of used key/value memory - char *kvm; // key/value memory. Consists of repeated entries of: - // Offset Size Description - // 0 1 Key length, including trailing 0. (K) - // 1 1 Value length, including trailing 0 (V) - // 2 1 Flags - // 3 K Key bytes - // 3 + K V Value bytes - // - // We refer to the first 3 entries as the 'tag header'. If extra values are - // introduced in the header, you will need to modify the TAG_HEADER_SIZE - // constant, the raw_tag structure (and everything that uses it) and the - // encode/decode functions appropriately. -}; - -// Number of bytes in tag header. -#define TAG_HEADER_SIZE 3 // key length (1) + value length (1) + flags (1) -// Offsets to tag header entries. -#define KEY_LEN_OFFSET 0 -#define VALUE_LEN_OFFSET 1 -#define FLAG_OFFSET 2 - -// raw_tag represents the raw-storage form of a tag in the kvm of a tag_set. -struct raw_tag { - uint8_t key_len; - uint8_t value_len; - uint8_t flags; - char *key; - char *value; -}; - -// Use a reserved flag bit for indication of deleted tag. -#define CENSUS_TAG_DELETED CENSUS_TAG_RESERVED -#define CENSUS_TAG_IS_DELETED(flags) (flags & CENSUS_TAG_DELETED) - -// Primary representation of a context. Composed of 2 underlying tag_set -// structs, one each for propagated and local (non-propagated) tags. This is -// to efficiently support tag encoding/decoding. -// TODO(aveitch): need to add tracing id's/structure. -struct census_context { - struct tag_set tags[2]; - census_context_status status; -}; - -// Indices into the tags member of census_context -#define PROPAGATED_TAGS 0 -#define LOCAL_TAGS 1 - -// Validate (check all characters are in range and size is less than limit) a -// key or value string. Returns 0 if the string is invalid, or the length -// (including terminator) if valid. -static size_t validate_tag(const char *kv) { - size_t len = 1; - char ch; - while ((ch = *kv++) != 0) { - if (ch < MIN_VALID_TAG_CHAR || ch > MAX_VALID_TAG_CHAR) { - return 0; - } - len++; - } - if (len > CENSUS_MAX_TAG_KV_LEN) { - return 0; - } - return len; -} - -// Extract a raw tag given a pointer (raw) to the tag header. Allow for some -// extra bytes in the tag header (see encode/decode functions for usage: this -// allows for future expansion of the tag header). -static char *decode_tag(struct raw_tag *tag, char *header, int offset) { - tag->key_len = (uint8_t)(*header++); - tag->value_len = (uint8_t)(*header++); - tag->flags = (uint8_t)(*header++); - header += offset; - tag->key = header; - header += tag->key_len; - tag->value = header; - return header + tag->value_len; -} - -// Make a copy (in 'to') of an existing tag_set. -static void tag_set_copy(struct tag_set *to, const struct tag_set *from) { - memcpy(to, from, sizeof(struct tag_set)); - to->kvm = (char *)gpr_malloc(to->kvm_size); - memcpy(to->kvm, from->kvm, from->kvm_used); -} - -// Delete a tag from a tag_set, if it exists (returns true if it did). -static bool tag_set_delete_tag(struct tag_set *tags, const char *key, - size_t key_len) { - char *kvp = tags->kvm; - for (int i = 0; i < tags->ntags_alloc; i++) { - uint8_t *flags = (uint8_t *)(kvp + FLAG_OFFSET); - struct raw_tag tag; - kvp = decode_tag(&tag, kvp, 0); - if (CENSUS_TAG_IS_DELETED(tag.flags)) continue; - if ((key_len == tag.key_len) && (memcmp(key, tag.key, key_len) == 0)) { - *flags |= CENSUS_TAG_DELETED; - tags->ntags--; - return true; - } - } - return false; -} - -// Delete a tag from a context, return true if it existed. -static bool context_delete_tag(census_context *context, const census_tag *tag, - size_t key_len) { - return ( - tag_set_delete_tag(&context->tags[LOCAL_TAGS], tag->key, key_len) || - tag_set_delete_tag(&context->tags[PROPAGATED_TAGS], tag->key, key_len)); -} - -// Add a tag to a tag_set. Return true on success, false if the tag could -// not be added because of constraints on tag set size. This function should -// not be called if the tag may already exist (in a non-deleted state) in -// the tag_set, as that would result in two tags with the same key. -static bool tag_set_add_tag(struct tag_set *tags, const census_tag *tag, - size_t key_len, size_t value_len) { - if (tags->ntags == CENSUS_MAX_PROPAGATED_TAGS) { - return false; - } - const size_t tag_size = key_len + value_len + TAG_HEADER_SIZE; - if (tags->kvm_used + tag_size > tags->kvm_size) { - // allocate new memory if needed - tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE; - char *new_kvm = (char *)gpr_malloc(tags->kvm_size); - if (tags->kvm_used > 0) memcpy(new_kvm, tags->kvm, tags->kvm_used); - gpr_free(tags->kvm); - tags->kvm = new_kvm; - } - char *kvp = tags->kvm + tags->kvm_used; - *kvp++ = (char)key_len; - *kvp++ = (char)value_len; - // ensure reserved flags are not used. - *kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS)); - memcpy(kvp, tag->key, key_len); - kvp += key_len; - memcpy(kvp, tag->value, value_len); - tags->kvm_used += tag_size; - tags->ntags++; - tags->ntags_alloc++; - return true; -} - -// Add/modify/delete a tag to/in a context. Caller must validate that tag key -// etc. are valid. -static void context_modify_tag(census_context *context, const census_tag *tag, - size_t key_len, size_t value_len) { - // First delete the tag if it is already present. - bool deleted = context_delete_tag(context, tag, key_len); - bool added = false; - if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) { - added = tag_set_add_tag(&context->tags[PROPAGATED_TAGS], tag, key_len, - value_len); - } else { - added = - tag_set_add_tag(&context->tags[LOCAL_TAGS], tag, key_len, value_len); - } - - if (deleted) { - context->status.n_modified_tags++; - } else { - if (added) { - context->status.n_added_tags++; - } else { - context->status.n_ignored_tags++; - } - } -} - -// Remove memory used for deleted tags from a tag set. Basic algorithm: -// 1) Walk through tag set to find first deleted tag. Record where it is. -// 2) Find the next not-deleted tag. Copy all of kvm from there to the end -// "over" the deleted tags -// 3) repeat #1 and #2 until we have seen all tags -// 4) if we are still looking for a not-deleted tag, then all the end portion -// of the kvm is deleted. Just reduce the used amount of memory by the -// appropriate amount. -static void tag_set_flatten(struct tag_set *tags) { - if (tags->ntags == tags->ntags_alloc) return; - bool found_deleted = false; // found a deleted tag. - char *kvp = tags->kvm; - char *dbase = NULL; // record location of deleted tag - for (int i = 0; i < tags->ntags_alloc; i++) { - struct raw_tag tag; - char *next_kvp = decode_tag(&tag, kvp, 0); - if (found_deleted) { - if (!CENSUS_TAG_IS_DELETED(tag.flags)) { - ptrdiff_t reduce = kvp - dbase; // #bytes in deleted tags - GPR_ASSERT(reduce > 0); - ptrdiff_t copy_size = tags->kvm + tags->kvm_used - kvp; - GPR_ASSERT(copy_size > 0); - memmove(dbase, kvp, (size_t)copy_size); - tags->kvm_used -= (size_t)reduce; - next_kvp -= reduce; - found_deleted = false; - } - } else { - if (CENSUS_TAG_IS_DELETED(tag.flags)) { - dbase = kvp; - found_deleted = true; - } - } - kvp = next_kvp; - } - if (found_deleted) { - GPR_ASSERT(dbase > tags->kvm); - tags->kvm_used = (size_t)(dbase - tags->kvm); - } - tags->ntags_alloc = tags->ntags; -} - -census_context *census_context_create(const census_context *base, - const census_tag *tags, int ntags, - census_context_status const **status) { - census_context *context = - (census_context *)gpr_malloc(sizeof(census_context)); - // If we are given a base, copy it into our new tag set. Otherwise set it - // to zero/NULL everything. - if (base == NULL) { - memset(context, 0, sizeof(census_context)); - } else { - tag_set_copy(&context->tags[PROPAGATED_TAGS], &base->tags[PROPAGATED_TAGS]); - tag_set_copy(&context->tags[LOCAL_TAGS], &base->tags[LOCAL_TAGS]); - memset(&context->status, 0, sizeof(context->status)); - } - // Walk over the additional tags and, for those that aren't invalid, modify - // the context to add/replace/delete as required. - for (int i = 0; i < ntags; i++) { - const census_tag *tag = &tags[i]; - size_t key_len = validate_tag(tag->key); - // ignore the tag if it is invalid or too short. - if (key_len <= 1) { - context->status.n_invalid_tags++; - } else { - if (tag->value != NULL) { - size_t value_len = validate_tag(tag->value); - if (value_len != 0) { - context_modify_tag(context, tag, key_len, value_len); - } else { - context->status.n_invalid_tags++; - } - } else { - if (context_delete_tag(context, tag, key_len)) { - context->status.n_deleted_tags++; - } - } - } - } - // Remove any deleted tags, update status if needed, and return. - tag_set_flatten(&context->tags[PROPAGATED_TAGS]); - tag_set_flatten(&context->tags[LOCAL_TAGS]); - context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; - context->status.n_local_tags = context->tags[LOCAL_TAGS].ntags; - if (status) { - *status = &context->status; - } - return context; -} - -const census_context_status *census_context_get_status( - const census_context *context) { - return &context->status; -} - -void census_context_destroy(census_context *context) { - gpr_free(context->tags[PROPAGATED_TAGS].kvm); - gpr_free(context->tags[LOCAL_TAGS].kvm); - gpr_free(context); -} - -void census_context_initialize_iterator(const census_context *context, - census_context_iterator *iterator) { - iterator->context = context; - iterator->index = 0; - if (context->tags[PROPAGATED_TAGS].ntags != 0) { - iterator->base = PROPAGATED_TAGS; - iterator->kvm = context->tags[PROPAGATED_TAGS].kvm; - } else if (context->tags[LOCAL_TAGS].ntags != 0) { - iterator->base = LOCAL_TAGS; - iterator->kvm = context->tags[LOCAL_TAGS].kvm; - } else { - iterator->base = -1; - } -} - -int census_context_next_tag(census_context_iterator *iterator, - census_tag *tag) { - if (iterator->base < 0) { - return 0; - } - struct raw_tag raw; - iterator->kvm = decode_tag(&raw, iterator->kvm, 0); - tag->key = raw.key; - tag->value = raw.value; - tag->flags = raw.flags; - if (++iterator->index == iterator->context->tags[iterator->base].ntags) { - do { - if (iterator->base == LOCAL_TAGS) { - iterator->base = -1; - return 1; - } - } while (iterator->context->tags[++iterator->base].ntags == 0); - iterator->index = 0; - iterator->kvm = iterator->context->tags[iterator->base].kvm; - } - return 1; -} - -// Find a tag in a tag_set by key. Return true if found, false otherwise. -static bool tag_set_get_tag(const struct tag_set *tags, const char *key, - size_t key_len, census_tag *tag) { - char *kvp = tags->kvm; - for (int i = 0; i < tags->ntags; i++) { - struct raw_tag raw; - kvp = decode_tag(&raw, kvp, 0); - if (key_len == raw.key_len && memcmp(raw.key, key, key_len) == 0) { - tag->key = raw.key; - tag->value = raw.value; - tag->flags = raw.flags; - return true; - } - } - return false; -} - -int census_context_get_tag(const census_context *context, const char *key, - census_tag *tag) { - size_t key_len = strlen(key) + 1; - if (key_len == 1) { - return 0; - } - if (tag_set_get_tag(&context->tags[PROPAGATED_TAGS], key, key_len, tag) || - tag_set_get_tag(&context->tags[LOCAL_TAGS], key, key_len, tag)) { - return 1; - } - return 0; -} - -// Context encoding and decoding functions. -// -// Wire format for tag_set's on the wire: -// -// First, a tag set header: -// -// offset bytes description -// 0 1 version number -// 1 1 number of bytes in this header. This allows for future -// expansion. -// 2 1 number of bytes in each tag header. -// 3 1 ntags value from tag set. -// -// This is followed by the key/value memory from struct tag_set. - -#define ENCODED_VERSION 0 // Version number -#define ENCODED_HEADER_SIZE 4 // size of tag set header - -// Encode a tag set. Returns 0 if buffer is too small. -static size_t tag_set_encode(const struct tag_set *tags, char *buffer, - size_t buf_size) { - if (buf_size < ENCODED_HEADER_SIZE + tags->kvm_used) { - return 0; - } - buf_size -= ENCODED_HEADER_SIZE; - *buffer++ = (char)ENCODED_VERSION; - *buffer++ = (char)ENCODED_HEADER_SIZE; - *buffer++ = (char)TAG_HEADER_SIZE; - *buffer++ = (char)tags->ntags; - if (tags->ntags == 0) { - return ENCODED_HEADER_SIZE; - } - memcpy(buffer, tags->kvm, tags->kvm_used); - return ENCODED_HEADER_SIZE + tags->kvm_used; -} - -size_t census_context_encode(const census_context *context, char *buffer, - size_t buf_size) { - return tag_set_encode(&context->tags[PROPAGATED_TAGS], buffer, buf_size); -} - -// Decode a tag set. -static void tag_set_decode(struct tag_set *tags, const char *buffer, - size_t size) { - uint8_t version = (uint8_t)(*buffer++); - uint8_t header_size = (uint8_t)(*buffer++); - uint8_t tag_header_size = (uint8_t)(*buffer++); - tags->ntags = tags->ntags_alloc = (int)(*buffer++); - if (tags->ntags == 0) { - tags->ntags_alloc = 0; - tags->kvm_size = 0; - tags->kvm_used = 0; - tags->kvm = NULL; - return; - } - if (header_size != ENCODED_HEADER_SIZE) { - GPR_ASSERT(version != ENCODED_VERSION); - GPR_ASSERT(ENCODED_HEADER_SIZE < header_size); - buffer += (header_size - ENCODED_HEADER_SIZE); - } - tags->kvm_used = size - header_size; - tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN; - tags->kvm = (char *)gpr_malloc(tags->kvm_size); - if (tag_header_size != TAG_HEADER_SIZE) { - // something new in the tag information. I don't understand it, so - // don't copy it over. - GPR_ASSERT(version != ENCODED_VERSION); - GPR_ASSERT(tag_header_size > TAG_HEADER_SIZE); - char *kvp = tags->kvm; - for (int i = 0; i < tags->ntags; i++) { - memcpy(kvp, buffer, TAG_HEADER_SIZE); - kvp += header_size; - struct raw_tag raw; - buffer = - decode_tag(&raw, (char *)buffer, tag_header_size - TAG_HEADER_SIZE); - memcpy(kvp, raw.key, (size_t)raw.key_len + raw.value_len); - kvp += raw.key_len + raw.value_len; - } - } else { - memcpy(tags->kvm, buffer, tags->kvm_used); - } -} - -census_context *census_context_decode(const char *buffer, size_t size) { - census_context *context = - (census_context *)gpr_malloc(sizeof(census_context)); - memset(&context->tags[LOCAL_TAGS], 0, sizeof(struct tag_set)); - if (buffer == NULL) { - memset(&context->tags[PROPAGATED_TAGS], 0, sizeof(struct tag_set)); - } else { - tag_set_decode(&context->tags[PROPAGATED_TAGS], buffer, size); - } - memset(&context->status, 0, sizeof(context->status)); - context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; - return context; -} diff --git a/src/core/ext/census/context.cc b/src/core/ext/census/context.cc new file mode 100644 index 0000000000..9b25a32e36 --- /dev/null +++ b/src/core/ext/census/context.cc @@ -0,0 +1,496 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/support/string.h" + +// Functions in this file support the public context API, including +// encoding/decoding as part of context propagation across RPC's. The overall +// requirements (in approximate priority order) for the +// context representation: +// 1. Efficient conversion to/from wire format +// 2. Minimal bytes used on-wire +// 3. Efficient context creation +// 4. Efficient lookup of tag value for a key +// 5. Efficient iteration over tags +// 6. Minimal memory footprint +// +// Notes on tradeoffs/decisions: +// * tag includes 1 byte length of key, as well as nil-terminating byte. These +// are to aid in efficient parsing and the ability to directly return key +// strings. This is more important than saving a single byte/tag on the wire. +// * The wire encoding uses only single byte values. This eliminates the need +// to handle endian-ness conversions. It also means there is a hard upper +// limit of 255 for both CENSUS_MAX_TAG_KV_LEN and CENSUS_MAX_PROPAGATED_TAGS. +// * Keep all tag information (keys/values/flags) in a single memory buffer, +// that can be directly copied to the wire. + +// min and max valid chars in tag keys and values. All printable ASCII is OK. +#define MIN_VALID_TAG_CHAR 32 // ' ' +#define MAX_VALID_TAG_CHAR 126 // '~' + +// Structure representing a set of tags. Essentially a count of number of tags +// present, and pointer to a chunk of memory that contains the per-tag details. +struct tag_set { + int ntags; // number of tags. + int ntags_alloc; // ntags + number of deleted tags (total number of tags + // in all of kvm). This will always be == ntags, except during the process + // of building a new tag set. + size_t kvm_size; // number of bytes allocated for key/value storage. + size_t kvm_used; // number of bytes of used key/value memory + char *kvm; // key/value memory. Consists of repeated entries of: + // Offset Size Description + // 0 1 Key length, including trailing 0. (K) + // 1 1 Value length, including trailing 0 (V) + // 2 1 Flags + // 3 K Key bytes + // 3 + K V Value bytes + // + // We refer to the first 3 entries as the 'tag header'. If extra values are + // introduced in the header, you will need to modify the TAG_HEADER_SIZE + // constant, the raw_tag structure (and everything that uses it) and the + // encode/decode functions appropriately. +}; + +// Number of bytes in tag header. +#define TAG_HEADER_SIZE 3 // key length (1) + value length (1) + flags (1) +// Offsets to tag header entries. +#define KEY_LEN_OFFSET 0 +#define VALUE_LEN_OFFSET 1 +#define FLAG_OFFSET 2 + +// raw_tag represents the raw-storage form of a tag in the kvm of a tag_set. +struct raw_tag { + uint8_t key_len; + uint8_t value_len; + uint8_t flags; + char *key; + char *value; +}; + +// Use a reserved flag bit for indication of deleted tag. +#define CENSUS_TAG_DELETED CENSUS_TAG_RESERVED +#define CENSUS_TAG_IS_DELETED(flags) (flags & CENSUS_TAG_DELETED) + +// Primary representation of a context. Composed of 2 underlying tag_set +// structs, one each for propagated and local (non-propagated) tags. This is +// to efficiently support tag encoding/decoding. +// TODO(aveitch): need to add tracing id's/structure. +struct census_context { + struct tag_set tags[2]; + census_context_status status; +}; + +// Indices into the tags member of census_context +#define PROPAGATED_TAGS 0 +#define LOCAL_TAGS 1 + +// Validate (check all characters are in range and size is less than limit) a +// key or value string. Returns 0 if the string is invalid, or the length +// (including terminator) if valid. +static size_t validate_tag(const char *kv) { + size_t len = 1; + char ch; + while ((ch = *kv++) != 0) { + if (ch < MIN_VALID_TAG_CHAR || ch > MAX_VALID_TAG_CHAR) { + return 0; + } + len++; + } + if (len > CENSUS_MAX_TAG_KV_LEN) { + return 0; + } + return len; +} + +// Extract a raw tag given a pointer (raw) to the tag header. Allow for some +// extra bytes in the tag header (see encode/decode functions for usage: this +// allows for future expansion of the tag header). +static char *decode_tag(struct raw_tag *tag, char *header, int offset) { + tag->key_len = (uint8_t)(*header++); + tag->value_len = (uint8_t)(*header++); + tag->flags = (uint8_t)(*header++); + header += offset; + tag->key = header; + header += tag->key_len; + tag->value = header; + return header + tag->value_len; +} + +// Make a copy (in 'to') of an existing tag_set. +static void tag_set_copy(struct tag_set *to, const struct tag_set *from) { + memcpy(to, from, sizeof(struct tag_set)); + to->kvm = (char *)gpr_malloc(to->kvm_size); + memcpy(to->kvm, from->kvm, from->kvm_used); +} + +// Delete a tag from a tag_set, if it exists (returns true if it did). +static bool tag_set_delete_tag(struct tag_set *tags, const char *key, + size_t key_len) { + char *kvp = tags->kvm; + for (int i = 0; i < tags->ntags_alloc; i++) { + uint8_t *flags = (uint8_t *)(kvp + FLAG_OFFSET); + struct raw_tag tag; + kvp = decode_tag(&tag, kvp, 0); + if (CENSUS_TAG_IS_DELETED(tag.flags)) continue; + if ((key_len == tag.key_len) && (memcmp(key, tag.key, key_len) == 0)) { + *flags |= CENSUS_TAG_DELETED; + tags->ntags--; + return true; + } + } + return false; +} + +// Delete a tag from a context, return true if it existed. +static bool context_delete_tag(census_context *context, const census_tag *tag, + size_t key_len) { + return ( + tag_set_delete_tag(&context->tags[LOCAL_TAGS], tag->key, key_len) || + tag_set_delete_tag(&context->tags[PROPAGATED_TAGS], tag->key, key_len)); +} + +// Add a tag to a tag_set. Return true on success, false if the tag could +// not be added because of constraints on tag set size. This function should +// not be called if the tag may already exist (in a non-deleted state) in +// the tag_set, as that would result in two tags with the same key. +static bool tag_set_add_tag(struct tag_set *tags, const census_tag *tag, + size_t key_len, size_t value_len) { + if (tags->ntags == CENSUS_MAX_PROPAGATED_TAGS) { + return false; + } + const size_t tag_size = key_len + value_len + TAG_HEADER_SIZE; + if (tags->kvm_used + tag_size > tags->kvm_size) { + // allocate new memory if needed + tags->kvm_size += 2 * CENSUS_MAX_TAG_KV_LEN + TAG_HEADER_SIZE; + char *new_kvm = (char *)gpr_malloc(tags->kvm_size); + if (tags->kvm_used > 0) memcpy(new_kvm, tags->kvm, tags->kvm_used); + gpr_free(tags->kvm); + tags->kvm = new_kvm; + } + char *kvp = tags->kvm + tags->kvm_used; + *kvp++ = (char)key_len; + *kvp++ = (char)value_len; + // ensure reserved flags are not used. + *kvp++ = (char)(tag->flags & (CENSUS_TAG_PROPAGATE | CENSUS_TAG_STATS)); + memcpy(kvp, tag->key, key_len); + kvp += key_len; + memcpy(kvp, tag->value, value_len); + tags->kvm_used += tag_size; + tags->ntags++; + tags->ntags_alloc++; + return true; +} + +// Add/modify/delete a tag to/in a context. Caller must validate that tag key +// etc. are valid. +static void context_modify_tag(census_context *context, const census_tag *tag, + size_t key_len, size_t value_len) { + // First delete the tag if it is already present. + bool deleted = context_delete_tag(context, tag, key_len); + bool added = false; + if (CENSUS_TAG_IS_PROPAGATED(tag->flags)) { + added = tag_set_add_tag(&context->tags[PROPAGATED_TAGS], tag, key_len, + value_len); + } else { + added = + tag_set_add_tag(&context->tags[LOCAL_TAGS], tag, key_len, value_len); + } + + if (deleted) { + context->status.n_modified_tags++; + } else { + if (added) { + context->status.n_added_tags++; + } else { + context->status.n_ignored_tags++; + } + } +} + +// Remove memory used for deleted tags from a tag set. Basic algorithm: +// 1) Walk through tag set to find first deleted tag. Record where it is. +// 2) Find the next not-deleted tag. Copy all of kvm from there to the end +// "over" the deleted tags +// 3) repeat #1 and #2 until we have seen all tags +// 4) if we are still looking for a not-deleted tag, then all the end portion +// of the kvm is deleted. Just reduce the used amount of memory by the +// appropriate amount. +static void tag_set_flatten(struct tag_set *tags) { + if (tags->ntags == tags->ntags_alloc) return; + bool found_deleted = false; // found a deleted tag. + char *kvp = tags->kvm; + char *dbase = NULL; // record location of deleted tag + for (int i = 0; i < tags->ntags_alloc; i++) { + struct raw_tag tag; + char *next_kvp = decode_tag(&tag, kvp, 0); + if (found_deleted) { + if (!CENSUS_TAG_IS_DELETED(tag.flags)) { + ptrdiff_t reduce = kvp - dbase; // #bytes in deleted tags + GPR_ASSERT(reduce > 0); + ptrdiff_t copy_size = tags->kvm + tags->kvm_used - kvp; + GPR_ASSERT(copy_size > 0); + memmove(dbase, kvp, (size_t)copy_size); + tags->kvm_used -= (size_t)reduce; + next_kvp -= reduce; + found_deleted = false; + } + } else { + if (CENSUS_TAG_IS_DELETED(tag.flags)) { + dbase = kvp; + found_deleted = true; + } + } + kvp = next_kvp; + } + if (found_deleted) { + GPR_ASSERT(dbase > tags->kvm); + tags->kvm_used = (size_t)(dbase - tags->kvm); + } + tags->ntags_alloc = tags->ntags; +} + +census_context *census_context_create(const census_context *base, + const census_tag *tags, int ntags, + census_context_status const **status) { + census_context *context = + (census_context *)gpr_malloc(sizeof(census_context)); + // If we are given a base, copy it into our new tag set. Otherwise set it + // to zero/NULL everything. + if (base == NULL) { + memset(context, 0, sizeof(census_context)); + } else { + tag_set_copy(&context->tags[PROPAGATED_TAGS], &base->tags[PROPAGATED_TAGS]); + tag_set_copy(&context->tags[LOCAL_TAGS], &base->tags[LOCAL_TAGS]); + memset(&context->status, 0, sizeof(context->status)); + } + // Walk over the additional tags and, for those that aren't invalid, modify + // the context to add/replace/delete as required. + for (int i = 0; i < ntags; i++) { + const census_tag *tag = &tags[i]; + size_t key_len = validate_tag(tag->key); + // ignore the tag if it is invalid or too short. + if (key_len <= 1) { + context->status.n_invalid_tags++; + } else { + if (tag->value != NULL) { + size_t value_len = validate_tag(tag->value); + if (value_len != 0) { + context_modify_tag(context, tag, key_len, value_len); + } else { + context->status.n_invalid_tags++; + } + } else { + if (context_delete_tag(context, tag, key_len)) { + context->status.n_deleted_tags++; + } + } + } + } + // Remove any deleted tags, update status if needed, and return. + tag_set_flatten(&context->tags[PROPAGATED_TAGS]); + tag_set_flatten(&context->tags[LOCAL_TAGS]); + context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; + context->status.n_local_tags = context->tags[LOCAL_TAGS].ntags; + if (status) { + *status = &context->status; + } + return context; +} + +const census_context_status *census_context_get_status( + const census_context *context) { + return &context->status; +} + +void census_context_destroy(census_context *context) { + gpr_free(context->tags[PROPAGATED_TAGS].kvm); + gpr_free(context->tags[LOCAL_TAGS].kvm); + gpr_free(context); +} + +void census_context_initialize_iterator(const census_context *context, + census_context_iterator *iterator) { + iterator->context = context; + iterator->index = 0; + if (context->tags[PROPAGATED_TAGS].ntags != 0) { + iterator->base = PROPAGATED_TAGS; + iterator->kvm = context->tags[PROPAGATED_TAGS].kvm; + } else if (context->tags[LOCAL_TAGS].ntags != 0) { + iterator->base = LOCAL_TAGS; + iterator->kvm = context->tags[LOCAL_TAGS].kvm; + } else { + iterator->base = -1; + } +} + +int census_context_next_tag(census_context_iterator *iterator, + census_tag *tag) { + if (iterator->base < 0) { + return 0; + } + struct raw_tag raw; + iterator->kvm = decode_tag(&raw, iterator->kvm, 0); + tag->key = raw.key; + tag->value = raw.value; + tag->flags = raw.flags; + if (++iterator->index == iterator->context->tags[iterator->base].ntags) { + do { + if (iterator->base == LOCAL_TAGS) { + iterator->base = -1; + return 1; + } + } while (iterator->context->tags[++iterator->base].ntags == 0); + iterator->index = 0; + iterator->kvm = iterator->context->tags[iterator->base].kvm; + } + return 1; +} + +// Find a tag in a tag_set by key. Return true if found, false otherwise. +static bool tag_set_get_tag(const struct tag_set *tags, const char *key, + size_t key_len, census_tag *tag) { + char *kvp = tags->kvm; + for (int i = 0; i < tags->ntags; i++) { + struct raw_tag raw; + kvp = decode_tag(&raw, kvp, 0); + if (key_len == raw.key_len && memcmp(raw.key, key, key_len) == 0) { + tag->key = raw.key; + tag->value = raw.value; + tag->flags = raw.flags; + return true; + } + } + return false; +} + +int census_context_get_tag(const census_context *context, const char *key, + census_tag *tag) { + size_t key_len = strlen(key) + 1; + if (key_len == 1) { + return 0; + } + if (tag_set_get_tag(&context->tags[PROPAGATED_TAGS], key, key_len, tag) || + tag_set_get_tag(&context->tags[LOCAL_TAGS], key, key_len, tag)) { + return 1; + } + return 0; +} + +// Context encoding and decoding functions. +// +// Wire format for tag_set's on the wire: +// +// First, a tag set header: +// +// offset bytes description +// 0 1 version number +// 1 1 number of bytes in this header. This allows for future +// expansion. +// 2 1 number of bytes in each tag header. +// 3 1 ntags value from tag set. +// +// This is followed by the key/value memory from struct tag_set. + +#define ENCODED_VERSION 0 // Version number +#define ENCODED_HEADER_SIZE 4 // size of tag set header + +// Encode a tag set. Returns 0 if buffer is too small. +static size_t tag_set_encode(const struct tag_set *tags, char *buffer, + size_t buf_size) { + if (buf_size < ENCODED_HEADER_SIZE + tags->kvm_used) { + return 0; + } + buf_size -= ENCODED_HEADER_SIZE; + *buffer++ = (char)ENCODED_VERSION; + *buffer++ = (char)ENCODED_HEADER_SIZE; + *buffer++ = (char)TAG_HEADER_SIZE; + *buffer++ = (char)tags->ntags; + if (tags->ntags == 0) { + return ENCODED_HEADER_SIZE; + } + memcpy(buffer, tags->kvm, tags->kvm_used); + return ENCODED_HEADER_SIZE + tags->kvm_used; +} + +size_t census_context_encode(const census_context *context, char *buffer, + size_t buf_size) { + return tag_set_encode(&context->tags[PROPAGATED_TAGS], buffer, buf_size); +} + +// Decode a tag set. +static void tag_set_decode(struct tag_set *tags, const char *buffer, + size_t size) { + uint8_t version = (uint8_t)(*buffer++); + uint8_t header_size = (uint8_t)(*buffer++); + uint8_t tag_header_size = (uint8_t)(*buffer++); + tags->ntags = tags->ntags_alloc = (int)(*buffer++); + if (tags->ntags == 0) { + tags->ntags_alloc = 0; + tags->kvm_size = 0; + tags->kvm_used = 0; + tags->kvm = NULL; + return; + } + if (header_size != ENCODED_HEADER_SIZE) { + GPR_ASSERT(version != ENCODED_VERSION); + GPR_ASSERT(ENCODED_HEADER_SIZE < header_size); + buffer += (header_size - ENCODED_HEADER_SIZE); + } + tags->kvm_used = size - header_size; + tags->kvm_size = tags->kvm_used + CENSUS_MAX_TAG_KV_LEN; + tags->kvm = (char *)gpr_malloc(tags->kvm_size); + if (tag_header_size != TAG_HEADER_SIZE) { + // something new in the tag information. I don't understand it, so + // don't copy it over. + GPR_ASSERT(version != ENCODED_VERSION); + GPR_ASSERT(tag_header_size > TAG_HEADER_SIZE); + char *kvp = tags->kvm; + for (int i = 0; i < tags->ntags; i++) { + memcpy(kvp, buffer, TAG_HEADER_SIZE); + kvp += header_size; + struct raw_tag raw; + buffer = + decode_tag(&raw, (char *)buffer, tag_header_size - TAG_HEADER_SIZE); + memcpy(kvp, raw.key, (size_t)raw.key_len + raw.value_len); + kvp += raw.key_len + raw.value_len; + } + } else { + memcpy(tags->kvm, buffer, tags->kvm_used); + } +} + +census_context *census_context_decode(const char *buffer, size_t size) { + census_context *context = + (census_context *)gpr_malloc(sizeof(census_context)); + memset(&context->tags[LOCAL_TAGS], 0, sizeof(struct tag_set)); + if (buffer == NULL) { + memset(&context->tags[PROPAGATED_TAGS], 0, sizeof(struct tag_set)); + } else { + tag_set_decode(&context->tags[PROPAGATED_TAGS], buffer, size); + } + memset(&context->status, 0, sizeof(context->status)); + context->status.n_propagated_tags = context->tags[PROPAGATED_TAGS].ntags; + return context; +} diff --git a/src/core/ext/census/gen/census.pb.c b/src/core/ext/census/gen/census.pb.c deleted file mode 100644 index 88efa73661..0000000000 --- a/src/core/ext/census/gen/census.pb.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.5-dev */ - -#include "src/core/ext/census/gen/census.pb.h" - -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - - - -const pb_field_t google_census_Duration_fields[3] = { - PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_census_Duration, seconds, seconds, 0), - PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, google_census_Duration, nanos, seconds, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Timestamp_fields[3] = { - PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_census_Timestamp, seconds, seconds, 0), - PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, google_census_Timestamp, nanos, seconds, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Resource_fields[4] = { - PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_Resource, name, name, 0), - PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, google_census_Resource, description, name, 0), - PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Resource, unit, description, &google_census_Resource_MeasurementUnit_fields), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Resource_MeasurementUnit_fields[4] = { - PB_FIELD( 1, INT32 , OPTIONAL, STATIC , FIRST, google_census_Resource_MeasurementUnit, prefix, prefix, 0), - PB_FIELD( 2, UENUM , REPEATED, CALLBACK, OTHER, google_census_Resource_MeasurementUnit, numerator, prefix, 0), - PB_FIELD( 3, UENUM , REPEATED, CALLBACK, OTHER, google_census_Resource_MeasurementUnit, denominator, numerator, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_AggregationDescriptor_fields[4] = { - PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, google_census_AggregationDescriptor, type, type, 0), - PB_ONEOF_FIELD(options, 2, MESSAGE , ONEOF, STATIC , OTHER, google_census_AggregationDescriptor, bucket_boundaries, type, &google_census_AggregationDescriptor_BucketBoundaries_fields), - PB_ONEOF_FIELD(options, 3, MESSAGE , ONEOF, STATIC , OTHER, google_census_AggregationDescriptor, interval_boundaries, type, &google_census_AggregationDescriptor_IntervalBoundaries_fields), - PB_LAST_FIELD -}; - -const pb_field_t google_census_AggregationDescriptor_BucketBoundaries_fields[2] = { - PB_FIELD( 1, DOUBLE , REPEATED, CALLBACK, FIRST, google_census_AggregationDescriptor_BucketBoundaries, bounds, bounds, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_AggregationDescriptor_IntervalBoundaries_fields[2] = { - PB_FIELD( 1, DOUBLE , REPEATED, CALLBACK, FIRST, google_census_AggregationDescriptor_IntervalBoundaries, window_size, window_size, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Distribution_fields[5] = { - PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_census_Distribution, count, count, 0), - PB_FIELD( 2, DOUBLE , OPTIONAL, STATIC , OTHER, google_census_Distribution, mean, count, 0), - PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Distribution, range, mean, &google_census_Distribution_Range_fields), - PB_FIELD( 4, INT64 , REPEATED, CALLBACK, OTHER, google_census_Distribution, bucket_count, range, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Distribution_Range_fields[3] = { - PB_FIELD( 1, DOUBLE , OPTIONAL, STATIC , FIRST, google_census_Distribution_Range, min, min, 0), - PB_FIELD( 2, DOUBLE , OPTIONAL, STATIC , OTHER, google_census_Distribution_Range, max, min, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_IntervalStats_fields[2] = { - PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_census_IntervalStats, window, window, &google_census_IntervalStats_Window_fields), - PB_LAST_FIELD -}; - -const pb_field_t google_census_IntervalStats_Window_fields[4] = { - PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, google_census_IntervalStats_Window, window_size, window_size, &google_census_Duration_fields), - PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, google_census_IntervalStats_Window, count, window_size, 0), - PB_FIELD( 3, DOUBLE , OPTIONAL, STATIC , OTHER, google_census_IntervalStats_Window, mean, count, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Tag_fields[3] = { - PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, google_census_Tag, key, key, 0), - PB_FIELD( 2, STRING , OPTIONAL, STATIC , OTHER, google_census_Tag, value, key, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_View_fields[6] = { - PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_View, name, name, 0), - PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, google_census_View, description, name, 0), - PB_FIELD( 3, STRING , OPTIONAL, CALLBACK, OTHER, google_census_View, resource_name, description, 0), - PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_View, aggregation, resource_name, &google_census_AggregationDescriptor_fields), - PB_FIELD( 5, STRING , REPEATED, CALLBACK, OTHER, google_census_View, tag_key, aggregation, 0), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Aggregation_fields[7] = { - PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_Aggregation, name, name, 0), - PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, google_census_Aggregation, description, name, 0), - PB_ONEOF_FIELD(data, 3, UINT64 , ONEOF, STATIC , OTHER, google_census_Aggregation, count, description, 0), - PB_ONEOF_FIELD(data, 4, MESSAGE , ONEOF, STATIC , OTHER, google_census_Aggregation, distribution, description, &google_census_Distribution_fields), - PB_ONEOF_FIELD(data, 5, MESSAGE , ONEOF, STATIC , OTHER, google_census_Aggregation, interval_stats, description, &google_census_IntervalStats_fields), - PB_FIELD( 6, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Aggregation, tag, data.interval_stats, &google_census_Tag_fields), - PB_LAST_FIELD -}; - -const pb_field_t google_census_Metric_fields[5] = { - PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_Metric, view_name, view_name, 0), - PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Metric, aggregation, view_name, &google_census_Aggregation_fields), - PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Metric, start, aggregation, &google_census_Timestamp_fields), - PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Metric, end, start, &google_census_Timestamp_fields), - PB_LAST_FIELD -}; - - -/* Check that field information fits in pb_field_t */ -#if !defined(PB_FIELD_32BIT) -/* If you get an error here, it means that you need to define PB_FIELD_32BIT - * compile-time option. You can do that in pb.h or on compiler command line. - * - * The reason you need to do this is that some of your messages contain tag - * numbers or field sizes that are larger than what can fit in 8 or 16 bit - * field descriptors. - */ -PB_STATIC_ASSERT((pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Aggregation, tag) < 65536 && pb_membersize(google_census_Metric, aggregation) < 65536 && pb_membersize(google_census_Metric, start) < 65536 && pb_membersize(google_census_Metric, end) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Resource_google_census_Resource_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_Metric) -#endif - -#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) -/* If you get an error here, it means that you need to define PB_FIELD_16BIT - * compile-time option. You can do that in pb.h or on compiler command line. - * - * The reason you need to do this is that some of your messages contain tag - * numbers or field sizes that are larger than what can fit in the default - * 8 bit descriptors. - */ -PB_STATIC_ASSERT((pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Aggregation, tag) < 256 && pb_membersize(google_census_Metric, aggregation) < 256 && pb_membersize(google_census_Metric, start) < 256 && pb_membersize(google_census_Metric, end) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Resource_google_census_Resource_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_Metric) -#endif - - -/* On some platforms (such as AVR), double is really float. - * These are not directly supported by nanopb, but see example_avr_double. - * To get rid of this error, remove any double fields from your .proto. - */ -PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) - diff --git a/src/core/ext/census/gen/census.pb.cc b/src/core/ext/census/gen/census.pb.cc new file mode 100644 index 0000000000..88efa73661 --- /dev/null +++ b/src/core/ext/census/gen/census.pb.cc @@ -0,0 +1,161 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.5-dev */ + +#include "src/core/ext/census/gen/census.pb.h" + +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_census_Duration_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_census_Duration, seconds, seconds, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, google_census_Duration, nanos, seconds, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Timestamp_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_census_Timestamp, seconds, seconds, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, google_census_Timestamp, nanos, seconds, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Resource_fields[4] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_Resource, name, name, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, google_census_Resource, description, name, 0), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Resource, unit, description, &google_census_Resource_MeasurementUnit_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Resource_MeasurementUnit_fields[4] = { + PB_FIELD( 1, INT32 , OPTIONAL, STATIC , FIRST, google_census_Resource_MeasurementUnit, prefix, prefix, 0), + PB_FIELD( 2, UENUM , REPEATED, CALLBACK, OTHER, google_census_Resource_MeasurementUnit, numerator, prefix, 0), + PB_FIELD( 3, UENUM , REPEATED, CALLBACK, OTHER, google_census_Resource_MeasurementUnit, denominator, numerator, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_AggregationDescriptor_fields[4] = { + PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, google_census_AggregationDescriptor, type, type, 0), + PB_ONEOF_FIELD(options, 2, MESSAGE , ONEOF, STATIC , OTHER, google_census_AggregationDescriptor, bucket_boundaries, type, &google_census_AggregationDescriptor_BucketBoundaries_fields), + PB_ONEOF_FIELD(options, 3, MESSAGE , ONEOF, STATIC , OTHER, google_census_AggregationDescriptor, interval_boundaries, type, &google_census_AggregationDescriptor_IntervalBoundaries_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_census_AggregationDescriptor_BucketBoundaries_fields[2] = { + PB_FIELD( 1, DOUBLE , REPEATED, CALLBACK, FIRST, google_census_AggregationDescriptor_BucketBoundaries, bounds, bounds, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_AggregationDescriptor_IntervalBoundaries_fields[2] = { + PB_FIELD( 1, DOUBLE , REPEATED, CALLBACK, FIRST, google_census_AggregationDescriptor_IntervalBoundaries, window_size, window_size, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Distribution_fields[5] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, google_census_Distribution, count, count, 0), + PB_FIELD( 2, DOUBLE , OPTIONAL, STATIC , OTHER, google_census_Distribution, mean, count, 0), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Distribution, range, mean, &google_census_Distribution_Range_fields), + PB_FIELD( 4, INT64 , REPEATED, CALLBACK, OTHER, google_census_Distribution, bucket_count, range, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Distribution_Range_fields[3] = { + PB_FIELD( 1, DOUBLE , OPTIONAL, STATIC , FIRST, google_census_Distribution_Range, min, min, 0), + PB_FIELD( 2, DOUBLE , OPTIONAL, STATIC , OTHER, google_census_Distribution_Range, max, min, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_IntervalStats_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, google_census_IntervalStats, window, window, &google_census_IntervalStats_Window_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_census_IntervalStats_Window_fields[4] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, google_census_IntervalStats_Window, window_size, window_size, &google_census_Duration_fields), + PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, google_census_IntervalStats_Window, count, window_size, 0), + PB_FIELD( 3, DOUBLE , OPTIONAL, STATIC , OTHER, google_census_IntervalStats_Window, mean, count, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Tag_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, google_census_Tag, key, key, 0), + PB_FIELD( 2, STRING , OPTIONAL, STATIC , OTHER, google_census_Tag, value, key, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_View_fields[6] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_View, name, name, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, google_census_View, description, name, 0), + PB_FIELD( 3, STRING , OPTIONAL, CALLBACK, OTHER, google_census_View, resource_name, description, 0), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_View, aggregation, resource_name, &google_census_AggregationDescriptor_fields), + PB_FIELD( 5, STRING , REPEATED, CALLBACK, OTHER, google_census_View, tag_key, aggregation, 0), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Aggregation_fields[7] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_Aggregation, name, name, 0), + PB_FIELD( 2, STRING , OPTIONAL, CALLBACK, OTHER, google_census_Aggregation, description, name, 0), + PB_ONEOF_FIELD(data, 3, UINT64 , ONEOF, STATIC , OTHER, google_census_Aggregation, count, description, 0), + PB_ONEOF_FIELD(data, 4, MESSAGE , ONEOF, STATIC , OTHER, google_census_Aggregation, distribution, description, &google_census_Distribution_fields), + PB_ONEOF_FIELD(data, 5, MESSAGE , ONEOF, STATIC , OTHER, google_census_Aggregation, interval_stats, description, &google_census_IntervalStats_fields), + PB_FIELD( 6, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Aggregation, tag, data.interval_stats, &google_census_Tag_fields), + PB_LAST_FIELD +}; + +const pb_field_t google_census_Metric_fields[5] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, google_census_Metric, view_name, view_name, 0), + PB_FIELD( 2, MESSAGE , REPEATED, CALLBACK, OTHER, google_census_Metric, aggregation, view_name, &google_census_Aggregation_fields), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Metric, start, aggregation, &google_census_Timestamp_fields), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, google_census_Metric, end, start, &google_census_Timestamp_fields), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Resource, unit) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 65536 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 65536 && pb_membersize(google_census_Distribution, range) < 65536 && pb_membersize(google_census_IntervalStats, window) < 65536 && pb_membersize(google_census_IntervalStats_Window, window_size) < 65536 && pb_membersize(google_census_View, aggregation) < 65536 && pb_membersize(google_census_Aggregation, data.distribution) < 65536 && pb_membersize(google_census_Aggregation, data.interval_stats) < 65536 && pb_membersize(google_census_Aggregation, tag) < 65536 && pb_membersize(google_census_Metric, aggregation) < 65536 && pb_membersize(google_census_Metric, start) < 65536 && pb_membersize(google_census_Metric, end) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Resource_google_census_Resource_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_Metric) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Resource, unit) < 256 && pb_membersize(google_census_AggregationDescriptor, options.bucket_boundaries) < 256 && pb_membersize(google_census_AggregationDescriptor, options.interval_boundaries) < 256 && pb_membersize(google_census_Distribution, range) < 256 && pb_membersize(google_census_IntervalStats, window) < 256 && pb_membersize(google_census_IntervalStats_Window, window_size) < 256 && pb_membersize(google_census_View, aggregation) < 256 && pb_membersize(google_census_Aggregation, data.distribution) < 256 && pb_membersize(google_census_Aggregation, data.interval_stats) < 256 && pb_membersize(google_census_Aggregation, tag) < 256 && pb_membersize(google_census_Metric, aggregation) < 256 && pb_membersize(google_census_Metric, start) < 256 && pb_membersize(google_census_Metric, end) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_google_census_Duration_google_census_Timestamp_google_census_Resource_google_census_Resource_MeasurementUnit_google_census_AggregationDescriptor_google_census_AggregationDescriptor_BucketBoundaries_google_census_AggregationDescriptor_IntervalBoundaries_google_census_Distribution_google_census_Distribution_Range_google_census_IntervalStats_google_census_IntervalStats_Window_google_census_Tag_google_census_View_google_census_Aggregation_google_census_Metric) +#endif + + +/* On some platforms (such as AVR), double is really float. + * These are not directly supported by nanopb, but see example_avr_double. + * To get rid of this error, remove any double fields from your .proto. + */ +PB_STATIC_ASSERT(sizeof(double) == 8, DOUBLE_MUST_BE_8_BYTES) + diff --git a/src/core/ext/census/gen/trace_context.pb.c b/src/core/ext/census/gen/trace_context.pb.c deleted file mode 100644 index b5c3d52a71..0000000000 --- a/src/core/ext/census/gen/trace_context.pb.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.7-dev at Fri Jan 20 16:14:22 2017. */ - -#include "src/core/ext/census/gen/trace_context.pb.h" - -/* @@protoc_insertion_point(includes) */ -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - - - -const pb_field_t google_trace_TraceContext_fields[5] = { - PB_FIELD( 1, FIXED64 , OPTIONAL, STATIC , FIRST, google_trace_TraceContext, trace_id_hi, trace_id_hi, 0), - PB_FIELD( 2, FIXED64 , OPTIONAL, STATIC , OTHER, google_trace_TraceContext, trace_id_lo, trace_id_hi, 0), - PB_FIELD( 3, FIXED64 , OPTIONAL, STATIC , OTHER, google_trace_TraceContext, span_id, trace_id_lo, 0), - PB_FIELD( 4, FIXED32 , OPTIONAL, STATIC , OTHER, google_trace_TraceContext, span_options, span_id, 0), - PB_LAST_FIELD -}; - - -/* @@protoc_insertion_point(eof) */ diff --git a/src/core/ext/census/gen/trace_context.pb.cc b/src/core/ext/census/gen/trace_context.pb.cc new file mode 100644 index 0000000000..b5c3d52a71 --- /dev/null +++ b/src/core/ext/census/gen/trace_context.pb.cc @@ -0,0 +1,39 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev at Fri Jan 20 16:14:22 2017. */ + +#include "src/core/ext/census/gen/trace_context.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t google_trace_TraceContext_fields[5] = { + PB_FIELD( 1, FIXED64 , OPTIONAL, STATIC , FIRST, google_trace_TraceContext, trace_id_hi, trace_id_hi, 0), + PB_FIELD( 2, FIXED64 , OPTIONAL, STATIC , OTHER, google_trace_TraceContext, trace_id_lo, trace_id_hi, 0), + PB_FIELD( 3, FIXED64 , OPTIONAL, STATIC , OTHER, google_trace_TraceContext, span_id, trace_id_lo, 0), + PB_FIELD( 4, FIXED32 , OPTIONAL, STATIC , OTHER, google_trace_TraceContext, span_options, span_id, 0), + PB_LAST_FIELD +}; + + +/* @@protoc_insertion_point(eof) */ diff --git a/src/core/ext/census/grpc_context.c b/src/core/ext/census/grpc_context.c deleted file mode 100644 index 0bfba63a5e..0000000000 --- a/src/core/ext/census/grpc_context.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" - -void grpc_census_call_set_context(grpc_call *call, census_context *context) { - GRPC_API_TRACE("grpc_census_call_set_context(call=%p, census_context=%p)", 2, - (call, context)); - if (census_enabled() == CENSUS_FEATURE_NONE) { - return; - } - if (context != NULL) { - grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, NULL); - } -} - -census_context *grpc_census_call_get_context(grpc_call *call) { - GRPC_API_TRACE("grpc_census_call_get_context(call=%p)", 1, (call)); - return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING); -} diff --git a/src/core/ext/census/grpc_context.cc b/src/core/ext/census/grpc_context.cc new file mode 100644 index 0000000000..0bfba63a5e --- /dev/null +++ b/src/core/ext/census/grpc_context.cc @@ -0,0 +1,38 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" + +void grpc_census_call_set_context(grpc_call *call, census_context *context) { + GRPC_API_TRACE("grpc_census_call_set_context(call=%p, census_context=%p)", 2, + (call, context)); + if (census_enabled() == CENSUS_FEATURE_NONE) { + return; + } + if (context != NULL) { + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, context, NULL); + } +} + +census_context *grpc_census_call_get_context(grpc_call *call) { + GRPC_API_TRACE("grpc_census_call_get_context(call=%p)", 1, (call)); + return (census_context *)grpc_call_context_get(call, GRPC_CONTEXT_TRACING); +} diff --git a/src/core/ext/census/grpc_filter.c b/src/core/ext/census/grpc_filter.c deleted file mode 100644 index b37ab90389..0000000000 --- a/src/core/ext/census/grpc_filter.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/grpc_filter.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/census/census_interface.h" -#include "src/core/ext/census/census_rpc_stats.h" -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/transport/static_metadata.h" - -typedef struct call_data { - census_op_id op_id; - census_context *ctxt; - gpr_timespec start_ts; - int error; - - /* recv callback */ - grpc_metadata_batch *recv_initial_metadata; - grpc_closure *on_done_recv; - grpc_closure finish_recv; -} call_data; - -typedef struct channel_data { uint8_t unused; } channel_data; - -static void extract_and_annotate_method_tag(grpc_metadata_batch *md, - call_data *calld, - channel_data *chand) { - grpc_linked_mdelem *m; - for (m = md->list.head; m != NULL; m = m->next) { - if (grpc_slice_eq(GRPC_MDKEY(m->md), GRPC_MDSTR_PATH)) { - /* Add method tag here */ - } - } -} - -static void client_mutate_op(grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (op->send_initial_metadata) { - extract_and_annotate_method_tag( - op->payload->send_initial_metadata.send_initial_metadata, calld, chand); - } -} - -static void client_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - client_mutate_op(elem, op); - grpc_call_next_op(exec_ctx, elem, op); -} - -static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr, - grpc_error *error) { - GPR_TIMER_BEGIN("census-server:server_on_done_recv", 0); - grpc_call_element *elem = (grpc_call_element *)ptr; - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (error == GRPC_ERROR_NONE) { - extract_and_annotate_method_tag(calld->recv_initial_metadata, calld, chand); - } - calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error); - GPR_TIMER_END("census-server:server_on_done_recv", 0); -} - -static void server_mutate_op(grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - call_data *calld = (call_data *)elem->call_data; - if (op->recv_initial_metadata) { - /* substitute our callback for the op callback */ - calld->recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata; - calld->on_done_recv = - op->payload->recv_initial_metadata.recv_initial_metadata_ready; - op->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->finish_recv; - } -} - -static void server_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - /* TODO(ctiller): this code fails. I don't know why. I expect it's - incomplete, and someone should look at it soon. - - call_data *calld = elem->call_data; - GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); */ - server_mutate_op(elem, op); - grpc_call_next_op(exec_ctx, elem, op); -} - -static grpc_error *client_init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *d = (call_data *)elem->call_data; - GPR_ASSERT(d != NULL); - memset(d, 0, sizeof(*d)); - d->start_ts = args->start_time; - return GRPC_ERROR_NONE; -} - -static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *d = (call_data *)elem->call_data; - GPR_ASSERT(d != NULL); - /* TODO(hongyu): record rpc client stats and census_rpc_end_op here */ -} - -static grpc_error *server_init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *d = (call_data *)elem->call_data; - GPR_ASSERT(d != NULL); - memset(d, 0, sizeof(*d)); - d->start_ts = args->start_time; - /* TODO(hongyu): call census_tracing_start_op here. */ - GRPC_CLOSURE_INIT(&d->finish_recv, server_on_done_recv, elem, - grpc_schedule_on_exec_ctx); - return GRPC_ERROR_NONE; -} - -static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *d = (call_data *)elem->call_data; - GPR_ASSERT(d != NULL); - /* TODO(hongyu): record rpc server stats and census_tracing_end_op here */ -} - -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = (channel_data *)elem->channel_data; - GPR_ASSERT(chand != NULL); - return GRPC_ERROR_NONE; -} - -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - GPR_ASSERT(chand != NULL); -} - -const grpc_channel_filter grpc_client_census_filter = { - client_start_transport_op, - grpc_channel_next_op, - sizeof(call_data), - client_init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - client_destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "census-client"}; - -const grpc_channel_filter grpc_server_census_filter = { - server_start_transport_op, - grpc_channel_next_op, - sizeof(call_data), - server_init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - server_destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "census-server"}; diff --git a/src/core/ext/census/grpc_filter.cc b/src/core/ext/census/grpc_filter.cc new file mode 100644 index 0000000000..b37ab90389 --- /dev/null +++ b/src/core/ext/census/grpc_filter.cc @@ -0,0 +1,196 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/grpc_filter.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/census/census_interface.h" +#include "src/core/ext/census/census_rpc_stats.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/transport/static_metadata.h" + +typedef struct call_data { + census_op_id op_id; + census_context *ctxt; + gpr_timespec start_ts; + int error; + + /* recv callback */ + grpc_metadata_batch *recv_initial_metadata; + grpc_closure *on_done_recv; + grpc_closure finish_recv; +} call_data; + +typedef struct channel_data { uint8_t unused; } channel_data; + +static void extract_and_annotate_method_tag(grpc_metadata_batch *md, + call_data *calld, + channel_data *chand) { + grpc_linked_mdelem *m; + for (m = md->list.head; m != NULL; m = m->next) { + if (grpc_slice_eq(GRPC_MDKEY(m->md), GRPC_MDSTR_PATH)) { + /* Add method tag here */ + } + } +} + +static void client_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (op->send_initial_metadata) { + extract_and_annotate_method_tag( + op->payload->send_initial_metadata.send_initial_metadata, calld, chand); + } +} + +static void client_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + client_mutate_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); +} + +static void server_on_done_recv(grpc_exec_ctx *exec_ctx, void *ptr, + grpc_error *error) { + GPR_TIMER_BEGIN("census-server:server_on_done_recv", 0); + grpc_call_element *elem = (grpc_call_element *)ptr; + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (error == GRPC_ERROR_NONE) { + extract_and_annotate_method_tag(calld->recv_initial_metadata, calld, chand); + } + calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, error); + GPR_TIMER_END("census-server:server_on_done_recv", 0); +} + +static void server_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + call_data *calld = (call_data *)elem->call_data; + if (op->recv_initial_metadata) { + /* substitute our callback for the op callback */ + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + calld->on_done_recv = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->finish_recv; + } +} + +static void server_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + /* TODO(ctiller): this code fails. I don't know why. I expect it's + incomplete, and someone should look at it soon. + + call_data *calld = elem->call_data; + GPR_ASSERT((calld->op_id.upper != 0) || (calld->op_id.lower != 0)); */ + server_mutate_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); +} + +static grpc_error *client_init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *d = (call_data *)elem->call_data; + GPR_ASSERT(d != NULL); + memset(d, 0, sizeof(*d)); + d->start_ts = args->start_time; + return GRPC_ERROR_NONE; +} + +static void client_destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *d = (call_data *)elem->call_data; + GPR_ASSERT(d != NULL); + /* TODO(hongyu): record rpc client stats and census_rpc_end_op here */ +} + +static grpc_error *server_init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *d = (call_data *)elem->call_data; + GPR_ASSERT(d != NULL); + memset(d, 0, sizeof(*d)); + d->start_ts = args->start_time; + /* TODO(hongyu): call census_tracing_start_op here. */ + GRPC_CLOSURE_INIT(&d->finish_recv, server_on_done_recv, elem, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +static void server_destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *d = (call_data *)elem->call_data; + GPR_ASSERT(d != NULL); + /* TODO(hongyu): record rpc server stats and census_tracing_end_op here */ +} + +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + channel_data *chand = (channel_data *)elem->channel_data; + GPR_ASSERT(chand != NULL); + return GRPC_ERROR_NONE; +} + +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + GPR_ASSERT(chand != NULL); +} + +const grpc_channel_filter grpc_client_census_filter = { + client_start_transport_op, + grpc_channel_next_op, + sizeof(call_data), + client_init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + client_destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "census-client"}; + +const grpc_channel_filter grpc_server_census_filter = { + server_start_transport_op, + grpc_channel_next_op, + sizeof(call_data), + server_init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + server_destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "census-server"}; diff --git a/src/core/ext/census/grpc_plugin.c b/src/core/ext/census/grpc_plugin.c deleted file mode 100644 index c0efe5afb8..0000000000 --- a/src/core/ext/census/grpc_plugin.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include - -#include "src/core/ext/census/grpc_filter.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/surface/channel_init.h" - -static bool is_census_enabled(const grpc_channel_args *a) { - size_t i; - if (a == NULL) return 0; - for (i = 0; i < a->num_args; i++) { - if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_CENSUS)) { - return a->args[i].value.integer != 0 && census_enabled(); - } - } - return census_enabled() && !grpc_channel_args_want_minimal_stack(a); -} - -static bool maybe_add_census_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, - void *arg) { - const grpc_channel_args *args = - grpc_channel_stack_builder_get_channel_arguments(builder); - if (is_census_enabled(args)) { - return grpc_channel_stack_builder_prepend_filter( - builder, (const grpc_channel_filter *)arg, NULL, NULL); - } - return true; -} - -void census_grpc_plugin_init(void) { - /* Only initialize census if no one else has and some features are - * available. */ - if (census_enabled() == CENSUS_FEATURE_NONE && - census_supported() != CENSUS_FEATURE_NONE) { - if (census_initialize(census_supported())) { /* enable all features. */ - gpr_log(GPR_ERROR, "Could not initialize census."); - } - } - grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX, - maybe_add_census_filter, - (void *)&grpc_client_census_filter); - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, - maybe_add_census_filter, - (void *)&grpc_server_census_filter); -} - -void census_grpc_plugin_shutdown(void) { census_shutdown(); } diff --git a/src/core/ext/census/grpc_plugin.cc b/src/core/ext/census/grpc_plugin.cc new file mode 100644 index 0000000000..22b16c6c63 --- /dev/null +++ b/src/core/ext/census/grpc_plugin.cc @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +#include "src/core/ext/census/grpc_filter.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/channel_init.h" + +static bool is_census_enabled(const grpc_channel_args *a) { + size_t i; + if (a == NULL) return 0; + for (i = 0; i < a->num_args; i++) { + if (0 == strcmp(a->args[i].key, GRPC_ARG_ENABLE_CENSUS)) { + return a->args[i].value.integer != 0 && census_enabled(); + } + } + return census_enabled() && !grpc_channel_args_want_minimal_stack(a); +} + +static bool maybe_add_census_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, + void *arg) { + const grpc_channel_args *args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (is_census_enabled(args)) { + return grpc_channel_stack_builder_prepend_filter( + builder, (const grpc_channel_filter *)arg, NULL, NULL); + } + return true; +} + +extern "C" void census_grpc_plugin_init(void) { + /* Only initialize census if no one else has and some features are + * available. */ + if (census_enabled() == CENSUS_FEATURE_NONE && + census_supported() != CENSUS_FEATURE_NONE) { + if (census_initialize(census_supported())) { /* enable all features. */ + gpr_log(GPR_ERROR, "Could not initialize census."); + } + } + grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MAX, + maybe_add_census_filter, + (void *)&grpc_client_census_filter); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, + maybe_add_census_filter, + (void *)&grpc_server_census_filter); +} + +extern "C" void census_grpc_plugin_shutdown(void) { census_shutdown(); } diff --git a/src/core/ext/census/hash_table.c b/src/core/ext/census/hash_table.c deleted file mode 100644 index 545b0857c7..0000000000 --- a/src/core/ext/census/hash_table.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/hash_table.h" - -#include -#include - -#include -#include -#include - -#define CENSUS_HT_NUM_BUCKETS 1999 - -/* A single hash table data entry */ -typedef struct ht_entry { - census_ht_key key; - void *data; - struct ht_entry *next; -} ht_entry; - -/* hash table bucket */ -typedef struct bucket { - /* NULL if bucket is empty */ - ht_entry *next; - /* -1 if all buckets are empty. */ - int32_t prev_non_empty_bucket; - /* -1 if all buckets are empty. */ - int32_t next_non_empty_bucket; -} bucket; - -struct unresizable_hash_table { - /* Number of entries in the table */ - size_t size; - /* Number of buckets */ - uint32_t num_buckets; - /* Array of buckets initialized at creation time. Memory consumption is - 16 bytes per bucket on a 64-bit platform. */ - bucket *buckets; - /* Index of the first non-empty bucket. -1 iff size == 0. */ - int32_t first_non_empty_bucket; - /* Index of the last non_empty bucket. -1 iff size == 0. */ - int32_t last_non_empty_bucket; - /* Immutable options of this hash table, initialized at creation time. */ - census_ht_option options; -}; - -typedef struct entry_locator { - int32_t bucket_idx; - int is_first_in_chain; - int found; - ht_entry *prev_entry; -} entry_locator; - -/* Asserts if option is not valid. */ -void check_options(const census_ht_option *option) { - GPR_ASSERT(option != NULL); - GPR_ASSERT(option->num_buckets > 0); - GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 || - option->key_type == CENSUS_HT_POINTER); - if (option->key_type == CENSUS_HT_UINT64) { - GPR_ASSERT(option->hash == NULL); - } else if (option->key_type == CENSUS_HT_POINTER) { - GPR_ASSERT(option->hash != NULL); - GPR_ASSERT(option->compare_keys != NULL); - } -} - -#define REMOVE_NEXT(options, ptr) \ - do { \ - ht_entry *tmp = (ptr)->next; \ - (ptr)->next = tmp->next; \ - delete_entry(options, tmp); \ - } while (0) - -static void delete_entry(const census_ht_option *opt, ht_entry *p) { - if (opt->delete_data != NULL) { - opt->delete_data(p->data); - } - if (opt->delete_key != NULL) { - opt->delete_key(p->key.ptr); - } - gpr_free(p); -} - -static uint64_t hash(const census_ht_option *opt, census_ht_key key) { - return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr); -} - -census_ht *census_ht_create(const census_ht_option *option) { - int i; - census_ht *ret = NULL; - check_options(option); - ret = (census_ht *)gpr_malloc(sizeof(census_ht)); - ret->size = 0; - ret->num_buckets = option->num_buckets; - ret->buckets = (bucket *)gpr_malloc(sizeof(bucket) * ret->num_buckets); - ret->options = *option; - /* initialize each bucket */ - for (i = 0; i < ret->options.num_buckets; i++) { - ret->buckets[i].prev_non_empty_bucket = -1; - ret->buckets[i].next_non_empty_bucket = -1; - ret->buckets[i].next = NULL; - } - return ret; -} - -static int32_t find_bucket_idx(const census_ht *ht, census_ht_key key) { - return hash(&ht->options, key) % ht->num_buckets; -} - -static int keys_match(const census_ht_option *opt, const ht_entry *p, - const census_ht_key key) { - GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 || - opt->key_type == CENSUS_HT_POINTER); - if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val; - return !opt->compare_keys((p->key).ptr, key.ptr); -} - -static entry_locator ht_find(const census_ht *ht, census_ht_key key) { - entry_locator loc = {0, 0, 0, NULL}; - int32_t idx = 0; - ht_entry *ptr = NULL; - GPR_ASSERT(ht != NULL); - idx = find_bucket_idx(ht, key); - ptr = ht->buckets[idx].next; - if (ptr == NULL) { - /* bucket is empty */ - return loc; - } - if (keys_match(&ht->options, ptr, key)) { - loc.bucket_idx = idx; - loc.is_first_in_chain = 1; - loc.found = 1; - return loc; - } else { - for (; ptr->next != NULL; ptr = ptr->next) { - if (keys_match(&ht->options, ptr->next, key)) { - loc.bucket_idx = idx; - loc.is_first_in_chain = 0; - loc.found = 1; - loc.prev_entry = ptr; - return loc; - } - } - } - /* Could not find the key */ - return loc; -} - -void *census_ht_find(const census_ht *ht, census_ht_key key) { - entry_locator loc = ht_find(ht, key); - if (loc.found == 0) { - return NULL; - } - return loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next->data - : loc.prev_entry->next->data; -} - -void census_ht_insert(census_ht *ht, census_ht_key key, void *data) { - int32_t idx = find_bucket_idx(ht, key); - ht_entry *ptr = NULL; - entry_locator loc = ht_find(ht, key); - if (loc.found) { - /* Replace old value with new value. */ - ptr = loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next - : loc.prev_entry->next; - if (ht->options.delete_data != NULL) { - ht->options.delete_data(ptr->data); - } - ptr->data = data; - return; - } - - /* first entry in the table. */ - if (ht->size == 0) { - ht->buckets[idx].next_non_empty_bucket = -1; - ht->buckets[idx].prev_non_empty_bucket = -1; - ht->first_non_empty_bucket = idx; - ht->last_non_empty_bucket = idx; - } else if (ht->buckets[idx].next == NULL) { - /* first entry in the bucket. */ - ht->buckets[ht->last_non_empty_bucket].next_non_empty_bucket = idx; - ht->buckets[idx].prev_non_empty_bucket = ht->last_non_empty_bucket; - ht->buckets[idx].next_non_empty_bucket = -1; - ht->last_non_empty_bucket = idx; - } - ptr = (ht_entry *)gpr_malloc(sizeof(ht_entry)); - ptr->key = key; - ptr->data = data; - ptr->next = ht->buckets[idx].next; - ht->buckets[idx].next = ptr; - ht->size++; -} - -void census_ht_erase(census_ht *ht, census_ht_key key) { - entry_locator loc = ht_find(ht, key); - if (loc.found == 0) { - /* noop if not found */ - return; - } - ht->size--; - if (loc.is_first_in_chain) { - bucket *b = &ht->buckets[loc.bucket_idx]; - GPR_ASSERT(b->next != NULL); - /* The only entry in the bucket */ - if (b->next->next == NULL) { - int prev = b->prev_non_empty_bucket; - int next = b->next_non_empty_bucket; - if (prev != -1) { - ht->buckets[prev].next_non_empty_bucket = next; - } else { - ht->first_non_empty_bucket = next; - } - if (next != -1) { - ht->buckets[next].prev_non_empty_bucket = prev; - } else { - ht->last_non_empty_bucket = prev; - } - } - REMOVE_NEXT(&ht->options, b); - } else { - GPR_ASSERT(loc.prev_entry->next != NULL); - REMOVE_NEXT(&ht->options, loc.prev_entry); - } -} - -/* Returns NULL if input table is empty. */ -census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num) { - census_ht_kv *ret = NULL; - int i = 0; - int32_t idx = -1; - GPR_ASSERT(ht != NULL && num != NULL); - *num = ht->size; - if (*num == 0) { - return NULL; - } - - ret = (census_ht_kv *)gpr_malloc(sizeof(census_ht_kv) * ht->size); - idx = ht->first_non_empty_bucket; - while (idx >= 0) { - ht_entry *ptr = ht->buckets[idx].next; - for (; ptr != NULL; ptr = ptr->next) { - ret[i].k = ptr->key; - ret[i].v = ptr->data; - i++; - } - idx = ht->buckets[idx].next_non_empty_bucket; - } - return ret; -} - -static void ht_delete_entry_chain(const census_ht_option *options, - ht_entry *first) { - if (first == NULL) { - return; - } - if (first->next != NULL) { - ht_delete_entry_chain(options, first->next); - } - delete_entry(options, first); -} - -void census_ht_destroy(census_ht *ht) { - unsigned i; - for (i = 0; i < ht->num_buckets; ++i) { - ht_delete_entry_chain(&ht->options, ht->buckets[i].next); - } - gpr_free(ht->buckets); - gpr_free(ht); -} - -size_t census_ht_get_size(const census_ht *ht) { return ht->size; } diff --git a/src/core/ext/census/hash_table.cc b/src/core/ext/census/hash_table.cc new file mode 100644 index 0000000000..545b0857c7 --- /dev/null +++ b/src/core/ext/census/hash_table.cc @@ -0,0 +1,288 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/hash_table.h" + +#include +#include + +#include +#include +#include + +#define CENSUS_HT_NUM_BUCKETS 1999 + +/* A single hash table data entry */ +typedef struct ht_entry { + census_ht_key key; + void *data; + struct ht_entry *next; +} ht_entry; + +/* hash table bucket */ +typedef struct bucket { + /* NULL if bucket is empty */ + ht_entry *next; + /* -1 if all buckets are empty. */ + int32_t prev_non_empty_bucket; + /* -1 if all buckets are empty. */ + int32_t next_non_empty_bucket; +} bucket; + +struct unresizable_hash_table { + /* Number of entries in the table */ + size_t size; + /* Number of buckets */ + uint32_t num_buckets; + /* Array of buckets initialized at creation time. Memory consumption is + 16 bytes per bucket on a 64-bit platform. */ + bucket *buckets; + /* Index of the first non-empty bucket. -1 iff size == 0. */ + int32_t first_non_empty_bucket; + /* Index of the last non_empty bucket. -1 iff size == 0. */ + int32_t last_non_empty_bucket; + /* Immutable options of this hash table, initialized at creation time. */ + census_ht_option options; +}; + +typedef struct entry_locator { + int32_t bucket_idx; + int is_first_in_chain; + int found; + ht_entry *prev_entry; +} entry_locator; + +/* Asserts if option is not valid. */ +void check_options(const census_ht_option *option) { + GPR_ASSERT(option != NULL); + GPR_ASSERT(option->num_buckets > 0); + GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 || + option->key_type == CENSUS_HT_POINTER); + if (option->key_type == CENSUS_HT_UINT64) { + GPR_ASSERT(option->hash == NULL); + } else if (option->key_type == CENSUS_HT_POINTER) { + GPR_ASSERT(option->hash != NULL); + GPR_ASSERT(option->compare_keys != NULL); + } +} + +#define REMOVE_NEXT(options, ptr) \ + do { \ + ht_entry *tmp = (ptr)->next; \ + (ptr)->next = tmp->next; \ + delete_entry(options, tmp); \ + } while (0) + +static void delete_entry(const census_ht_option *opt, ht_entry *p) { + if (opt->delete_data != NULL) { + opt->delete_data(p->data); + } + if (opt->delete_key != NULL) { + opt->delete_key(p->key.ptr); + } + gpr_free(p); +} + +static uint64_t hash(const census_ht_option *opt, census_ht_key key) { + return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr); +} + +census_ht *census_ht_create(const census_ht_option *option) { + int i; + census_ht *ret = NULL; + check_options(option); + ret = (census_ht *)gpr_malloc(sizeof(census_ht)); + ret->size = 0; + ret->num_buckets = option->num_buckets; + ret->buckets = (bucket *)gpr_malloc(sizeof(bucket) * ret->num_buckets); + ret->options = *option; + /* initialize each bucket */ + for (i = 0; i < ret->options.num_buckets; i++) { + ret->buckets[i].prev_non_empty_bucket = -1; + ret->buckets[i].next_non_empty_bucket = -1; + ret->buckets[i].next = NULL; + } + return ret; +} + +static int32_t find_bucket_idx(const census_ht *ht, census_ht_key key) { + return hash(&ht->options, key) % ht->num_buckets; +} + +static int keys_match(const census_ht_option *opt, const ht_entry *p, + const census_ht_key key) { + GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 || + opt->key_type == CENSUS_HT_POINTER); + if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val; + return !opt->compare_keys((p->key).ptr, key.ptr); +} + +static entry_locator ht_find(const census_ht *ht, census_ht_key key) { + entry_locator loc = {0, 0, 0, NULL}; + int32_t idx = 0; + ht_entry *ptr = NULL; + GPR_ASSERT(ht != NULL); + idx = find_bucket_idx(ht, key); + ptr = ht->buckets[idx].next; + if (ptr == NULL) { + /* bucket is empty */ + return loc; + } + if (keys_match(&ht->options, ptr, key)) { + loc.bucket_idx = idx; + loc.is_first_in_chain = 1; + loc.found = 1; + return loc; + } else { + for (; ptr->next != NULL; ptr = ptr->next) { + if (keys_match(&ht->options, ptr->next, key)) { + loc.bucket_idx = idx; + loc.is_first_in_chain = 0; + loc.found = 1; + loc.prev_entry = ptr; + return loc; + } + } + } + /* Could not find the key */ + return loc; +} + +void *census_ht_find(const census_ht *ht, census_ht_key key) { + entry_locator loc = ht_find(ht, key); + if (loc.found == 0) { + return NULL; + } + return loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next->data + : loc.prev_entry->next->data; +} + +void census_ht_insert(census_ht *ht, census_ht_key key, void *data) { + int32_t idx = find_bucket_idx(ht, key); + ht_entry *ptr = NULL; + entry_locator loc = ht_find(ht, key); + if (loc.found) { + /* Replace old value with new value. */ + ptr = loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next + : loc.prev_entry->next; + if (ht->options.delete_data != NULL) { + ht->options.delete_data(ptr->data); + } + ptr->data = data; + return; + } + + /* first entry in the table. */ + if (ht->size == 0) { + ht->buckets[idx].next_non_empty_bucket = -1; + ht->buckets[idx].prev_non_empty_bucket = -1; + ht->first_non_empty_bucket = idx; + ht->last_non_empty_bucket = idx; + } else if (ht->buckets[idx].next == NULL) { + /* first entry in the bucket. */ + ht->buckets[ht->last_non_empty_bucket].next_non_empty_bucket = idx; + ht->buckets[idx].prev_non_empty_bucket = ht->last_non_empty_bucket; + ht->buckets[idx].next_non_empty_bucket = -1; + ht->last_non_empty_bucket = idx; + } + ptr = (ht_entry *)gpr_malloc(sizeof(ht_entry)); + ptr->key = key; + ptr->data = data; + ptr->next = ht->buckets[idx].next; + ht->buckets[idx].next = ptr; + ht->size++; +} + +void census_ht_erase(census_ht *ht, census_ht_key key) { + entry_locator loc = ht_find(ht, key); + if (loc.found == 0) { + /* noop if not found */ + return; + } + ht->size--; + if (loc.is_first_in_chain) { + bucket *b = &ht->buckets[loc.bucket_idx]; + GPR_ASSERT(b->next != NULL); + /* The only entry in the bucket */ + if (b->next->next == NULL) { + int prev = b->prev_non_empty_bucket; + int next = b->next_non_empty_bucket; + if (prev != -1) { + ht->buckets[prev].next_non_empty_bucket = next; + } else { + ht->first_non_empty_bucket = next; + } + if (next != -1) { + ht->buckets[next].prev_non_empty_bucket = prev; + } else { + ht->last_non_empty_bucket = prev; + } + } + REMOVE_NEXT(&ht->options, b); + } else { + GPR_ASSERT(loc.prev_entry->next != NULL); + REMOVE_NEXT(&ht->options, loc.prev_entry); + } +} + +/* Returns NULL if input table is empty. */ +census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num) { + census_ht_kv *ret = NULL; + int i = 0; + int32_t idx = -1; + GPR_ASSERT(ht != NULL && num != NULL); + *num = ht->size; + if (*num == 0) { + return NULL; + } + + ret = (census_ht_kv *)gpr_malloc(sizeof(census_ht_kv) * ht->size); + idx = ht->first_non_empty_bucket; + while (idx >= 0) { + ht_entry *ptr = ht->buckets[idx].next; + for (; ptr != NULL; ptr = ptr->next) { + ret[i].k = ptr->key; + ret[i].v = ptr->data; + i++; + } + idx = ht->buckets[idx].next_non_empty_bucket; + } + return ret; +} + +static void ht_delete_entry_chain(const census_ht_option *options, + ht_entry *first) { + if (first == NULL) { + return; + } + if (first->next != NULL) { + ht_delete_entry_chain(options, first->next); + } + delete_entry(options, first); +} + +void census_ht_destroy(census_ht *ht) { + unsigned i; + for (i = 0; i < ht->num_buckets; ++i) { + ht_delete_entry_chain(&ht->options, ht->buckets[i].next); + } + gpr_free(ht->buckets); + gpr_free(ht); +} + +size_t census_ht_get_size(const census_ht *ht) { return ht->size; } diff --git a/src/core/ext/census/initialize.c b/src/core/ext/census/initialize.c deleted file mode 100644 index 165a1221d4..0000000000 --- a/src/core/ext/census/initialize.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include "src/core/ext/census/base_resources.h" -#include "src/core/ext/census/resource.h" - -static int features_enabled = CENSUS_FEATURE_NONE; - -int census_initialize(int features) { - if (features_enabled != CENSUS_FEATURE_NONE) { - // Must have been a previous call to census_initialize; return error - return -1; - } - features_enabled = features & CENSUS_FEATURE_ALL; - if (features & CENSUS_FEATURE_STATS) { - initialize_resources(); - define_base_resources(); - } - - return features_enabled; -} - -void census_shutdown(void) { - if (features_enabled & CENSUS_FEATURE_STATS) { - shutdown_resources(); - } - features_enabled = CENSUS_FEATURE_NONE; -} - -int census_supported(void) { - /* TODO(aveitch): improve this as we implement features... */ - return CENSUS_FEATURE_NONE; -} - -int census_enabled(void) { return features_enabled; } diff --git a/src/core/ext/census/initialize.cc b/src/core/ext/census/initialize.cc new file mode 100644 index 0000000000..165a1221d4 --- /dev/null +++ b/src/core/ext/census/initialize.cc @@ -0,0 +1,51 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include "src/core/ext/census/base_resources.h" +#include "src/core/ext/census/resource.h" + +static int features_enabled = CENSUS_FEATURE_NONE; + +int census_initialize(int features) { + if (features_enabled != CENSUS_FEATURE_NONE) { + // Must have been a previous call to census_initialize; return error + return -1; + } + features_enabled = features & CENSUS_FEATURE_ALL; + if (features & CENSUS_FEATURE_STATS) { + initialize_resources(); + define_base_resources(); + } + + return features_enabled; +} + +void census_shutdown(void) { + if (features_enabled & CENSUS_FEATURE_STATS) { + shutdown_resources(); + } + features_enabled = CENSUS_FEATURE_NONE; +} + +int census_supported(void) { + /* TODO(aveitch): improve this as we implement features... */ + return CENSUS_FEATURE_NONE; +} + +int census_enabled(void) { return features_enabled; } diff --git a/src/core/ext/census/intrusive_hash_map.c b/src/core/ext/census/intrusive_hash_map.c deleted file mode 100644 index 7930486963..0000000000 --- a/src/core/ext/census/intrusive_hash_map.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/intrusive_hash_map.h" -#include - -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.cc b/src/core/ext/census/intrusive_hash_map.cc new file mode 100644 index 0000000000..7930486963 --- /dev/null +++ b/src/core/ext/census/intrusive_hash_map.cc @@ -0,0 +1,305 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/intrusive_hash_map.h" +#include + +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/mlog.c b/src/core/ext/census/mlog.c deleted file mode 100644 index 4b8c8466b3..0000000000 --- a/src/core/ext/census/mlog.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Implements an efficient in-memory log, optimized for multiple writers and -// a single reader. Available log space is divided up in blocks of -// CENSUS_LOG_2_MAX_RECORD_SIZE bytes. A block can be in one of the following -// three data structures: -// - Free blocks (free_block_list) -// - Blocks with unread data (dirty_block_list) -// - Blocks currently attached to cores (core_local_blocks[]) -// -// census_log_start_write() moves a block from core_local_blocks[] to the end of -// dirty_block_list when block: -// - is out-of-space OR -// - has an incomplete record (an incomplete record occurs when a thread calls -// census_log_start_write() and is context-switched before calling -// census_log_end_write() -// So, blocks in dirty_block_list are ordered, from oldest to newest, by the -// time when block is detached from the core. -// -// census_log_read_next() first iterates over dirty_block_list and then -// core_local_blocks[]. It moves completely read blocks from dirty_block_list -// to free_block_list. Blocks in core_local_blocks[] are not freed, even when -// completely read. -// -// If the log is configured to discard old records and free_block_list is empty, -// census_log_start_write() iterates over dirty_block_list to allocate a -// new block. It moves the oldest available block (no pending read/write) to -// core_local_blocks[]. -// -// core_local_block_struct is used to implement a map from core id to the block -// associated with that core. This mapping is advisory. It is possible that the -// block returned by this mapping is no longer associated with that core. This -// mapping is updated, lazily, by census_log_start_write(). -// -// Locking in block struct: -// -// Exclusive g_log.lock must be held before calling any functions operating on -// block structs except census_log_start_write() and census_log_end_write(). -// -// Writes to a block are serialized via writer_lock. census_log_start_write() -// acquires this lock and census_log_end_write() releases it. On failure to -// acquire the lock, writer allocates a new block for the current core and -// updates core_local_block accordingly. -// -// Simultaneous read and write access is allowed. Readers can safely read up to -// committed bytes (bytes_committed). -// -// reader_lock protects the block, currently being read, from getting recycled. -// start_read() acquires reader_lock and end_read() releases the lock. -// -// Read/write access to a block is disabled via try_disable_access(). It returns -// with both writer_lock and reader_lock held. These locks are subsequently -// released by enable_access() to enable access to the block. -// -// A note on naming: Most function/struct names are prepended by cl_ -// (shorthand for census_log). Further, functions that manipulate structures -// include the name of the structure, which will be passed as the first -// argument. E.g. cl_block_initialize() will initialize a cl_block. - -#include "src/core/ext/census/mlog.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// End of platform specific code - -typedef struct census_log_block_list_struct { - struct census_log_block_list_struct* next; - struct census_log_block_list_struct* prev; - struct census_log_block* block; -} cl_block_list_struct; - -typedef struct census_log_block { - // Pointer to underlying buffer. - char* buffer; - gpr_atm writer_lock; - gpr_atm reader_lock; - // Keeps completely written bytes. Declared atomic because accessed - // simultaneously by reader and writer. - gpr_atm bytes_committed; - // Bytes already read. - size_t bytes_read; - // Links for list. - cl_block_list_struct link; -// We want this structure to be cacheline aligned. We assume the following -// sizes for the various parts on 32/64bit systems: -// type 32b size 64b size -// char* 4 8 -// 3x gpr_atm 12 24 -// size_t 4 8 -// cl_block_list_struct 12 24 -// TOTAL 32 64 -// -// Depending on the size of our cacheline and the architecture, we -// selectively add char buffering to this structure. The size is checked -// via assert in census_log_initialize(). -#if defined(GPR_ARCH_64) -#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 64) -#else -#if defined(GPR_ARCH_32) -#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 32) -#else -#error "Unknown architecture" -#endif -#endif -#if CL_BLOCK_PAD_SIZE > 0 - char padding[CL_BLOCK_PAD_SIZE]; -#endif -} cl_block; - -// A list of cl_blocks, doubly-linked through cl_block::link. -typedef struct census_log_block_list { - int32_t count; // Number of items in list. - cl_block_list_struct ht; // head/tail of linked list. -} cl_block_list; - -// Cacheline aligned block pointers to avoid false sharing. Block pointer must -// be initialized via set_block(), before calling other functions -typedef struct census_log_core_local_block { - gpr_atm block; -// Ensure cachline alignment: we assume sizeof(gpr_atm) == 4 or 8 -#if defined(GPR_ARCH_64) -#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 8) -#else -#if defined(GPR_ARCH_32) -#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 4) -#else -#error "Unknown architecture" -#endif -#endif -#if CL_CORE_LOCAL_BLOCK_PAD_SIZE > 0 - char padding[CL_CORE_LOCAL_BLOCK_PAD_SIZE]; -#endif -} cl_core_local_block; - -struct census_log { - int discard_old_records; - // Number of cores (aka hardware-contexts) - unsigned num_cores; - // number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log - uint32_t num_blocks; - cl_block* blocks; // Block metadata. - cl_core_local_block* core_local_blocks; // Keeps core to block mappings. - gpr_mu lock; - int initialized; // has log been initialized? - // Keeps the state of the reader iterator. A value of 0 indicates that - // iterator has reached the end. census_log_init_reader() resets the value - // to num_core to restart iteration. - uint32_t read_iterator_state; - // Points to the block being read. If non-NULL, the block is locked for - // reading(block_being_read_->reader_lock is held). - cl_block* block_being_read; - char* buffer; - cl_block_list free_block_list; - cl_block_list dirty_block_list; - gpr_atm out_of_space_count; -}; - -// Single internal log. -static struct census_log g_log; - -// Functions that operate on an atomic memory location used as a lock. - -// Returns non-zero if lock is acquired. -static int cl_try_lock(gpr_atm* lock) { return gpr_atm_acq_cas(lock, 0, 1); } - -static void cl_unlock(gpr_atm* lock) { gpr_atm_rel_store(lock, 0); } - -// Functions that operate on cl_core_local_block's. - -static void cl_core_local_block_set_block(cl_core_local_block* clb, - cl_block* block) { - gpr_atm_rel_store(&clb->block, (gpr_atm)block); -} - -static cl_block* cl_core_local_block_get_block(cl_core_local_block* clb) { - return (cl_block*)gpr_atm_acq_load(&clb->block); -} - -// Functions that operate on cl_block_list_struct's. - -static void cl_block_list_struct_initialize(cl_block_list_struct* bls, - cl_block* block) { - bls->next = bls->prev = bls; - bls->block = block; -} - -// Functions that operate on cl_block_list's. - -static void cl_block_list_initialize(cl_block_list* list) { - list->count = 0; - cl_block_list_struct_initialize(&list->ht, NULL); -} - -// Returns head of *this, or NULL if empty. -static cl_block* cl_block_list_head(cl_block_list* list) { - return list->ht.next->block; -} - -// Insert element *e after *pos. -static void cl_block_list_insert(cl_block_list* list, cl_block_list_struct* pos, - cl_block_list_struct* e) { - list->count++; - e->next = pos->next; - e->prev = pos; - e->next->prev = e; - e->prev->next = e; -} - -// Insert block at the head of the list -static void cl_block_list_insert_at_head(cl_block_list* list, cl_block* block) { - cl_block_list_insert(list, &list->ht, &block->link); -} - -// Insert block at the tail of the list. -static void cl_block_list_insert_at_tail(cl_block_list* list, cl_block* block) { - cl_block_list_insert(list, list->ht.prev, &block->link); -} - -// Removes block *b. Requires *b be in the list. -static void cl_block_list_remove(cl_block_list* list, cl_block* b) { - list->count--; - b->link.next->prev = b->link.prev; - b->link.prev->next = b->link.next; -} - -// Functions that operate on cl_block's - -static void cl_block_initialize(cl_block* block, char* buffer) { - block->buffer = buffer; - gpr_atm_rel_store(&block->writer_lock, 0); - gpr_atm_rel_store(&block->reader_lock, 0); - gpr_atm_rel_store(&block->bytes_committed, 0); - block->bytes_read = 0; - cl_block_list_struct_initialize(&block->link, block); -} - -// Guards against exposing partially written buffer to the reader. -static void cl_block_set_bytes_committed(cl_block* block, - size_t bytes_committed) { - gpr_atm_rel_store(&block->bytes_committed, (gpr_atm)bytes_committed); -} - -static size_t cl_block_get_bytes_committed(cl_block* block) { - return (size_t)gpr_atm_acq_load(&block->bytes_committed); -} - -// Tries to disable future read/write access to this block. Succeeds if: -// - no in-progress write AND -// - no in-progress read AND -// - 'discard_data' set to true OR no unread data -// On success, clears the block state and returns with writer_lock_ and -// reader_lock_ held. These locks are released by a subsequent -// cl_block_access_enable() call. -static bool cl_block_try_disable_access(cl_block* block, int discard_data) { - if (!cl_try_lock(&block->writer_lock)) { - return false; - } - if (!cl_try_lock(&block->reader_lock)) { - cl_unlock(&block->writer_lock); - return false; - } - if (!discard_data && - (block->bytes_read != cl_block_get_bytes_committed(block))) { - cl_unlock(&block->reader_lock); - cl_unlock(&block->writer_lock); - return false; - } - cl_block_set_bytes_committed(block, 0); - block->bytes_read = 0; - return true; -} - -static void cl_block_enable_access(cl_block* block) { - cl_unlock(&block->reader_lock); - cl_unlock(&block->writer_lock); -} - -// Returns with writer_lock held. -static void* cl_block_start_write(cl_block* block, size_t size) { - if (!cl_try_lock(&block->writer_lock)) { - return NULL; - } - size_t bytes_committed = cl_block_get_bytes_committed(block); - if (bytes_committed + size > CENSUS_LOG_MAX_RECORD_SIZE) { - cl_unlock(&block->writer_lock); - return NULL; - } - return block->buffer + bytes_committed; -} - -// Releases writer_lock and increments committed bytes by 'bytes_written'. -// 'bytes_written' must be <= 'size' specified in the corresponding -// StartWrite() call. This function is thread-safe. -static void cl_block_end_write(cl_block* block, size_t bytes_written) { - cl_block_set_bytes_committed( - block, cl_block_get_bytes_committed(block) + bytes_written); - cl_unlock(&block->writer_lock); -} - -// Returns a pointer to the first unread byte in buffer. The number of bytes -// available are returned in 'bytes_available'. Acquires reader lock that is -// released by a subsequent cl_block_end_read() call. Returns NULL if: -// - read in progress -// - no data available -static void* cl_block_start_read(cl_block* block, size_t* bytes_available) { - if (!cl_try_lock(&block->reader_lock)) { - return NULL; - } - // bytes_committed may change from under us. Use bytes_available to update - // bytes_read below. - size_t bytes_committed = cl_block_get_bytes_committed(block); - GPR_ASSERT(bytes_committed >= block->bytes_read); - *bytes_available = bytes_committed - block->bytes_read; - if (*bytes_available == 0) { - cl_unlock(&block->reader_lock); - return NULL; - } - void* record = block->buffer + block->bytes_read; - block->bytes_read += *bytes_available; - return record; -} - -static void cl_block_end_read(cl_block* block) { - cl_unlock(&block->reader_lock); -} - -// Internal functions operating on g_log - -// Allocates a new free block (or recycles an available dirty block if log is -// configured to discard old records). Returns NULL if out-of-space. -static cl_block* cl_allocate_block(void) { - cl_block* block = cl_block_list_head(&g_log.free_block_list); - if (block != NULL) { - cl_block_list_remove(&g_log.free_block_list, block); - return block; - } - if (!g_log.discard_old_records) { - // No free block and log is configured to keep old records. - return NULL; - } - // Recycle dirty block. Start from the oldest. - for (block = cl_block_list_head(&g_log.dirty_block_list); block != NULL; - block = block->link.next->block) { - if (cl_block_try_disable_access(block, 1 /* discard data */)) { - cl_block_list_remove(&g_log.dirty_block_list, block); - return block; - } - } - return NULL; -} - -// Allocates a new block and updates core id => block mapping. 'old_block' -// points to the block that the caller thinks is attached to -// 'core_id'. 'old_block' may be NULL. Returns true if: -// - allocated a new block OR -// - 'core_id' => 'old_block' mapping changed (another thread allocated a -// block before lock was acquired). -static bool cl_allocate_core_local_block(uint32_t core_id, - cl_block* old_block) { - // Now that we have the lock, check if core-local mapping has changed. - cl_core_local_block* core_local_block = &g_log.core_local_blocks[core_id]; - cl_block* block = cl_core_local_block_get_block(core_local_block); - if ((block != NULL) && (block != old_block)) { - return true; - } - if (block != NULL) { - cl_core_local_block_set_block(core_local_block, NULL); - cl_block_list_insert_at_tail(&g_log.dirty_block_list, block); - } - block = cl_allocate_block(); - if (block == NULL) { - return false; - } - cl_core_local_block_set_block(core_local_block, block); - cl_block_enable_access(block); - return true; -} - -static cl_block* cl_get_block(void* record) { - uintptr_t p = (uintptr_t)((char*)record - g_log.buffer); - uintptr_t index = p >> CENSUS_LOG_2_MAX_RECORD_SIZE; - return &g_log.blocks[index]; -} - -// Gets the next block to read and tries to free 'prev' block (if not NULL). -// Returns NULL if reached the end. -static cl_block* cl_next_block_to_read(cl_block* prev) { - cl_block* block = NULL; - if (g_log.read_iterator_state == g_log.num_cores) { - // We are traversing dirty list; find the next dirty block. - if (prev != NULL) { - // Try to free the previous block if there is no unread data. This - // block - // may have unread data if previously incomplete record completed - // between - // read_next() calls. - block = prev->link.next->block; - if (cl_block_try_disable_access(prev, 0 /* do not discard data */)) { - cl_block_list_remove(&g_log.dirty_block_list, prev); - cl_block_list_insert_at_head(&g_log.free_block_list, prev); - } - } else { - block = cl_block_list_head(&g_log.dirty_block_list); - } - if (block != NULL) { - return block; - } - // We are done with the dirty list; moving on to core-local blocks. - } - while (g_log.read_iterator_state > 0) { - g_log.read_iterator_state--; - block = cl_core_local_block_get_block( - &g_log.core_local_blocks[g_log.read_iterator_state]); - if (block != NULL) { - return block; - } - } - return NULL; -} - -#define CL_LOG_2_MB 20 // 2^20 = 1MB - -// External functions: primary stats_log interface -void census_log_initialize(size_t size_in_mb, int discard_old_records) { - // Check cacheline alignment. - GPR_ASSERT(sizeof(cl_block) % GPR_CACHELINE_SIZE == 0); - GPR_ASSERT(sizeof(cl_core_local_block) % GPR_CACHELINE_SIZE == 0); - GPR_ASSERT(!g_log.initialized); - g_log.discard_old_records = discard_old_records; - g_log.num_cores = gpr_cpu_num_cores(); - // Ensure that we will not get any overflow in calaculating num_blocks - GPR_ASSERT(CL_LOG_2_MB >= CENSUS_LOG_2_MAX_RECORD_SIZE); - GPR_ASSERT(size_in_mb < 1000); - // Ensure at least 2x as many blocks as there are cores. - g_log.num_blocks = - (uint32_t)GPR_MAX(2 * g_log.num_cores, (size_in_mb << CL_LOG_2_MB) >> - CENSUS_LOG_2_MAX_RECORD_SIZE); - gpr_mu_init(&g_log.lock); - g_log.read_iterator_state = 0; - g_log.block_being_read = NULL; - g_log.core_local_blocks = (cl_core_local_block*)gpr_malloc_aligned( - g_log.num_cores * sizeof(cl_core_local_block), GPR_CACHELINE_SIZE_LOG); - memset(g_log.core_local_blocks, 0, - g_log.num_cores * sizeof(cl_core_local_block)); - g_log.blocks = (cl_block*)gpr_malloc_aligned( - g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); - memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); - g_log.buffer = - (char*)gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); - memset(g_log.buffer, 0, g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); - cl_block_list_initialize(&g_log.free_block_list); - cl_block_list_initialize(&g_log.dirty_block_list); - for (uint32_t i = 0; i < g_log.num_blocks; ++i) { - cl_block* block = g_log.blocks + i; - cl_block_initialize(block, g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * i)); - cl_block_try_disable_access(block, 1 /* discard data */); - cl_block_list_insert_at_tail(&g_log.free_block_list, block); - } - gpr_atm_rel_store(&g_log.out_of_space_count, 0); - g_log.initialized = 1; -} - -void census_log_shutdown(void) { - GPR_ASSERT(g_log.initialized); - gpr_mu_destroy(&g_log.lock); - gpr_free_aligned(g_log.core_local_blocks); - g_log.core_local_blocks = NULL; - gpr_free_aligned(g_log.blocks); - g_log.blocks = NULL; - gpr_free(g_log.buffer); - g_log.buffer = NULL; - g_log.initialized = 0; -} - -void* census_log_start_write(size_t size) { - // Used to bound number of times block allocation is attempted. - GPR_ASSERT(size > 0); - GPR_ASSERT(g_log.initialized); - if (size > CENSUS_LOG_MAX_RECORD_SIZE) { - return NULL; - } - uint32_t attempts_remaining = g_log.num_blocks; - uint32_t core_id = gpr_cpu_current_cpu(); - do { - void* record = NULL; - cl_block* block = - cl_core_local_block_get_block(&g_log.core_local_blocks[core_id]); - if (block && (record = cl_block_start_write(block, size))) { - return record; - } - // Need to allocate a new block. We are here if: - // - No block associated with the core OR - // - Write in-progress on the block OR - // - block is out of space - gpr_mu_lock(&g_log.lock); - bool allocated = cl_allocate_core_local_block(core_id, block); - gpr_mu_unlock(&g_log.lock); - if (!allocated) { - gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); - return NULL; - } - } while (attempts_remaining--); - // Give up. - gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); - return NULL; -} - -void census_log_end_write(void* record, size_t bytes_written) { - GPR_ASSERT(g_log.initialized); - cl_block_end_write(cl_get_block(record), bytes_written); -} - -void census_log_init_reader(void) { - GPR_ASSERT(g_log.initialized); - gpr_mu_lock(&g_log.lock); - // If a block is locked for reading unlock it. - if (g_log.block_being_read != NULL) { - cl_block_end_read(g_log.block_being_read); - g_log.block_being_read = NULL; - } - g_log.read_iterator_state = g_log.num_cores; - gpr_mu_unlock(&g_log.lock); -} - -const void* census_log_read_next(size_t* bytes_available) { - GPR_ASSERT(g_log.initialized); - gpr_mu_lock(&g_log.lock); - if (g_log.block_being_read != NULL) { - cl_block_end_read(g_log.block_being_read); - } - do { - g_log.block_being_read = cl_next_block_to_read(g_log.block_being_read); - if (g_log.block_being_read != NULL) { - void* record = - cl_block_start_read(g_log.block_being_read, bytes_available); - if (record != NULL) { - gpr_mu_unlock(&g_log.lock); - return record; - } - } - } while (g_log.block_being_read != NULL); - gpr_mu_unlock(&g_log.lock); - return NULL; -} - -size_t census_log_remaining_space(void) { - GPR_ASSERT(g_log.initialized); - size_t space = 0; - gpr_mu_lock(&g_log.lock); - if (g_log.discard_old_records) { - // Remaining space is not meaningful; just return the entire log space. - space = g_log.num_blocks << CENSUS_LOG_2_MAX_RECORD_SIZE; - } else { - GPR_ASSERT(g_log.free_block_list.count >= 0); - space = (size_t)g_log.free_block_list.count * CENSUS_LOG_MAX_RECORD_SIZE; - } - gpr_mu_unlock(&g_log.lock); - return space; -} - -int64_t census_log_out_of_space_count(void) { - GPR_ASSERT(g_log.initialized); - return gpr_atm_acq_load(&g_log.out_of_space_count); -} diff --git a/src/core/ext/census/mlog.cc b/src/core/ext/census/mlog.cc new file mode 100644 index 0000000000..4b8c8466b3 --- /dev/null +++ b/src/core/ext/census/mlog.cc @@ -0,0 +1,586 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// Implements an efficient in-memory log, optimized for multiple writers and +// a single reader. Available log space is divided up in blocks of +// CENSUS_LOG_2_MAX_RECORD_SIZE bytes. A block can be in one of the following +// three data structures: +// - Free blocks (free_block_list) +// - Blocks with unread data (dirty_block_list) +// - Blocks currently attached to cores (core_local_blocks[]) +// +// census_log_start_write() moves a block from core_local_blocks[] to the end of +// dirty_block_list when block: +// - is out-of-space OR +// - has an incomplete record (an incomplete record occurs when a thread calls +// census_log_start_write() and is context-switched before calling +// census_log_end_write() +// So, blocks in dirty_block_list are ordered, from oldest to newest, by the +// time when block is detached from the core. +// +// census_log_read_next() first iterates over dirty_block_list and then +// core_local_blocks[]. It moves completely read blocks from dirty_block_list +// to free_block_list. Blocks in core_local_blocks[] are not freed, even when +// completely read. +// +// If the log is configured to discard old records and free_block_list is empty, +// census_log_start_write() iterates over dirty_block_list to allocate a +// new block. It moves the oldest available block (no pending read/write) to +// core_local_blocks[]. +// +// core_local_block_struct is used to implement a map from core id to the block +// associated with that core. This mapping is advisory. It is possible that the +// block returned by this mapping is no longer associated with that core. This +// mapping is updated, lazily, by census_log_start_write(). +// +// Locking in block struct: +// +// Exclusive g_log.lock must be held before calling any functions operating on +// block structs except census_log_start_write() and census_log_end_write(). +// +// Writes to a block are serialized via writer_lock. census_log_start_write() +// acquires this lock and census_log_end_write() releases it. On failure to +// acquire the lock, writer allocates a new block for the current core and +// updates core_local_block accordingly. +// +// Simultaneous read and write access is allowed. Readers can safely read up to +// committed bytes (bytes_committed). +// +// reader_lock protects the block, currently being read, from getting recycled. +// start_read() acquires reader_lock and end_read() releases the lock. +// +// Read/write access to a block is disabled via try_disable_access(). It returns +// with both writer_lock and reader_lock held. These locks are subsequently +// released by enable_access() to enable access to the block. +// +// A note on naming: Most function/struct names are prepended by cl_ +// (shorthand for census_log). Further, functions that manipulate structures +// include the name of the structure, which will be passed as the first +// argument. E.g. cl_block_initialize() will initialize a cl_block. + +#include "src/core/ext/census/mlog.h" +#include +#include +#include +#include +#include +#include +#include +#include + +// End of platform specific code + +typedef struct census_log_block_list_struct { + struct census_log_block_list_struct* next; + struct census_log_block_list_struct* prev; + struct census_log_block* block; +} cl_block_list_struct; + +typedef struct census_log_block { + // Pointer to underlying buffer. + char* buffer; + gpr_atm writer_lock; + gpr_atm reader_lock; + // Keeps completely written bytes. Declared atomic because accessed + // simultaneously by reader and writer. + gpr_atm bytes_committed; + // Bytes already read. + size_t bytes_read; + // Links for list. + cl_block_list_struct link; +// We want this structure to be cacheline aligned. We assume the following +// sizes for the various parts on 32/64bit systems: +// type 32b size 64b size +// char* 4 8 +// 3x gpr_atm 12 24 +// size_t 4 8 +// cl_block_list_struct 12 24 +// TOTAL 32 64 +// +// Depending on the size of our cacheline and the architecture, we +// selectively add char buffering to this structure. The size is checked +// via assert in census_log_initialize(). +#if defined(GPR_ARCH_64) +#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 64) +#else +#if defined(GPR_ARCH_32) +#define CL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 32) +#else +#error "Unknown architecture" +#endif +#endif +#if CL_BLOCK_PAD_SIZE > 0 + char padding[CL_BLOCK_PAD_SIZE]; +#endif +} cl_block; + +// A list of cl_blocks, doubly-linked through cl_block::link. +typedef struct census_log_block_list { + int32_t count; // Number of items in list. + cl_block_list_struct ht; // head/tail of linked list. +} cl_block_list; + +// Cacheline aligned block pointers to avoid false sharing. Block pointer must +// be initialized via set_block(), before calling other functions +typedef struct census_log_core_local_block { + gpr_atm block; +// Ensure cachline alignment: we assume sizeof(gpr_atm) == 4 or 8 +#if defined(GPR_ARCH_64) +#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 8) +#else +#if defined(GPR_ARCH_32) +#define CL_CORE_LOCAL_BLOCK_PAD_SIZE (GPR_CACHELINE_SIZE - 4) +#else +#error "Unknown architecture" +#endif +#endif +#if CL_CORE_LOCAL_BLOCK_PAD_SIZE > 0 + char padding[CL_CORE_LOCAL_BLOCK_PAD_SIZE]; +#endif +} cl_core_local_block; + +struct census_log { + int discard_old_records; + // Number of cores (aka hardware-contexts) + unsigned num_cores; + // number of CENSUS_LOG_2_MAX_RECORD_SIZE blocks in log + uint32_t num_blocks; + cl_block* blocks; // Block metadata. + cl_core_local_block* core_local_blocks; // Keeps core to block mappings. + gpr_mu lock; + int initialized; // has log been initialized? + // Keeps the state of the reader iterator. A value of 0 indicates that + // iterator has reached the end. census_log_init_reader() resets the value + // to num_core to restart iteration. + uint32_t read_iterator_state; + // Points to the block being read. If non-NULL, the block is locked for + // reading(block_being_read_->reader_lock is held). + cl_block* block_being_read; + char* buffer; + cl_block_list free_block_list; + cl_block_list dirty_block_list; + gpr_atm out_of_space_count; +}; + +// Single internal log. +static struct census_log g_log; + +// Functions that operate on an atomic memory location used as a lock. + +// Returns non-zero if lock is acquired. +static int cl_try_lock(gpr_atm* lock) { return gpr_atm_acq_cas(lock, 0, 1); } + +static void cl_unlock(gpr_atm* lock) { gpr_atm_rel_store(lock, 0); } + +// Functions that operate on cl_core_local_block's. + +static void cl_core_local_block_set_block(cl_core_local_block* clb, + cl_block* block) { + gpr_atm_rel_store(&clb->block, (gpr_atm)block); +} + +static cl_block* cl_core_local_block_get_block(cl_core_local_block* clb) { + return (cl_block*)gpr_atm_acq_load(&clb->block); +} + +// Functions that operate on cl_block_list_struct's. + +static void cl_block_list_struct_initialize(cl_block_list_struct* bls, + cl_block* block) { + bls->next = bls->prev = bls; + bls->block = block; +} + +// Functions that operate on cl_block_list's. + +static void cl_block_list_initialize(cl_block_list* list) { + list->count = 0; + cl_block_list_struct_initialize(&list->ht, NULL); +} + +// Returns head of *this, or NULL if empty. +static cl_block* cl_block_list_head(cl_block_list* list) { + return list->ht.next->block; +} + +// Insert element *e after *pos. +static void cl_block_list_insert(cl_block_list* list, cl_block_list_struct* pos, + cl_block_list_struct* e) { + list->count++; + e->next = pos->next; + e->prev = pos; + e->next->prev = e; + e->prev->next = e; +} + +// Insert block at the head of the list +static void cl_block_list_insert_at_head(cl_block_list* list, cl_block* block) { + cl_block_list_insert(list, &list->ht, &block->link); +} + +// Insert block at the tail of the list. +static void cl_block_list_insert_at_tail(cl_block_list* list, cl_block* block) { + cl_block_list_insert(list, list->ht.prev, &block->link); +} + +// Removes block *b. Requires *b be in the list. +static void cl_block_list_remove(cl_block_list* list, cl_block* b) { + list->count--; + b->link.next->prev = b->link.prev; + b->link.prev->next = b->link.next; +} + +// Functions that operate on cl_block's + +static void cl_block_initialize(cl_block* block, char* buffer) { + block->buffer = buffer; + gpr_atm_rel_store(&block->writer_lock, 0); + gpr_atm_rel_store(&block->reader_lock, 0); + gpr_atm_rel_store(&block->bytes_committed, 0); + block->bytes_read = 0; + cl_block_list_struct_initialize(&block->link, block); +} + +// Guards against exposing partially written buffer to the reader. +static void cl_block_set_bytes_committed(cl_block* block, + size_t bytes_committed) { + gpr_atm_rel_store(&block->bytes_committed, (gpr_atm)bytes_committed); +} + +static size_t cl_block_get_bytes_committed(cl_block* block) { + return (size_t)gpr_atm_acq_load(&block->bytes_committed); +} + +// Tries to disable future read/write access to this block. Succeeds if: +// - no in-progress write AND +// - no in-progress read AND +// - 'discard_data' set to true OR no unread data +// On success, clears the block state and returns with writer_lock_ and +// reader_lock_ held. These locks are released by a subsequent +// cl_block_access_enable() call. +static bool cl_block_try_disable_access(cl_block* block, int discard_data) { + if (!cl_try_lock(&block->writer_lock)) { + return false; + } + if (!cl_try_lock(&block->reader_lock)) { + cl_unlock(&block->writer_lock); + return false; + } + if (!discard_data && + (block->bytes_read != cl_block_get_bytes_committed(block))) { + cl_unlock(&block->reader_lock); + cl_unlock(&block->writer_lock); + return false; + } + cl_block_set_bytes_committed(block, 0); + block->bytes_read = 0; + return true; +} + +static void cl_block_enable_access(cl_block* block) { + cl_unlock(&block->reader_lock); + cl_unlock(&block->writer_lock); +} + +// Returns with writer_lock held. +static void* cl_block_start_write(cl_block* block, size_t size) { + if (!cl_try_lock(&block->writer_lock)) { + return NULL; + } + size_t bytes_committed = cl_block_get_bytes_committed(block); + if (bytes_committed + size > CENSUS_LOG_MAX_RECORD_SIZE) { + cl_unlock(&block->writer_lock); + return NULL; + } + return block->buffer + bytes_committed; +} + +// Releases writer_lock and increments committed bytes by 'bytes_written'. +// 'bytes_written' must be <= 'size' specified in the corresponding +// StartWrite() call. This function is thread-safe. +static void cl_block_end_write(cl_block* block, size_t bytes_written) { + cl_block_set_bytes_committed( + block, cl_block_get_bytes_committed(block) + bytes_written); + cl_unlock(&block->writer_lock); +} + +// Returns a pointer to the first unread byte in buffer. The number of bytes +// available are returned in 'bytes_available'. Acquires reader lock that is +// released by a subsequent cl_block_end_read() call. Returns NULL if: +// - read in progress +// - no data available +static void* cl_block_start_read(cl_block* block, size_t* bytes_available) { + if (!cl_try_lock(&block->reader_lock)) { + return NULL; + } + // bytes_committed may change from under us. Use bytes_available to update + // bytes_read below. + size_t bytes_committed = cl_block_get_bytes_committed(block); + GPR_ASSERT(bytes_committed >= block->bytes_read); + *bytes_available = bytes_committed - block->bytes_read; + if (*bytes_available == 0) { + cl_unlock(&block->reader_lock); + return NULL; + } + void* record = block->buffer + block->bytes_read; + block->bytes_read += *bytes_available; + return record; +} + +static void cl_block_end_read(cl_block* block) { + cl_unlock(&block->reader_lock); +} + +// Internal functions operating on g_log + +// Allocates a new free block (or recycles an available dirty block if log is +// configured to discard old records). Returns NULL if out-of-space. +static cl_block* cl_allocate_block(void) { + cl_block* block = cl_block_list_head(&g_log.free_block_list); + if (block != NULL) { + cl_block_list_remove(&g_log.free_block_list, block); + return block; + } + if (!g_log.discard_old_records) { + // No free block and log is configured to keep old records. + return NULL; + } + // Recycle dirty block. Start from the oldest. + for (block = cl_block_list_head(&g_log.dirty_block_list); block != NULL; + block = block->link.next->block) { + if (cl_block_try_disable_access(block, 1 /* discard data */)) { + cl_block_list_remove(&g_log.dirty_block_list, block); + return block; + } + } + return NULL; +} + +// Allocates a new block and updates core id => block mapping. 'old_block' +// points to the block that the caller thinks is attached to +// 'core_id'. 'old_block' may be NULL. Returns true if: +// - allocated a new block OR +// - 'core_id' => 'old_block' mapping changed (another thread allocated a +// block before lock was acquired). +static bool cl_allocate_core_local_block(uint32_t core_id, + cl_block* old_block) { + // Now that we have the lock, check if core-local mapping has changed. + cl_core_local_block* core_local_block = &g_log.core_local_blocks[core_id]; + cl_block* block = cl_core_local_block_get_block(core_local_block); + if ((block != NULL) && (block != old_block)) { + return true; + } + if (block != NULL) { + cl_core_local_block_set_block(core_local_block, NULL); + cl_block_list_insert_at_tail(&g_log.dirty_block_list, block); + } + block = cl_allocate_block(); + if (block == NULL) { + return false; + } + cl_core_local_block_set_block(core_local_block, block); + cl_block_enable_access(block); + return true; +} + +static cl_block* cl_get_block(void* record) { + uintptr_t p = (uintptr_t)((char*)record - g_log.buffer); + uintptr_t index = p >> CENSUS_LOG_2_MAX_RECORD_SIZE; + return &g_log.blocks[index]; +} + +// Gets the next block to read and tries to free 'prev' block (if not NULL). +// Returns NULL if reached the end. +static cl_block* cl_next_block_to_read(cl_block* prev) { + cl_block* block = NULL; + if (g_log.read_iterator_state == g_log.num_cores) { + // We are traversing dirty list; find the next dirty block. + if (prev != NULL) { + // Try to free the previous block if there is no unread data. This + // block + // may have unread data if previously incomplete record completed + // between + // read_next() calls. + block = prev->link.next->block; + if (cl_block_try_disable_access(prev, 0 /* do not discard data */)) { + cl_block_list_remove(&g_log.dirty_block_list, prev); + cl_block_list_insert_at_head(&g_log.free_block_list, prev); + } + } else { + block = cl_block_list_head(&g_log.dirty_block_list); + } + if (block != NULL) { + return block; + } + // We are done with the dirty list; moving on to core-local blocks. + } + while (g_log.read_iterator_state > 0) { + g_log.read_iterator_state--; + block = cl_core_local_block_get_block( + &g_log.core_local_blocks[g_log.read_iterator_state]); + if (block != NULL) { + return block; + } + } + return NULL; +} + +#define CL_LOG_2_MB 20 // 2^20 = 1MB + +// External functions: primary stats_log interface +void census_log_initialize(size_t size_in_mb, int discard_old_records) { + // Check cacheline alignment. + GPR_ASSERT(sizeof(cl_block) % GPR_CACHELINE_SIZE == 0); + GPR_ASSERT(sizeof(cl_core_local_block) % GPR_CACHELINE_SIZE == 0); + GPR_ASSERT(!g_log.initialized); + g_log.discard_old_records = discard_old_records; + g_log.num_cores = gpr_cpu_num_cores(); + // Ensure that we will not get any overflow in calaculating num_blocks + GPR_ASSERT(CL_LOG_2_MB >= CENSUS_LOG_2_MAX_RECORD_SIZE); + GPR_ASSERT(size_in_mb < 1000); + // Ensure at least 2x as many blocks as there are cores. + g_log.num_blocks = + (uint32_t)GPR_MAX(2 * g_log.num_cores, (size_in_mb << CL_LOG_2_MB) >> + CENSUS_LOG_2_MAX_RECORD_SIZE); + gpr_mu_init(&g_log.lock); + g_log.read_iterator_state = 0; + g_log.block_being_read = NULL; + g_log.core_local_blocks = (cl_core_local_block*)gpr_malloc_aligned( + g_log.num_cores * sizeof(cl_core_local_block), GPR_CACHELINE_SIZE_LOG); + memset(g_log.core_local_blocks, 0, + g_log.num_cores * sizeof(cl_core_local_block)); + g_log.blocks = (cl_block*)gpr_malloc_aligned( + g_log.num_blocks * sizeof(cl_block), GPR_CACHELINE_SIZE_LOG); + memset(g_log.blocks, 0, g_log.num_blocks * sizeof(cl_block)); + g_log.buffer = + (char*)gpr_malloc(g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + memset(g_log.buffer, 0, g_log.num_blocks * CENSUS_LOG_MAX_RECORD_SIZE); + cl_block_list_initialize(&g_log.free_block_list); + cl_block_list_initialize(&g_log.dirty_block_list); + for (uint32_t i = 0; i < g_log.num_blocks; ++i) { + cl_block* block = g_log.blocks + i; + cl_block_initialize(block, g_log.buffer + (CENSUS_LOG_MAX_RECORD_SIZE * i)); + cl_block_try_disable_access(block, 1 /* discard data */); + cl_block_list_insert_at_tail(&g_log.free_block_list, block); + } + gpr_atm_rel_store(&g_log.out_of_space_count, 0); + g_log.initialized = 1; +} + +void census_log_shutdown(void) { + GPR_ASSERT(g_log.initialized); + gpr_mu_destroy(&g_log.lock); + gpr_free_aligned(g_log.core_local_blocks); + g_log.core_local_blocks = NULL; + gpr_free_aligned(g_log.blocks); + g_log.blocks = NULL; + gpr_free(g_log.buffer); + g_log.buffer = NULL; + g_log.initialized = 0; +} + +void* census_log_start_write(size_t size) { + // Used to bound number of times block allocation is attempted. + GPR_ASSERT(size > 0); + GPR_ASSERT(g_log.initialized); + if (size > CENSUS_LOG_MAX_RECORD_SIZE) { + return NULL; + } + uint32_t attempts_remaining = g_log.num_blocks; + uint32_t core_id = gpr_cpu_current_cpu(); + do { + void* record = NULL; + cl_block* block = + cl_core_local_block_get_block(&g_log.core_local_blocks[core_id]); + if (block && (record = cl_block_start_write(block, size))) { + return record; + } + // Need to allocate a new block. We are here if: + // - No block associated with the core OR + // - Write in-progress on the block OR + // - block is out of space + gpr_mu_lock(&g_log.lock); + bool allocated = cl_allocate_core_local_block(core_id, block); + gpr_mu_unlock(&g_log.lock); + if (!allocated) { + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; + } + } while (attempts_remaining--); + // Give up. + gpr_atm_no_barrier_fetch_add(&g_log.out_of_space_count, 1); + return NULL; +} + +void census_log_end_write(void* record, size_t bytes_written) { + GPR_ASSERT(g_log.initialized); + cl_block_end_write(cl_get_block(record), bytes_written); +} + +void census_log_init_reader(void) { + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + // If a block is locked for reading unlock it. + if (g_log.block_being_read != NULL) { + cl_block_end_read(g_log.block_being_read); + g_log.block_being_read = NULL; + } + g_log.read_iterator_state = g_log.num_cores; + gpr_mu_unlock(&g_log.lock); +} + +const void* census_log_read_next(size_t* bytes_available) { + GPR_ASSERT(g_log.initialized); + gpr_mu_lock(&g_log.lock); + if (g_log.block_being_read != NULL) { + cl_block_end_read(g_log.block_being_read); + } + do { + g_log.block_being_read = cl_next_block_to_read(g_log.block_being_read); + if (g_log.block_being_read != NULL) { + void* record = + cl_block_start_read(g_log.block_being_read, bytes_available); + if (record != NULL) { + gpr_mu_unlock(&g_log.lock); + return record; + } + } + } while (g_log.block_being_read != NULL); + gpr_mu_unlock(&g_log.lock); + return NULL; +} + +size_t census_log_remaining_space(void) { + GPR_ASSERT(g_log.initialized); + size_t space = 0; + gpr_mu_lock(&g_log.lock); + if (g_log.discard_old_records) { + // Remaining space is not meaningful; just return the entire log space. + space = g_log.num_blocks << CENSUS_LOG_2_MAX_RECORD_SIZE; + } else { + GPR_ASSERT(g_log.free_block_list.count >= 0); + space = (size_t)g_log.free_block_list.count * CENSUS_LOG_MAX_RECORD_SIZE; + } + gpr_mu_unlock(&g_log.lock); + return space; +} + +int64_t census_log_out_of_space_count(void) { + GPR_ASSERT(g_log.initialized); + return gpr_atm_acq_load(&g_log.out_of_space_count); +} diff --git a/src/core/ext/census/operation.c b/src/core/ext/census/operation.c deleted file mode 100644 index be88ac74e6..0000000000 --- a/src/core/ext/census/operation.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -/* TODO(aveitch): These are all placeholder implementations. */ - -census_timestamp census_start_rpc_op_timestamp(void) { - census_timestamp ct; - /* TODO(aveitch): assumes gpr_timespec implementation of census_timestamp. */ - ct.ts = gpr_now(GPR_CLOCK_MONOTONIC); - return ct; -} - -census_context *census_start_client_rpc_op( - const census_context *context, int64_t rpc_name_id, - const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, - const census_timestamp *start_time) { - return NULL; -} - -census_context *census_start_server_rpc_op( - const char *buffer, int64_t rpc_name_id, - const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, - census_timestamp *start_time) { - return NULL; -} - -census_context *census_start_op(census_context *context, const char *family, - const char *name, int trace_mask) { - return NULL; -} - -void census_end_op(census_context *context, int status) {} diff --git a/src/core/ext/census/operation.cc b/src/core/ext/census/operation.cc new file mode 100644 index 0000000000..be88ac74e6 --- /dev/null +++ b/src/core/ext/census/operation.cc @@ -0,0 +1,48 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +/* TODO(aveitch): These are all placeholder implementations. */ + +census_timestamp census_start_rpc_op_timestamp(void) { + census_timestamp ct; + /* TODO(aveitch): assumes gpr_timespec implementation of census_timestamp. */ + ct.ts = gpr_now(GPR_CLOCK_MONOTONIC); + return ct; +} + +census_context *census_start_client_rpc_op( + const census_context *context, int64_t rpc_name_id, + const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, + const census_timestamp *start_time) { + return NULL; +} + +census_context *census_start_server_rpc_op( + const char *buffer, int64_t rpc_name_id, + const census_rpc_name_info *rpc_name_info, const char *peer, int trace_mask, + census_timestamp *start_time) { + return NULL; +} + +census_context *census_start_op(census_context *context, const char *family, + const char *name, int trace_mask) { + return NULL; +} + +void census_end_op(census_context *context, int status) {} diff --git a/src/core/ext/census/placeholders.c b/src/core/ext/census/placeholders.c deleted file mode 100644 index bed9837ee3..0000000000 --- a/src/core/ext/census/placeholders.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -/* Placeholders for the pending APIs */ - -int census_get_trace_record(census_trace_record *trace_record) { - (void)trace_record; - abort(); -} - -void census_record_values(census_context *context, census_value *values, - size_t nvalues) { - (void)context; - (void)values; - (void)nvalues; - abort(); -} - -void census_set_rpc_client_peer(census_context *context, const char *peer) { - (void)context; - (void)peer; - abort(); -} - -void census_trace_scan_end() { abort(); } - -int census_trace_scan_start(int consume) { - (void)consume; - abort(); -} diff --git a/src/core/ext/census/placeholders.cc b/src/core/ext/census/placeholders.cc new file mode 100644 index 0000000000..bed9837ee3 --- /dev/null +++ b/src/core/ext/census/placeholders.cc @@ -0,0 +1,49 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +/* Placeholders for the pending APIs */ + +int census_get_trace_record(census_trace_record *trace_record) { + (void)trace_record; + abort(); +} + +void census_record_values(census_context *context, census_value *values, + size_t nvalues) { + (void)context; + (void)values; + (void)nvalues; + abort(); +} + +void census_set_rpc_client_peer(census_context *context, const char *peer) { + (void)context; + (void)peer; + abort(); +} + +void census_trace_scan_end() { abort(); } + +int census_trace_scan_start(int consume) { + (void)consume; + abort(); +} diff --git a/src/core/ext/census/resource.c b/src/core/ext/census/resource.c deleted file mode 100644 index 44a887231c..0000000000 --- a/src/core/ext/census/resource.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/resource.h" -#include "third_party/nanopb/pb_decode.h" - -#include -#include -#include -#include - -#include -#include - -// Protect local resource data structures. -static gpr_mu resource_lock; - -// Deleteing and creating resources are relatively rare events, and should not -// be done in the critical path of performance sensitive code. We record -// current resource id's used in a simple array, and just search it each time -// we need to assign a new id, or look up a resource. -static resource **resources = NULL; - -// Number of entries in *resources -static size_t n_resources = 0; - -// Number of defined resources -static size_t n_defined_resources = 0; - -void initialize_resources(void) { - gpr_mu_init(&resource_lock); - gpr_mu_lock(&resource_lock); - GPR_ASSERT(resources == NULL && n_resources == 0 && n_defined_resources == 0); - gpr_mu_unlock(&resource_lock); -} - -// Delete a resource given it's ID. The ID must be a valid resource ID. Must be -// called with resource_lock held. -static void delete_resource_locked(size_t rid) { - GPR_ASSERT(resources[rid] != NULL); - gpr_free(resources[rid]->name); - gpr_free(resources[rid]->description); - gpr_free(resources[rid]->numerators); - gpr_free(resources[rid]->denominators); - gpr_free(resources[rid]); - resources[rid] = NULL; - n_defined_resources--; -} - -void shutdown_resources(void) { - gpr_mu_lock(&resource_lock); - for (size_t i = 0; i < n_resources; i++) { - if (resources[i] != NULL) { - delete_resource_locked(i); - } - } - GPR_ASSERT(n_defined_resources == 0); - gpr_free(resources); - resources = NULL; - n_resources = 0; - gpr_mu_unlock(&resource_lock); -} - -// Check the contents of string fields in a resource proto. -static bool validate_string(pb_istream_t *stream, const pb_field_t *field, - void **arg) { - resource *vresource = (resource *)*arg; - switch (field->tag) { - case google_census_Resource_name_tag: - // Name must have at least one character - if (stream->bytes_left == 0) { - gpr_log(GPR_INFO, "Zero-length Resource name."); - return false; - } - vresource->name = (char *)gpr_malloc(stream->bytes_left + 1); - vresource->name[stream->bytes_left] = '\0'; - if (!pb_read(stream, (uint8_t *)vresource->name, stream->bytes_left)) { - return false; - } - // Can't have same name as an existing resource. - for (size_t i = 0; i < n_resources; i++) { - resource *compare = resources[i]; - if (compare == vresource || compare == NULL) continue; - if (strcmp(compare->name, vresource->name) == 0) { - gpr_log(GPR_INFO, "Duplicate Resource name %s.", vresource->name); - return false; - } - } - break; - case google_census_Resource_description_tag: - if (stream->bytes_left == 0) { - return true; - } - vresource->description = (char *)gpr_malloc(stream->bytes_left + 1); - vresource->description[stream->bytes_left] = '\0'; - if (!pb_read(stream, (uint8_t *)vresource->description, - stream->bytes_left)) { - return false; - } - break; - default: - // No other string fields in Resource. Print warning and skip. - gpr_log(GPR_INFO, "Unknown string field type in Resource protobuf."); - if (!pb_read(stream, NULL, stream->bytes_left)) { - return false; - } - break; - } - return true; -} - -// Decode numerators/denominators in a stream. The `count` and `bup` -// (BasicUnit pointer) are pointers to the approriate fields in a resource -// struct. -static bool validate_units_helper(pb_istream_t *stream, int *count, - google_census_Resource_BasicUnit **bup) { - while (stream->bytes_left) { - (*count)++; - // Have to allocate a new array of values. Normal case is 0 or 1, so - // this should normally not be an issue. - google_census_Resource_BasicUnit *new_bup = - (google_census_Resource_BasicUnit *)gpr_malloc( - (size_t)*count * sizeof(google_census_Resource_BasicUnit)); - if (*count != 1) { - memcpy(new_bup, *bup, - (size_t)(*count - 1) * sizeof(google_census_Resource_BasicUnit)); - gpr_free(*bup); - } - *bup = new_bup; - uint64_t value; - if (!pb_decode_varint(stream, &value)) { - return false; - } - *(*bup + *count - 1) = (google_census_Resource_BasicUnit)value; - } - return true; -} - -// Validate units field of a Resource proto. -static bool validate_units(pb_istream_t *stream, const pb_field_t *field, - void **arg) { - resource *vresource = (resource *)(*arg); - switch (field->tag) { - case google_census_Resource_MeasurementUnit_numerator_tag: - return validate_units_helper(stream, &vresource->n_numerators, - &vresource->numerators); - break; - case google_census_Resource_MeasurementUnit_denominator_tag: - return validate_units_helper(stream, &vresource->n_denominators, - &vresource->denominators); - break; - default: - gpr_log(GPR_ERROR, "Unknown field type."); - return false; - break; - } - return true; -} - -// Validate the contents of a Resource proto. `id` is the intended resource id. -static bool validate_resource_pb(const uint8_t *resource_pb, - size_t resource_pb_size, size_t id) { - GPR_ASSERT(id < n_resources); - if (resource_pb == NULL) { - return false; - } - google_census_Resource vresource; - vresource.name.funcs.decode = &validate_string; - vresource.name.arg = resources[id]; - vresource.description.funcs.decode = &validate_string; - vresource.description.arg = resources[id]; - vresource.unit.numerator.funcs.decode = &validate_units; - vresource.unit.numerator.arg = resources[id]; - vresource.unit.denominator.funcs.decode = &validate_units; - vresource.unit.denominator.arg = resources[id]; - - pb_istream_t stream = - pb_istream_from_buffer((uint8_t *)resource_pb, resource_pb_size); - if (!pb_decode(&stream, google_census_Resource_fields, &vresource)) { - return false; - } - // A Resource must have a name, a unit, with at least one numerator. - return (resources[id]->name != NULL && vresource.has_unit && - resources[id]->n_numerators > 0); -} - -// Allocate a blank resource, and return associated ID. Must be called with -// resource_lock held. -size_t allocate_resource(void) { - // use next_id to optimize expected placement of next new resource. - static size_t next_id = 0; - size_t id = n_resources; // resource ID - initialize to invalid value. - // Expand resources if needed. - if (n_resources == n_defined_resources) { - size_t new_n_resources = n_resources ? n_resources * 2 : 2; - resource **new_resources = - (resource **)gpr_malloc(new_n_resources * sizeof(resource *)); - if (n_resources != 0) { - memcpy(new_resources, resources, n_resources * sizeof(resource *)); - } - memset(new_resources + n_resources, 0, - (new_n_resources - n_resources) * sizeof(resource *)); - gpr_free(resources); - resources = new_resources; - n_resources = new_n_resources; - id = n_defined_resources; - } else { - GPR_ASSERT(n_defined_resources < n_resources); - // Find a free id. - for (size_t base = 0; base < n_resources; base++) { - id = (next_id + base) % n_resources; - if (resources[id] == NULL) break; - } - } - GPR_ASSERT(id < n_resources && resources[id] == NULL); - resources[id] = (resource *)gpr_malloc(sizeof(resource)); - memset(resources[id], 0, sizeof(resource)); - n_defined_resources++; - next_id = (id + 1) % n_resources; - return id; -} - -int32_t census_define_resource(const uint8_t *resource_pb, - size_t resource_pb_size) { - if (resource_pb == NULL) { - return -1; - } - gpr_mu_lock(&resource_lock); - size_t id = allocate_resource(); - // Validate pb, extract name. - if (!validate_resource_pb(resource_pb, resource_pb_size, id)) { - delete_resource_locked(id); - gpr_mu_unlock(&resource_lock); - return -1; - } - gpr_mu_unlock(&resource_lock); - return (int32_t)id; -} - -void census_delete_resource(int32_t rid) { - gpr_mu_lock(&resource_lock); - if (rid >= 0 && (size_t)rid < n_resources && resources[rid] != NULL) { - delete_resource_locked((size_t)rid); - } - gpr_mu_unlock(&resource_lock); -} - -int32_t census_resource_id(const char *name) { - gpr_mu_lock(&resource_lock); - for (int32_t id = 0; (size_t)id < n_resources; id++) { - if (resources[id] != NULL && strcmp(resources[id]->name, name) == 0) { - gpr_mu_unlock(&resource_lock); - return id; - } - } - gpr_mu_unlock(&resource_lock); - return -1; -} - -int32_t define_resource(const resource *base) { - GPR_ASSERT(base != NULL && base->name != NULL && base->n_numerators > 0 && - base->numerators != NULL); - gpr_mu_lock(&resource_lock); - size_t id = allocate_resource(); - size_t len = strlen(base->name) + 1; - resources[id]->name = (char *)gpr_malloc(len); - memcpy(resources[id]->name, base->name, len); - if (base->description) { - len = strlen(base->description) + 1; - resources[id]->description = (char *)gpr_malloc(len); - memcpy(resources[id]->description, base->description, len); - } - resources[id]->prefix = base->prefix; - resources[id]->n_numerators = base->n_numerators; - len = (size_t)base->n_numerators * sizeof(*base->numerators); - resources[id]->numerators = - (google_census_Resource_BasicUnit *)gpr_malloc(len); - memcpy(resources[id]->numerators, base->numerators, len); - resources[id]->n_denominators = base->n_denominators; - if (base->n_denominators != 0) { - len = (size_t)base->n_denominators * sizeof(*base->denominators); - resources[id]->denominators = - (google_census_Resource_BasicUnit *)gpr_malloc(len); - memcpy(resources[id]->denominators, base->denominators, len); - } - gpr_mu_unlock(&resource_lock); - return (int32_t)id; -} diff --git a/src/core/ext/census/resource.cc b/src/core/ext/census/resource.cc new file mode 100644 index 0000000000..44a887231c --- /dev/null +++ b/src/core/ext/census/resource.cc @@ -0,0 +1,303 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/resource.h" +#include "third_party/nanopb/pb_decode.h" + +#include +#include +#include +#include + +#include +#include + +// Protect local resource data structures. +static gpr_mu resource_lock; + +// Deleteing and creating resources are relatively rare events, and should not +// be done in the critical path of performance sensitive code. We record +// current resource id's used in a simple array, and just search it each time +// we need to assign a new id, or look up a resource. +static resource **resources = NULL; + +// Number of entries in *resources +static size_t n_resources = 0; + +// Number of defined resources +static size_t n_defined_resources = 0; + +void initialize_resources(void) { + gpr_mu_init(&resource_lock); + gpr_mu_lock(&resource_lock); + GPR_ASSERT(resources == NULL && n_resources == 0 && n_defined_resources == 0); + gpr_mu_unlock(&resource_lock); +} + +// Delete a resource given it's ID. The ID must be a valid resource ID. Must be +// called with resource_lock held. +static void delete_resource_locked(size_t rid) { + GPR_ASSERT(resources[rid] != NULL); + gpr_free(resources[rid]->name); + gpr_free(resources[rid]->description); + gpr_free(resources[rid]->numerators); + gpr_free(resources[rid]->denominators); + gpr_free(resources[rid]); + resources[rid] = NULL; + n_defined_resources--; +} + +void shutdown_resources(void) { + gpr_mu_lock(&resource_lock); + for (size_t i = 0; i < n_resources; i++) { + if (resources[i] != NULL) { + delete_resource_locked(i); + } + } + GPR_ASSERT(n_defined_resources == 0); + gpr_free(resources); + resources = NULL; + n_resources = 0; + gpr_mu_unlock(&resource_lock); +} + +// Check the contents of string fields in a resource proto. +static bool validate_string(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + resource *vresource = (resource *)*arg; + switch (field->tag) { + case google_census_Resource_name_tag: + // Name must have at least one character + if (stream->bytes_left == 0) { + gpr_log(GPR_INFO, "Zero-length Resource name."); + return false; + } + vresource->name = (char *)gpr_malloc(stream->bytes_left + 1); + vresource->name[stream->bytes_left] = '\0'; + if (!pb_read(stream, (uint8_t *)vresource->name, stream->bytes_left)) { + return false; + } + // Can't have same name as an existing resource. + for (size_t i = 0; i < n_resources; i++) { + resource *compare = resources[i]; + if (compare == vresource || compare == NULL) continue; + if (strcmp(compare->name, vresource->name) == 0) { + gpr_log(GPR_INFO, "Duplicate Resource name %s.", vresource->name); + return false; + } + } + break; + case google_census_Resource_description_tag: + if (stream->bytes_left == 0) { + return true; + } + vresource->description = (char *)gpr_malloc(stream->bytes_left + 1); + vresource->description[stream->bytes_left] = '\0'; + if (!pb_read(stream, (uint8_t *)vresource->description, + stream->bytes_left)) { + return false; + } + break; + default: + // No other string fields in Resource. Print warning and skip. + gpr_log(GPR_INFO, "Unknown string field type in Resource protobuf."); + if (!pb_read(stream, NULL, stream->bytes_left)) { + return false; + } + break; + } + return true; +} + +// Decode numerators/denominators in a stream. The `count` and `bup` +// (BasicUnit pointer) are pointers to the approriate fields in a resource +// struct. +static bool validate_units_helper(pb_istream_t *stream, int *count, + google_census_Resource_BasicUnit **bup) { + while (stream->bytes_left) { + (*count)++; + // Have to allocate a new array of values. Normal case is 0 or 1, so + // this should normally not be an issue. + google_census_Resource_BasicUnit *new_bup = + (google_census_Resource_BasicUnit *)gpr_malloc( + (size_t)*count * sizeof(google_census_Resource_BasicUnit)); + if (*count != 1) { + memcpy(new_bup, *bup, + (size_t)(*count - 1) * sizeof(google_census_Resource_BasicUnit)); + gpr_free(*bup); + } + *bup = new_bup; + uint64_t value; + if (!pb_decode_varint(stream, &value)) { + return false; + } + *(*bup + *count - 1) = (google_census_Resource_BasicUnit)value; + } + return true; +} + +// Validate units field of a Resource proto. +static bool validate_units(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + resource *vresource = (resource *)(*arg); + switch (field->tag) { + case google_census_Resource_MeasurementUnit_numerator_tag: + return validate_units_helper(stream, &vresource->n_numerators, + &vresource->numerators); + break; + case google_census_Resource_MeasurementUnit_denominator_tag: + return validate_units_helper(stream, &vresource->n_denominators, + &vresource->denominators); + break; + default: + gpr_log(GPR_ERROR, "Unknown field type."); + return false; + break; + } + return true; +} + +// Validate the contents of a Resource proto. `id` is the intended resource id. +static bool validate_resource_pb(const uint8_t *resource_pb, + size_t resource_pb_size, size_t id) { + GPR_ASSERT(id < n_resources); + if (resource_pb == NULL) { + return false; + } + google_census_Resource vresource; + vresource.name.funcs.decode = &validate_string; + vresource.name.arg = resources[id]; + vresource.description.funcs.decode = &validate_string; + vresource.description.arg = resources[id]; + vresource.unit.numerator.funcs.decode = &validate_units; + vresource.unit.numerator.arg = resources[id]; + vresource.unit.denominator.funcs.decode = &validate_units; + vresource.unit.denominator.arg = resources[id]; + + pb_istream_t stream = + pb_istream_from_buffer((uint8_t *)resource_pb, resource_pb_size); + if (!pb_decode(&stream, google_census_Resource_fields, &vresource)) { + return false; + } + // A Resource must have a name, a unit, with at least one numerator. + return (resources[id]->name != NULL && vresource.has_unit && + resources[id]->n_numerators > 0); +} + +// Allocate a blank resource, and return associated ID. Must be called with +// resource_lock held. +size_t allocate_resource(void) { + // use next_id to optimize expected placement of next new resource. + static size_t next_id = 0; + size_t id = n_resources; // resource ID - initialize to invalid value. + // Expand resources if needed. + if (n_resources == n_defined_resources) { + size_t new_n_resources = n_resources ? n_resources * 2 : 2; + resource **new_resources = + (resource **)gpr_malloc(new_n_resources * sizeof(resource *)); + if (n_resources != 0) { + memcpy(new_resources, resources, n_resources * sizeof(resource *)); + } + memset(new_resources + n_resources, 0, + (new_n_resources - n_resources) * sizeof(resource *)); + gpr_free(resources); + resources = new_resources; + n_resources = new_n_resources; + id = n_defined_resources; + } else { + GPR_ASSERT(n_defined_resources < n_resources); + // Find a free id. + for (size_t base = 0; base < n_resources; base++) { + id = (next_id + base) % n_resources; + if (resources[id] == NULL) break; + } + } + GPR_ASSERT(id < n_resources && resources[id] == NULL); + resources[id] = (resource *)gpr_malloc(sizeof(resource)); + memset(resources[id], 0, sizeof(resource)); + n_defined_resources++; + next_id = (id + 1) % n_resources; + return id; +} + +int32_t census_define_resource(const uint8_t *resource_pb, + size_t resource_pb_size) { + if (resource_pb == NULL) { + return -1; + } + gpr_mu_lock(&resource_lock); + size_t id = allocate_resource(); + // Validate pb, extract name. + if (!validate_resource_pb(resource_pb, resource_pb_size, id)) { + delete_resource_locked(id); + gpr_mu_unlock(&resource_lock); + return -1; + } + gpr_mu_unlock(&resource_lock); + return (int32_t)id; +} + +void census_delete_resource(int32_t rid) { + gpr_mu_lock(&resource_lock); + if (rid >= 0 && (size_t)rid < n_resources && resources[rid] != NULL) { + delete_resource_locked((size_t)rid); + } + gpr_mu_unlock(&resource_lock); +} + +int32_t census_resource_id(const char *name) { + gpr_mu_lock(&resource_lock); + for (int32_t id = 0; (size_t)id < n_resources; id++) { + if (resources[id] != NULL && strcmp(resources[id]->name, name) == 0) { + gpr_mu_unlock(&resource_lock); + return id; + } + } + gpr_mu_unlock(&resource_lock); + return -1; +} + +int32_t define_resource(const resource *base) { + GPR_ASSERT(base != NULL && base->name != NULL && base->n_numerators > 0 && + base->numerators != NULL); + gpr_mu_lock(&resource_lock); + size_t id = allocate_resource(); + size_t len = strlen(base->name) + 1; + resources[id]->name = (char *)gpr_malloc(len); + memcpy(resources[id]->name, base->name, len); + if (base->description) { + len = strlen(base->description) + 1; + resources[id]->description = (char *)gpr_malloc(len); + memcpy(resources[id]->description, base->description, len); + } + resources[id]->prefix = base->prefix; + resources[id]->n_numerators = base->n_numerators; + len = (size_t)base->n_numerators * sizeof(*base->numerators); + resources[id]->numerators = + (google_census_Resource_BasicUnit *)gpr_malloc(len); + memcpy(resources[id]->numerators, base->numerators, len); + resources[id]->n_denominators = base->n_denominators; + if (base->n_denominators != 0) { + len = (size_t)base->n_denominators * sizeof(*base->denominators); + resources[id]->denominators = + (google_census_Resource_BasicUnit *)gpr_malloc(len); + memcpy(resources[id]->denominators, base->denominators, len); + } + gpr_mu_unlock(&resource_lock); + return (int32_t)id; +} diff --git a/src/core/ext/census/trace_context.c b/src/core/ext/census/trace_context.c deleted file mode 100644 index af92ae6d9e..0000000000 --- a/src/core/ext/census/trace_context.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/trace_context.h" - -#include -#include -#include - -#include "third_party/nanopb/pb_decode.h" -#include "third_party/nanopb/pb_encode.h" - -// This function assumes the TraceContext is valid. -size_t encode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer, - const size_t buf_size) { - // Create a stream that will write to our buffer. - pb_ostream_t stream = pb_ostream_from_buffer(buffer, buf_size); - - // encode message - bool status = pb_encode(&stream, google_trace_TraceContext_fields, ctxt); - - if (!status) { - gpr_log(GPR_DEBUG, "TraceContext encoding failed: %s", - PB_GET_ERROR(&stream)); - return 0; - } - - return stream.bytes_written; -} - -bool decode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer, - const size_t nbytes) { - // Create a stream that reads nbytes from the buffer. - pb_istream_t stream = pb_istream_from_buffer(buffer, nbytes); - - // decode message - bool status = pb_decode(&stream, google_trace_TraceContext_fields, ctxt); - - if (!status) { - gpr_log(GPR_DEBUG, "TraceContext decoding failed: %s", - PB_GET_ERROR(&stream)); - return false; - } - - // check fields - if (!ctxt->has_trace_id_hi || !ctxt->has_trace_id_lo) { - gpr_log(GPR_DEBUG, "Invalid TraceContext: missing trace_id"); - return false; - } - if (!ctxt->has_span_id) { - gpr_log(GPR_DEBUG, "Invalid TraceContext: missing span_id"); - return false; - } - - return true; -} diff --git a/src/core/ext/census/trace_context.cc b/src/core/ext/census/trace_context.cc new file mode 100644 index 0000000000..af92ae6d9e --- /dev/null +++ b/src/core/ext/census/trace_context.cc @@ -0,0 +1,71 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/trace_context.h" + +#include +#include +#include + +#include "third_party/nanopb/pb_decode.h" +#include "third_party/nanopb/pb_encode.h" + +// This function assumes the TraceContext is valid. +size_t encode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer, + const size_t buf_size) { + // Create a stream that will write to our buffer. + pb_ostream_t stream = pb_ostream_from_buffer(buffer, buf_size); + + // encode message + bool status = pb_encode(&stream, google_trace_TraceContext_fields, ctxt); + + if (!status) { + gpr_log(GPR_DEBUG, "TraceContext encoding failed: %s", + PB_GET_ERROR(&stream)); + return 0; + } + + return stream.bytes_written; +} + +bool decode_trace_context(google_trace_TraceContext *ctxt, uint8_t *buffer, + const size_t nbytes) { + // Create a stream that reads nbytes from the buffer. + pb_istream_t stream = pb_istream_from_buffer(buffer, nbytes); + + // decode message + bool status = pb_decode(&stream, google_trace_TraceContext_fields, ctxt); + + if (!status) { + gpr_log(GPR_DEBUG, "TraceContext decoding failed: %s", + PB_GET_ERROR(&stream)); + return false; + } + + // check fields + if (!ctxt->has_trace_id_hi || !ctxt->has_trace_id_lo) { + gpr_log(GPR_DEBUG, "Invalid TraceContext: missing trace_id"); + return false; + } + if (!ctxt->has_span_id) { + gpr_log(GPR_DEBUG, "Invalid TraceContext: missing span_id"); + return false; + } + + return true; +} diff --git a/src/core/ext/census/tracing.c b/src/core/ext/census/tracing.c deleted file mode 100644 index 823c681abf..0000000000 --- a/src/core/ext/census/tracing.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/tracing.h" - -#include -#include -#include -#include "src/core/ext/census/mlog.h" - -void trace_start_span(const trace_span_context *span_ctxt, - const trace_string name, const start_span_options *opts, - trace_span_context *new_span_ctxt, - bool has_remote_parent) { - // Noop implementation. -} - -void trace_add_span_annotation(const trace_string description, - const trace_label *labels, const size_t n_labels, - trace_span_context *span_ctxt) { - // Noop implementation. -} - -void trace_add_span_network_event_annotation(const trace_string description, - const trace_label *labels, - const size_t n_labels, - const gpr_timespec timestamp, - bool sent, uint64_t id, - trace_span_context *span_ctxt) { - // Noop implementation. -} - -void trace_add_span_labels(const trace_label *labels, const size_t n_labels, - trace_span_context *span_ctxt) { - // Noop implementation. -} - -void trace_end_span(const trace_status *status, trace_span_context *span_ctxt) { - // Noop implementation. -} diff --git a/src/core/ext/census/tracing.cc b/src/core/ext/census/tracing.cc new file mode 100644 index 0000000000..823c681abf --- /dev/null +++ b/src/core/ext/census/tracing.cc @@ -0,0 +1,55 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/tracing.h" + +#include +#include +#include +#include "src/core/ext/census/mlog.h" + +void trace_start_span(const trace_span_context *span_ctxt, + const trace_string name, const start_span_options *opts, + trace_span_context *new_span_ctxt, + bool has_remote_parent) { + // Noop implementation. +} + +void trace_add_span_annotation(const trace_string description, + const trace_label *labels, const size_t n_labels, + trace_span_context *span_ctxt) { + // Noop implementation. +} + +void trace_add_span_network_event_annotation(const trace_string description, + const trace_label *labels, + const size_t n_labels, + const gpr_timespec timestamp, + bool sent, uint64_t id, + trace_span_context *span_ctxt) { + // Noop implementation. +} + +void trace_add_span_labels(const trace_label *labels, const size_t n_labels, + trace_span_context *span_ctxt) { + // Noop implementation. +} + +void trace_end_span(const trace_status *status, trace_span_context *span_ctxt) { + // Noop implementation. +} diff --git a/src/core/ext/census/window_stats.c b/src/core/ext/census/window_stats.c deleted file mode 100644 index 0058e4bf9c..0000000000 --- a/src/core/ext/census/window_stats.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/census/window_stats.h" -#include -#include -#include -#include -#include -#include -#include - -/* typedefs make typing long names easier. Use cws (for census_window_stats) */ -typedef census_window_stats_stat_info cws_stat_info; -typedef struct census_window_stats_sum cws_sum; - -/* Each interval is composed of a number of buckets, which hold a count of - entries and a single statistic */ -typedef struct census_window_stats_bucket { - int64_t count; - void *statistic; -} cws_bucket; - -/* Each interval has a set of buckets, and the variables needed to keep - track of their current state */ -typedef struct census_window_stats_interval_stats { - /* The buckets. There will be 'granularity' + 1 of these. */ - cws_bucket *buckets; - /* Index of the bucket containing the smallest time interval. */ - int bottom_bucket; - /* The smallest time storable in the current window. */ - int64_t bottom; - /* The largest time storable in the current window + 1ns */ - int64_t top; - /* The width of each bucket in ns. */ - int64_t width; -} cws_interval_stats; - -typedef struct census_window_stats { - /* Number of intervals. */ - int nintervals; - /* Number of buckets in each interval. 'granularity' + 1. */ - int nbuckets; - /* Record of stat_info. */ - cws_stat_info stat_info; - /* Stats for each interval. */ - cws_interval_stats *interval_stats; - /* The time the newset stat was recorded. */ - int64_t newest_time; -} window_stats; - -/* Calculate an actual bucket index from a logical index 'IDX'. Other - parameters supply information on the interval struct and overall stats. */ -#define BUCKET_IDX(IS, IDX, WSTATS) \ - ((IS->bottom_bucket + (IDX)) % WSTATS->nbuckets) - -/* The maximum seconds value we can have in a valid timespec. More than this - will result in overflow in timespec_to_ns(). This works out to ~292 years. - TODO: consider using doubles instead of int64. */ -static int64_t max_seconds = (GPR_INT64_MAX - GPR_NS_PER_SEC) / GPR_NS_PER_SEC; - -static int64_t timespec_to_ns(const gpr_timespec ts) { - if (ts.tv_sec > max_seconds) { - return GPR_INT64_MAX - 1; - } - return ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec; -} - -static void cws_initialize_statistic(void *statistic, - const cws_stat_info *stat_info) { - if (stat_info->stat_initialize == NULL) { - memset(statistic, 0, stat_info->stat_size); - } else { - stat_info->stat_initialize(statistic); - } -} - -/* Create and initialize a statistic */ -static void *cws_create_statistic(const cws_stat_info *stat_info) { - void *stat = gpr_malloc(stat_info->stat_size); - cws_initialize_statistic(stat, stat_info); - return stat; -} - -window_stats *census_window_stats_create(int nintervals, - const gpr_timespec intervals[], - int granularity, - const cws_stat_info *stat_info) { - window_stats *ret; - int i; - /* validate inputs */ - GPR_ASSERT(nintervals > 0 && granularity > 2 && intervals != NULL && - stat_info != NULL); - for (i = 0; i < nintervals; i++) { - int64_t ns = timespec_to_ns(intervals[i]); - GPR_ASSERT(intervals[i].tv_sec >= 0 && intervals[i].tv_nsec >= 0 && - intervals[i].tv_nsec < GPR_NS_PER_SEC && ns >= 100 && - granularity * 10 <= ns); - } - /* Allocate and initialize relevant data structures */ - ret = (window_stats *)gpr_malloc(sizeof(window_stats)); - ret->nintervals = nintervals; - ret->nbuckets = granularity + 1; - ret->stat_info = *stat_info; - ret->interval_stats = - (cws_interval_stats *)gpr_malloc(nintervals * sizeof(cws_interval_stats)); - for (i = 0; i < nintervals; i++) { - int64_t size_ns = timespec_to_ns(intervals[i]); - cws_interval_stats *is = ret->interval_stats + i; - cws_bucket *buckets = is->buckets = - (cws_bucket *)gpr_malloc(ret->nbuckets * sizeof(cws_bucket)); - int b; - for (b = 0; b < ret->nbuckets; b++) { - buckets[b].statistic = cws_create_statistic(stat_info); - buckets[b].count = 0; - } - is->bottom_bucket = 0; - is->bottom = 0; - is->width = size_ns / granularity; - /* Check for possible overflow issues, and maximize interval size if the - user requested something large enough. */ - if ((GPR_INT64_MAX - is->width) > size_ns) { - is->top = size_ns + is->width; - } else { - is->top = GPR_INT64_MAX; - is->width = GPR_INT64_MAX / (granularity + 1); - } - /* If size doesn't divide evenly, we can have a width slightly too small; - better to have it slightly large. */ - if ((size_ns - (granularity + 1) * is->width) > 0) { - is->width += 1; - } - } - ret->newest_time = 0; - return ret; -} - -/* When we try adding a measurement above the current interval range, we - need to "shift" the buckets sufficiently to cover the new range. */ -static void cws_shift_buckets(const window_stats *wstats, - cws_interval_stats *is, int64_t when_ns) { - int i; - /* number of bucket time widths to "shift" */ - int shift; - /* number of buckets to clear */ - int nclear; - GPR_ASSERT(when_ns >= is->top); - /* number of bucket time widths to "shift" */ - shift = ((when_ns - is->top) / is->width) + 1; - /* number of buckets to clear - limited by actual number of buckets */ - nclear = GPR_MIN(shift, wstats->nbuckets); - for (i = 0; i < nclear; i++) { - int b = BUCKET_IDX(is, i, wstats); - is->buckets[b].count = 0; - cws_initialize_statistic(is->buckets[b].statistic, &wstats->stat_info); - } - /* adjust top/bottom times and current bottom bucket */ - is->bottom_bucket = BUCKET_IDX(is, shift, wstats); - is->top += shift * is->width; - is->bottom += shift * is->width; -} - -void census_window_stats_add(window_stats *wstats, const gpr_timespec when, - const void *stat_value) { - int i; - int64_t when_ns = timespec_to_ns(when); - GPR_ASSERT(wstats->interval_stats != NULL); - for (i = 0; i < wstats->nintervals; i++) { - cws_interval_stats *is = wstats->interval_stats + i; - cws_bucket *bucket; - if (when_ns < is->bottom) { /* Below smallest time in interval: drop */ - continue; - } - if (when_ns >= is->top) { /* above limit: shift buckets */ - cws_shift_buckets(wstats, is, when_ns); - } - /* Add the stat. */ - GPR_ASSERT(is->bottom <= when_ns && when_ns < is->top); - bucket = is->buckets + - BUCKET_IDX(is, (when_ns - is->bottom) / is->width, wstats); - bucket->count++; - wstats->stat_info.stat_add(bucket->statistic, stat_value); - } - if (when_ns > wstats->newest_time) { - wstats->newest_time = when_ns; - } -} - -/* Add a specific bucket contents to an accumulating total. */ -static void cws_add_bucket_to_sum(cws_sum *sum, const cws_bucket *bucket, - const cws_stat_info *stat_info) { - sum->count += bucket->count; - stat_info->stat_add(sum->statistic, bucket->statistic); -} - -/* Add a proportion to an accumulating sum. */ -static void cws_add_proportion_to_sum(double p, cws_sum *sum, - const cws_bucket *bucket, - const cws_stat_info *stat_info) { - sum->count += p * bucket->count; - stat_info->stat_add_proportion(p, sum->statistic, bucket->statistic); -} - -void census_window_stats_get_sums(const window_stats *wstats, - const gpr_timespec when, cws_sum sums[]) { - int i; - int64_t when_ns = timespec_to_ns(when); - GPR_ASSERT(wstats->interval_stats != NULL); - for (i = 0; i < wstats->nintervals; i++) { - int when_bucket; - int new_bucket; - double last_proportion = 1.0; - double bottom_proportion; - cws_interval_stats *is = wstats->interval_stats + i; - cws_sum *sum = sums + i; - sum->count = 0; - cws_initialize_statistic(sum->statistic, &wstats->stat_info); - if (when_ns < is->bottom) { - continue; - } - if (when_ns >= is->top) { - cws_shift_buckets(wstats, is, when_ns); - } - /* Calculating the appropriate amount of which buckets to use can get - complicated. Essentially there are two cases: - 1) if the "top" bucket (new_bucket, where the newest additions to the - stats recorded are entered) corresponds to 'when', then we need - to take a proportion of it - (if when < newest_time) or the full - thing. We also (possibly) need to take a corresponding - proportion of the bottom bucket. - 2) Other cases, we just take a straight proportion. - */ - when_bucket = (when_ns - is->bottom) / is->width; - new_bucket = (wstats->newest_time - is->bottom) / is->width; - if (new_bucket == when_bucket) { - int64_t bottom_bucket_time = is->bottom + when_bucket * is->width; - if (when_ns < wstats->newest_time) { - last_proportion = (double)(when_ns - bottom_bucket_time) / - (double)(wstats->newest_time - bottom_bucket_time); - bottom_proportion = - (double)(is->width - (when_ns - bottom_bucket_time)) / is->width; - } else { - bottom_proportion = - (double)(is->width - (wstats->newest_time - bottom_bucket_time)) / - is->width; - } - } else { - last_proportion = - (double)(when_ns + 1 - is->bottom - when_bucket * is->width) / - is->width; - bottom_proportion = 1.0 - last_proportion; - } - cws_add_proportion_to_sum(last_proportion, sum, - is->buckets + BUCKET_IDX(is, when_bucket, wstats), - &wstats->stat_info); - if (when_bucket != 0) { /* last bucket isn't also bottom bucket */ - int b; - /* Add all of "bottom" bucket if we are looking at a subset of the - full interval, or a proportion if we are adding full interval. */ - cws_add_proportion_to_sum( - (when_bucket == wstats->nbuckets - 1 ? bottom_proportion : 1.0), sum, - is->buckets + is->bottom_bucket, &wstats->stat_info); - /* Add all the remaining buckets (everything but top and bottom). */ - for (b = 1; b < when_bucket; b++) { - cws_add_bucket_to_sum(sum, is->buckets + BUCKET_IDX(is, b, wstats), - &wstats->stat_info); - } - } - } -} - -void census_window_stats_destroy(window_stats *wstats) { - int i; - GPR_ASSERT(wstats->interval_stats != NULL); - for (i = 0; i < wstats->nintervals; i++) { - int b; - for (b = 0; b < wstats->nbuckets; b++) { - gpr_free(wstats->interval_stats[i].buckets[b].statistic); - } - gpr_free(wstats->interval_stats[i].buckets); - } - gpr_free(wstats->interval_stats); - /* Ensure any use-after free triggers assert. */ - wstats->interval_stats = NULL; - gpr_free(wstats); -} diff --git a/src/core/ext/census/window_stats.cc b/src/core/ext/census/window_stats.cc new file mode 100644 index 0000000000..0058e4bf9c --- /dev/null +++ b/src/core/ext/census/window_stats.cc @@ -0,0 +1,301 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/census/window_stats.h" +#include +#include +#include +#include +#include +#include +#include + +/* typedefs make typing long names easier. Use cws (for census_window_stats) */ +typedef census_window_stats_stat_info cws_stat_info; +typedef struct census_window_stats_sum cws_sum; + +/* Each interval is composed of a number of buckets, which hold a count of + entries and a single statistic */ +typedef struct census_window_stats_bucket { + int64_t count; + void *statistic; +} cws_bucket; + +/* Each interval has a set of buckets, and the variables needed to keep + track of their current state */ +typedef struct census_window_stats_interval_stats { + /* The buckets. There will be 'granularity' + 1 of these. */ + cws_bucket *buckets; + /* Index of the bucket containing the smallest time interval. */ + int bottom_bucket; + /* The smallest time storable in the current window. */ + int64_t bottom; + /* The largest time storable in the current window + 1ns */ + int64_t top; + /* The width of each bucket in ns. */ + int64_t width; +} cws_interval_stats; + +typedef struct census_window_stats { + /* Number of intervals. */ + int nintervals; + /* Number of buckets in each interval. 'granularity' + 1. */ + int nbuckets; + /* Record of stat_info. */ + cws_stat_info stat_info; + /* Stats for each interval. */ + cws_interval_stats *interval_stats; + /* The time the newset stat was recorded. */ + int64_t newest_time; +} window_stats; + +/* Calculate an actual bucket index from a logical index 'IDX'. Other + parameters supply information on the interval struct and overall stats. */ +#define BUCKET_IDX(IS, IDX, WSTATS) \ + ((IS->bottom_bucket + (IDX)) % WSTATS->nbuckets) + +/* The maximum seconds value we can have in a valid timespec. More than this + will result in overflow in timespec_to_ns(). This works out to ~292 years. + TODO: consider using doubles instead of int64. */ +static int64_t max_seconds = (GPR_INT64_MAX - GPR_NS_PER_SEC) / GPR_NS_PER_SEC; + +static int64_t timespec_to_ns(const gpr_timespec ts) { + if (ts.tv_sec > max_seconds) { + return GPR_INT64_MAX - 1; + } + return ts.tv_sec * GPR_NS_PER_SEC + ts.tv_nsec; +} + +static void cws_initialize_statistic(void *statistic, + const cws_stat_info *stat_info) { + if (stat_info->stat_initialize == NULL) { + memset(statistic, 0, stat_info->stat_size); + } else { + stat_info->stat_initialize(statistic); + } +} + +/* Create and initialize a statistic */ +static void *cws_create_statistic(const cws_stat_info *stat_info) { + void *stat = gpr_malloc(stat_info->stat_size); + cws_initialize_statistic(stat, stat_info); + return stat; +} + +window_stats *census_window_stats_create(int nintervals, + const gpr_timespec intervals[], + int granularity, + const cws_stat_info *stat_info) { + window_stats *ret; + int i; + /* validate inputs */ + GPR_ASSERT(nintervals > 0 && granularity > 2 && intervals != NULL && + stat_info != NULL); + for (i = 0; i < nintervals; i++) { + int64_t ns = timespec_to_ns(intervals[i]); + GPR_ASSERT(intervals[i].tv_sec >= 0 && intervals[i].tv_nsec >= 0 && + intervals[i].tv_nsec < GPR_NS_PER_SEC && ns >= 100 && + granularity * 10 <= ns); + } + /* Allocate and initialize relevant data structures */ + ret = (window_stats *)gpr_malloc(sizeof(window_stats)); + ret->nintervals = nintervals; + ret->nbuckets = granularity + 1; + ret->stat_info = *stat_info; + ret->interval_stats = + (cws_interval_stats *)gpr_malloc(nintervals * sizeof(cws_interval_stats)); + for (i = 0; i < nintervals; i++) { + int64_t size_ns = timespec_to_ns(intervals[i]); + cws_interval_stats *is = ret->interval_stats + i; + cws_bucket *buckets = is->buckets = + (cws_bucket *)gpr_malloc(ret->nbuckets * sizeof(cws_bucket)); + int b; + for (b = 0; b < ret->nbuckets; b++) { + buckets[b].statistic = cws_create_statistic(stat_info); + buckets[b].count = 0; + } + is->bottom_bucket = 0; + is->bottom = 0; + is->width = size_ns / granularity; + /* Check for possible overflow issues, and maximize interval size if the + user requested something large enough. */ + if ((GPR_INT64_MAX - is->width) > size_ns) { + is->top = size_ns + is->width; + } else { + is->top = GPR_INT64_MAX; + is->width = GPR_INT64_MAX / (granularity + 1); + } + /* If size doesn't divide evenly, we can have a width slightly too small; + better to have it slightly large. */ + if ((size_ns - (granularity + 1) * is->width) > 0) { + is->width += 1; + } + } + ret->newest_time = 0; + return ret; +} + +/* When we try adding a measurement above the current interval range, we + need to "shift" the buckets sufficiently to cover the new range. */ +static void cws_shift_buckets(const window_stats *wstats, + cws_interval_stats *is, int64_t when_ns) { + int i; + /* number of bucket time widths to "shift" */ + int shift; + /* number of buckets to clear */ + int nclear; + GPR_ASSERT(when_ns >= is->top); + /* number of bucket time widths to "shift" */ + shift = ((when_ns - is->top) / is->width) + 1; + /* number of buckets to clear - limited by actual number of buckets */ + nclear = GPR_MIN(shift, wstats->nbuckets); + for (i = 0; i < nclear; i++) { + int b = BUCKET_IDX(is, i, wstats); + is->buckets[b].count = 0; + cws_initialize_statistic(is->buckets[b].statistic, &wstats->stat_info); + } + /* adjust top/bottom times and current bottom bucket */ + is->bottom_bucket = BUCKET_IDX(is, shift, wstats); + is->top += shift * is->width; + is->bottom += shift * is->width; +} + +void census_window_stats_add(window_stats *wstats, const gpr_timespec when, + const void *stat_value) { + int i; + int64_t when_ns = timespec_to_ns(when); + GPR_ASSERT(wstats->interval_stats != NULL); + for (i = 0; i < wstats->nintervals; i++) { + cws_interval_stats *is = wstats->interval_stats + i; + cws_bucket *bucket; + if (when_ns < is->bottom) { /* Below smallest time in interval: drop */ + continue; + } + if (when_ns >= is->top) { /* above limit: shift buckets */ + cws_shift_buckets(wstats, is, when_ns); + } + /* Add the stat. */ + GPR_ASSERT(is->bottom <= when_ns && when_ns < is->top); + bucket = is->buckets + + BUCKET_IDX(is, (when_ns - is->bottom) / is->width, wstats); + bucket->count++; + wstats->stat_info.stat_add(bucket->statistic, stat_value); + } + if (when_ns > wstats->newest_time) { + wstats->newest_time = when_ns; + } +} + +/* Add a specific bucket contents to an accumulating total. */ +static void cws_add_bucket_to_sum(cws_sum *sum, const cws_bucket *bucket, + const cws_stat_info *stat_info) { + sum->count += bucket->count; + stat_info->stat_add(sum->statistic, bucket->statistic); +} + +/* Add a proportion to an accumulating sum. */ +static void cws_add_proportion_to_sum(double p, cws_sum *sum, + const cws_bucket *bucket, + const cws_stat_info *stat_info) { + sum->count += p * bucket->count; + stat_info->stat_add_proportion(p, sum->statistic, bucket->statistic); +} + +void census_window_stats_get_sums(const window_stats *wstats, + const gpr_timespec when, cws_sum sums[]) { + int i; + int64_t when_ns = timespec_to_ns(when); + GPR_ASSERT(wstats->interval_stats != NULL); + for (i = 0; i < wstats->nintervals; i++) { + int when_bucket; + int new_bucket; + double last_proportion = 1.0; + double bottom_proportion; + cws_interval_stats *is = wstats->interval_stats + i; + cws_sum *sum = sums + i; + sum->count = 0; + cws_initialize_statistic(sum->statistic, &wstats->stat_info); + if (when_ns < is->bottom) { + continue; + } + if (when_ns >= is->top) { + cws_shift_buckets(wstats, is, when_ns); + } + /* Calculating the appropriate amount of which buckets to use can get + complicated. Essentially there are two cases: + 1) if the "top" bucket (new_bucket, where the newest additions to the + stats recorded are entered) corresponds to 'when', then we need + to take a proportion of it - (if when < newest_time) or the full + thing. We also (possibly) need to take a corresponding + proportion of the bottom bucket. + 2) Other cases, we just take a straight proportion. + */ + when_bucket = (when_ns - is->bottom) / is->width; + new_bucket = (wstats->newest_time - is->bottom) / is->width; + if (new_bucket == when_bucket) { + int64_t bottom_bucket_time = is->bottom + when_bucket * is->width; + if (when_ns < wstats->newest_time) { + last_proportion = (double)(when_ns - bottom_bucket_time) / + (double)(wstats->newest_time - bottom_bucket_time); + bottom_proportion = + (double)(is->width - (when_ns - bottom_bucket_time)) / is->width; + } else { + bottom_proportion = + (double)(is->width - (wstats->newest_time - bottom_bucket_time)) / + is->width; + } + } else { + last_proportion = + (double)(when_ns + 1 - is->bottom - when_bucket * is->width) / + is->width; + bottom_proportion = 1.0 - last_proportion; + } + cws_add_proportion_to_sum(last_proportion, sum, + is->buckets + BUCKET_IDX(is, when_bucket, wstats), + &wstats->stat_info); + if (when_bucket != 0) { /* last bucket isn't also bottom bucket */ + int b; + /* Add all of "bottom" bucket if we are looking at a subset of the + full interval, or a proportion if we are adding full interval. */ + cws_add_proportion_to_sum( + (when_bucket == wstats->nbuckets - 1 ? bottom_proportion : 1.0), sum, + is->buckets + is->bottom_bucket, &wstats->stat_info); + /* Add all the remaining buckets (everything but top and bottom). */ + for (b = 1; b < when_bucket; b++) { + cws_add_bucket_to_sum(sum, is->buckets + BUCKET_IDX(is, b, wstats), + &wstats->stat_info); + } + } + } +} + +void census_window_stats_destroy(window_stats *wstats) { + int i; + GPR_ASSERT(wstats->interval_stats != NULL); + for (i = 0; i < wstats->nintervals; i++) { + int b; + for (b = 0; b < wstats->nbuckets; b++) { + gpr_free(wstats->interval_stats[i].buckets[b].statistic); + } + gpr_free(wstats->interval_stats[i].buckets); + } + gpr_free(wstats->interval_stats); + /* Ensure any use-after free triggers assert. */ + wstats->interval_stats = NULL; + gpr_free(wstats); +} diff --git a/src/core/ext/filters/client_channel/channel_connectivity.c b/src/core/ext/filters/client_channel/channel_connectivity.c deleted file mode 100644 index 3844b98021..0000000000 --- a/src/core/ext/filters/client_channel/channel_connectivity.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/channel.h" - -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/completion_queue.h" - -grpc_connectivity_state grpc_channel_check_connectivity_state( - grpc_channel *channel, int try_to_connect) { - /* forward through to the underlying client channel */ - grpc_channel_element *client_channel_elem = - grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_connectivity_state state; - GRPC_API_TRACE( - "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2, - (channel, try_to_connect)); - if (client_channel_elem->filter == &grpc_client_channel_filter) { - state = grpc_client_channel_check_connectivity_state( - &exec_ctx, client_channel_elem, try_to_connect); - grpc_exec_ctx_finish(&exec_ctx); - return state; - } - gpr_log(GPR_ERROR, - "grpc_channel_check_connectivity_state called on something that is " - "not a client channel, but '%s'", - client_channel_elem->filter->name); - grpc_exec_ctx_finish(&exec_ctx); - return GRPC_CHANNEL_SHUTDOWN; -} - -typedef enum { - WAITING, - READY_TO_CALL_BACK, - CALLING_BACK_AND_FINISHED, -} callback_phase; - -typedef struct { - gpr_mu mu; - callback_phase phase; - grpc_closure on_complete; - grpc_closure on_timeout; - grpc_closure watcher_timer_init; - grpc_timer alarm; - grpc_connectivity_state state; - grpc_completion_queue *cq; - grpc_cq_completion completion_storage; - grpc_channel *channel; - grpc_error *error; - void *tag; -} state_watcher; - -static void delete_state_watcher(grpc_exec_ctx *exec_ctx, state_watcher *w) { - grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(w->channel)); - if (client_channel_elem->filter == &grpc_client_channel_filter) { - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, - "watch_channel_connectivity"); - } else { - abort(); - } - gpr_mu_destroy(&w->mu); - gpr_free(w); -} - -static void finished_completion(grpc_exec_ctx *exec_ctx, void *pw, - grpc_cq_completion *ignored) { - bool should_delete = false; - state_watcher *w = (state_watcher *)pw; - gpr_mu_lock(&w->mu); - switch (w->phase) { - case WAITING: - case READY_TO_CALL_BACK: - GPR_UNREACHABLE_CODE(return ); - case CALLING_BACK_AND_FINISHED: - should_delete = true; - break; - } - gpr_mu_unlock(&w->mu); - - if (should_delete) { - delete_state_watcher(exec_ctx, w); - } -} - -static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w, - bool due_to_completion, grpc_error *error) { - if (due_to_completion) { - grpc_timer_cancel(exec_ctx, &w->alarm); - } else { - grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(w->channel)); - grpc_client_channel_watch_connectivity_state( - exec_ctx, client_channel_elem, - grpc_polling_entity_create_from_pollset(grpc_cq_pollset(w->cq)), NULL, - &w->on_complete, NULL); - } - - gpr_mu_lock(&w->mu); - - if (due_to_completion) { - if (GRPC_TRACER_ON(grpc_trace_operation_failures)) { - GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error)); - } - GRPC_ERROR_UNREF(error); - error = GRPC_ERROR_NONE; - } else { - if (error == GRPC_ERROR_NONE) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Timed out waiting for connection state change"); - } else if (error == GRPC_ERROR_CANCELLED) { - error = GRPC_ERROR_NONE; - } - } - switch (w->phase) { - case WAITING: - GRPC_ERROR_REF(error); - w->error = error; - w->phase = READY_TO_CALL_BACK; - break; - case READY_TO_CALL_BACK: - if (error != GRPC_ERROR_NONE) { - GPR_ASSERT(!due_to_completion); - GRPC_ERROR_UNREF(w->error); - GRPC_ERROR_REF(error); - w->error = error; - } - w->phase = CALLING_BACK_AND_FINISHED; - grpc_cq_end_op(exec_ctx, w->cq, w->tag, w->error, finished_completion, w, - &w->completion_storage); - break; - case CALLING_BACK_AND_FINISHED: - GPR_UNREACHABLE_CODE(return ); - break; - } - gpr_mu_unlock(&w->mu); - - GRPC_ERROR_UNREF(error); -} - -static void watch_complete(grpc_exec_ctx *exec_ctx, void *pw, - grpc_error *error) { - partly_done(exec_ctx, (state_watcher *)pw, true, GRPC_ERROR_REF(error)); -} - -static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw, - grpc_error *error) { - partly_done(exec_ctx, (state_watcher *)pw, false, GRPC_ERROR_REF(error)); -} - -int grpc_channel_num_external_connectivity_watchers(grpc_channel *channel) { - grpc_channel_element *client_channel_elem = - grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); - return grpc_client_channel_num_external_connectivity_watchers( - client_channel_elem); -} - -typedef struct watcher_timer_init_arg { - state_watcher *w; - gpr_timespec deadline; -} watcher_timer_init_arg; - -static void watcher_timer_init(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error_ignored) { - watcher_timer_init_arg *wa = (watcher_timer_init_arg *)arg; - - grpc_timer_init(exec_ctx, &wa->w->alarm, - gpr_convert_clock_type(wa->deadline, GPR_CLOCK_MONOTONIC), - &wa->w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC)); - gpr_free(wa); -} - -int grpc_channel_support_connectivity_watcher(grpc_channel *channel) { - grpc_channel_element *client_channel_elem = - grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); - return client_channel_elem->filter != &grpc_client_channel_filter ? 0 : 1; -} - -void grpc_channel_watch_connectivity_state( - grpc_channel *channel, grpc_connectivity_state last_observed_state, - gpr_timespec deadline, grpc_completion_queue *cq, void *tag) { - grpc_channel_element *client_channel_elem = - grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - state_watcher *w = (state_watcher *)gpr_malloc(sizeof(*w)); - - GRPC_API_TRACE( - "grpc_channel_watch_connectivity_state(" - "channel=%p, last_observed_state=%d, " - "deadline=gpr_timespec { tv_sec: %" PRId64 - ", tv_nsec: %d, clock_type: %d }, " - "cq=%p, tag=%p)", - 7, (channel, (int)last_observed_state, deadline.tv_sec, deadline.tv_nsec, - (int)deadline.clock_type, cq, tag)); - - GPR_ASSERT(grpc_cq_begin_op(cq, tag)); - - gpr_mu_init(&w->mu); - GRPC_CLOSURE_INIT(&w->on_complete, watch_complete, w, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&w->on_timeout, timeout_complete, w, - grpc_schedule_on_exec_ctx); - w->phase = WAITING; - w->state = last_observed_state; - w->cq = cq; - w->tag = tag; - w->channel = channel; - w->error = NULL; - - watcher_timer_init_arg *wa = - (watcher_timer_init_arg *)gpr_malloc(sizeof(watcher_timer_init_arg)); - wa->w = w; - wa->deadline = deadline; - GRPC_CLOSURE_INIT(&w->watcher_timer_init, watcher_timer_init, wa, - grpc_schedule_on_exec_ctx); - - if (client_channel_elem->filter == &grpc_client_channel_filter) { - GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); - grpc_client_channel_watch_connectivity_state( - &exec_ctx, client_channel_elem, - grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)), &w->state, - &w->on_complete, &w->watcher_timer_init); - } else { - abort(); - } - - grpc_exec_ctx_finish(&exec_ctx); -} diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc new file mode 100644 index 0000000000..3844b98021 --- /dev/null +++ b/src/core/ext/filters/client_channel/channel_connectivity.cc @@ -0,0 +1,249 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/channel.h" + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/completion_queue.h" + +grpc_connectivity_state grpc_channel_check_connectivity_state( + grpc_channel *channel, int try_to_connect) { + /* forward through to the underlying client channel */ + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_connectivity_state state; + GRPC_API_TRACE( + "grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2, + (channel, try_to_connect)); + if (client_channel_elem->filter == &grpc_client_channel_filter) { + state = grpc_client_channel_check_connectivity_state( + &exec_ctx, client_channel_elem, try_to_connect); + grpc_exec_ctx_finish(&exec_ctx); + return state; + } + gpr_log(GPR_ERROR, + "grpc_channel_check_connectivity_state called on something that is " + "not a client channel, but '%s'", + client_channel_elem->filter->name); + grpc_exec_ctx_finish(&exec_ctx); + return GRPC_CHANNEL_SHUTDOWN; +} + +typedef enum { + WAITING, + READY_TO_CALL_BACK, + CALLING_BACK_AND_FINISHED, +} callback_phase; + +typedef struct { + gpr_mu mu; + callback_phase phase; + grpc_closure on_complete; + grpc_closure on_timeout; + grpc_closure watcher_timer_init; + grpc_timer alarm; + grpc_connectivity_state state; + grpc_completion_queue *cq; + grpc_cq_completion completion_storage; + grpc_channel *channel; + grpc_error *error; + void *tag; +} state_watcher; + +static void delete_state_watcher(grpc_exec_ctx *exec_ctx, state_watcher *w) { + grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(w->channel)); + if (client_channel_elem->filter == &grpc_client_channel_filter) { + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, w->channel, + "watch_channel_connectivity"); + } else { + abort(); + } + gpr_mu_destroy(&w->mu); + gpr_free(w); +} + +static void finished_completion(grpc_exec_ctx *exec_ctx, void *pw, + grpc_cq_completion *ignored) { + bool should_delete = false; + state_watcher *w = (state_watcher *)pw; + gpr_mu_lock(&w->mu); + switch (w->phase) { + case WAITING: + case READY_TO_CALL_BACK: + GPR_UNREACHABLE_CODE(return ); + case CALLING_BACK_AND_FINISHED: + should_delete = true; + break; + } + gpr_mu_unlock(&w->mu); + + if (should_delete) { + delete_state_watcher(exec_ctx, w); + } +} + +static void partly_done(grpc_exec_ctx *exec_ctx, state_watcher *w, + bool due_to_completion, grpc_error *error) { + if (due_to_completion) { + grpc_timer_cancel(exec_ctx, &w->alarm); + } else { + grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(w->channel)); + grpc_client_channel_watch_connectivity_state( + exec_ctx, client_channel_elem, + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(w->cq)), NULL, + &w->on_complete, NULL); + } + + gpr_mu_lock(&w->mu); + + if (due_to_completion) { + if (GRPC_TRACER_ON(grpc_trace_operation_failures)) { + GRPC_LOG_IF_ERROR("watch_completion_error", GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_NONE; + } else { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Timed out waiting for connection state change"); + } else if (error == GRPC_ERROR_CANCELLED) { + error = GRPC_ERROR_NONE; + } + } + switch (w->phase) { + case WAITING: + GRPC_ERROR_REF(error); + w->error = error; + w->phase = READY_TO_CALL_BACK; + break; + case READY_TO_CALL_BACK: + if (error != GRPC_ERROR_NONE) { + GPR_ASSERT(!due_to_completion); + GRPC_ERROR_UNREF(w->error); + GRPC_ERROR_REF(error); + w->error = error; + } + w->phase = CALLING_BACK_AND_FINISHED; + grpc_cq_end_op(exec_ctx, w->cq, w->tag, w->error, finished_completion, w, + &w->completion_storage); + break; + case CALLING_BACK_AND_FINISHED: + GPR_UNREACHABLE_CODE(return ); + break; + } + gpr_mu_unlock(&w->mu); + + GRPC_ERROR_UNREF(error); +} + +static void watch_complete(grpc_exec_ctx *exec_ctx, void *pw, + grpc_error *error) { + partly_done(exec_ctx, (state_watcher *)pw, true, GRPC_ERROR_REF(error)); +} + +static void timeout_complete(grpc_exec_ctx *exec_ctx, void *pw, + grpc_error *error) { + partly_done(exec_ctx, (state_watcher *)pw, false, GRPC_ERROR_REF(error)); +} + +int grpc_channel_num_external_connectivity_watchers(grpc_channel *channel) { + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + return grpc_client_channel_num_external_connectivity_watchers( + client_channel_elem); +} + +typedef struct watcher_timer_init_arg { + state_watcher *w; + gpr_timespec deadline; +} watcher_timer_init_arg; + +static void watcher_timer_init(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error_ignored) { + watcher_timer_init_arg *wa = (watcher_timer_init_arg *)arg; + + grpc_timer_init(exec_ctx, &wa->w->alarm, + gpr_convert_clock_type(wa->deadline, GPR_CLOCK_MONOTONIC), + &wa->w->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC)); + gpr_free(wa); +} + +int grpc_channel_support_connectivity_watcher(grpc_channel *channel) { + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + return client_channel_elem->filter != &grpc_client_channel_filter ? 0 : 1; +} + +void grpc_channel_watch_connectivity_state( + grpc_channel *channel, grpc_connectivity_state last_observed_state, + gpr_timespec deadline, grpc_completion_queue *cq, void *tag) { + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + state_watcher *w = (state_watcher *)gpr_malloc(sizeof(*w)); + + GRPC_API_TRACE( + "grpc_channel_watch_connectivity_state(" + "channel=%p, last_observed_state=%d, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "cq=%p, tag=%p)", + 7, (channel, (int)last_observed_state, deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, cq, tag)); + + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + + gpr_mu_init(&w->mu); + GRPC_CLOSURE_INIT(&w->on_complete, watch_complete, w, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&w->on_timeout, timeout_complete, w, + grpc_schedule_on_exec_ctx); + w->phase = WAITING; + w->state = last_observed_state; + w->cq = cq; + w->tag = tag; + w->channel = channel; + w->error = NULL; + + watcher_timer_init_arg *wa = + (watcher_timer_init_arg *)gpr_malloc(sizeof(watcher_timer_init_arg)); + wa->w = w; + wa->deadline = deadline; + GRPC_CLOSURE_INIT(&w->watcher_timer_init, watcher_timer_init, wa, + grpc_schedule_on_exec_ctx); + + if (client_channel_elem->filter == &grpc_client_channel_filter) { + GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity"); + grpc_client_channel_watch_connectivity_state( + &exec_ctx, client_channel_elem, + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)), &w->state, + &w->on_complete, &w->watcher_timer_init); + } else { + abort(); + } + + grpc_exec_ctx_finish(&exec_ctx); +} diff --git a/src/core/ext/filters/client_channel/client_channel.c b/src/core/ext/filters/client_channel/client_channel.c deleted file mode 100644 index 016199b1f4..0000000000 --- a/src/core/ext/filters/client_channel/client_channel.c +++ /dev/null @@ -1,1657 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/client_channel.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/ext/filters/client_channel/retry_throttle.h" -#include "src/core/ext/filters/client_channel/subchannel.h" -#include "src/core/ext/filters/deadline/deadline_filter.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/connected_channel.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "src/core/lib/iomgr/polling_entity.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/transport/connectivity_state.h" -#include "src/core/lib/transport/metadata.h" -#include "src/core/lib/transport/metadata_batch.h" -#include "src/core/lib/transport/service_config.h" -#include "src/core/lib/transport/static_metadata.h" - -/* Client channel implementation */ - -grpc_tracer_flag grpc_client_channel_trace = - GRPC_TRACER_INITIALIZER(false, "client_channel"); - -/************************************************************************* - * METHOD-CONFIG TABLE - */ - -typedef enum { - /* zero so it can be default initialized */ - WAIT_FOR_READY_UNSET = 0, - WAIT_FOR_READY_FALSE, - WAIT_FOR_READY_TRUE -} wait_for_ready_value; - -typedef struct { - gpr_refcount refs; - gpr_timespec timeout; - wait_for_ready_value wait_for_ready; -} method_parameters; - -static method_parameters *method_parameters_ref( - method_parameters *method_params) { - gpr_ref(&method_params->refs); - return method_params; -} - -static void method_parameters_unref(method_parameters *method_params) { - if (gpr_unref(&method_params->refs)) { - gpr_free(method_params); - } -} - -static void method_parameters_free(grpc_exec_ctx *exec_ctx, void *value) { - method_parameters_unref((method_parameters *)value); -} - -static bool parse_wait_for_ready(grpc_json *field, - wait_for_ready_value *wait_for_ready) { - if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { - return false; - } - *wait_for_ready = field->type == GRPC_JSON_TRUE ? WAIT_FOR_READY_TRUE - : WAIT_FOR_READY_FALSE; - return true; -} - -static bool parse_timeout(grpc_json *field, gpr_timespec *timeout) { - if (field->type != GRPC_JSON_STRING) return false; - size_t len = strlen(field->value); - if (field->value[len - 1] != 's') return false; - char *buf = gpr_strdup(field->value); - buf[len - 1] = '\0'; // Remove trailing 's'. - char *decimal_point = strchr(buf, '.'); - if (decimal_point != NULL) { - *decimal_point = '\0'; - timeout->tv_nsec = gpr_parse_nonnegative_int(decimal_point + 1); - if (timeout->tv_nsec == -1) { - gpr_free(buf); - return false; - } - // There should always be exactly 3, 6, or 9 fractional digits. - int multiplier = 1; - switch (strlen(decimal_point + 1)) { - case 9: - break; - case 6: - multiplier *= 1000; - break; - case 3: - multiplier *= 1000000; - break; - default: // Unsupported number of digits. - gpr_free(buf); - return false; - } - timeout->tv_nsec *= multiplier; - } - timeout->tv_sec = gpr_parse_nonnegative_int(buf); - gpr_free(buf); - if (timeout->tv_sec == -1) return false; - return true; -} - -static void *method_parameters_create_from_json(const grpc_json *json) { - wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET; - gpr_timespec timeout = {0, 0, GPR_TIMESPAN}; - for (grpc_json *field = json->child; field != NULL; field = field->next) { - if (field->key == NULL) continue; - if (strcmp(field->key, "waitForReady") == 0) { - if (wait_for_ready != WAIT_FOR_READY_UNSET) return NULL; // Duplicate. - if (!parse_wait_for_ready(field, &wait_for_ready)) return NULL; - } else if (strcmp(field->key, "timeout") == 0) { - if (timeout.tv_sec > 0 || timeout.tv_nsec > 0) return NULL; // Duplicate. - if (!parse_timeout(field, &timeout)) return NULL; - } - } - method_parameters *value = - (method_parameters *)gpr_malloc(sizeof(method_parameters)); - gpr_ref_init(&value->refs, 1); - value->timeout = timeout; - value->wait_for_ready = wait_for_ready; - return value; -} - -struct external_connectivity_watcher; - -/************************************************************************* - * CHANNEL-WIDE FUNCTIONS - */ - -typedef struct client_channel_channel_data { - /** resolver for this channel */ - grpc_resolver *resolver; - /** have we started resolving this channel */ - bool started_resolving; - /** is deadline checking enabled? */ - bool deadline_checking_enabled; - /** client channel factory */ - grpc_client_channel_factory *client_channel_factory; - - /** combiner protecting all variables below in this data structure */ - grpc_combiner *combiner; - /** currently active load balancer */ - grpc_lb_policy *lb_policy; - /** retry throttle data */ - grpc_server_retry_throttle_data *retry_throttle_data; - /** maps method names to method_parameters structs */ - grpc_slice_hash_table *method_params_table; - /** incoming resolver result - set by resolver.next() */ - grpc_channel_args *resolver_result; - /** a list of closures that are all waiting for resolver result to come in */ - grpc_closure_list waiting_for_resolver_result_closures; - /** resolver callback */ - grpc_closure on_resolver_result_changed; - /** connectivity state being tracked */ - grpc_connectivity_state_tracker state_tracker; - /** when an lb_policy arrives, should we try to exit idle */ - bool exit_idle_when_lb_policy_arrives; - /** owning stack */ - grpc_channel_stack *owning_stack; - /** interested parties (owned) */ - grpc_pollset_set *interested_parties; - - /* external_connectivity_watcher_list head is guarded by its own mutex, since - * counts need to be grabbed immediately without polling on a cq */ - gpr_mu external_connectivity_watcher_list_mu; - struct external_connectivity_watcher *external_connectivity_watcher_list_head; - - /* the following properties are guarded by a mutex since API's require them - to be instantaneously available */ - gpr_mu info_mu; - char *info_lb_policy_name; - /** service config in JSON form */ - char *info_service_config_json; -} channel_data; - -/** We create one watcher for each new lb_policy that is returned from a - resolver, to watch for state changes from the lb_policy. When a state - change is seen, we update the channel, and create a new watcher. */ -typedef struct { - channel_data *chand; - grpc_closure on_changed; - grpc_connectivity_state state; - grpc_lb_policy *lb_policy; -} lb_policy_connectivity_watcher; - -static void watch_lb_policy_locked(grpc_exec_ctx *exec_ctx, channel_data *chand, - grpc_lb_policy *lb_policy, - grpc_connectivity_state current_state); - -static void set_channel_connectivity_state_locked(grpc_exec_ctx *exec_ctx, - channel_data *chand, - grpc_connectivity_state state, - grpc_error *error, - const char *reason) { - /* TODO: Improve failure handling: - * - Make it possible for policies to return GRPC_CHANNEL_TRANSIENT_FAILURE. - * - Hand over pending picks from old policies during the switch that happens - * when resolver provides an update. */ - if (chand->lb_policy != NULL) { - if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) { - /* cancel picks with wait_for_ready=false */ - grpc_lb_policy_cancel_picks_locked( - exec_ctx, chand->lb_policy, - /* mask= */ GRPC_INITIAL_METADATA_WAIT_FOR_READY, - /* check= */ 0, GRPC_ERROR_REF(error)); - } else if (state == GRPC_CHANNEL_SHUTDOWN) { - /* cancel all picks */ - grpc_lb_policy_cancel_picks_locked(exec_ctx, chand->lb_policy, - /* mask= */ 0, /* check= */ 0, - GRPC_ERROR_REF(error)); - } - } - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: setting connectivity state to %s", chand, - grpc_connectivity_state_name(state)); - } - grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, state, error, - reason); -} - -static void on_lb_policy_state_changed_locked(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error) { - lb_policy_connectivity_watcher *w = (lb_policy_connectivity_watcher *)arg; - grpc_connectivity_state publish_state = w->state; - /* check if the notification is for the latest policy */ - if (w->lb_policy == w->chand->lb_policy) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: lb_policy=%p state changed to %s", w->chand, - w->lb_policy, grpc_connectivity_state_name(w->state)); - } - if (publish_state == GRPC_CHANNEL_SHUTDOWN && w->chand->resolver != NULL) { - publish_state = GRPC_CHANNEL_TRANSIENT_FAILURE; - grpc_resolver_channel_saw_error_locked(exec_ctx, w->chand->resolver); - GRPC_LB_POLICY_UNREF(exec_ctx, w->chand->lb_policy, "channel"); - w->chand->lb_policy = NULL; - } - set_channel_connectivity_state_locked(exec_ctx, w->chand, publish_state, - GRPC_ERROR_REF(error), "lb_changed"); - if (w->state != GRPC_CHANNEL_SHUTDOWN) { - watch_lb_policy_locked(exec_ctx, w->chand, w->lb_policy, w->state); - } - } - GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "watch_lb_policy"); - gpr_free(w); -} - -static void watch_lb_policy_locked(grpc_exec_ctx *exec_ctx, channel_data *chand, - grpc_lb_policy *lb_policy, - grpc_connectivity_state current_state) { - lb_policy_connectivity_watcher *w = - (lb_policy_connectivity_watcher *)gpr_malloc(sizeof(*w)); - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "watch_lb_policy"); - w->chand = chand; - GRPC_CLOSURE_INIT(&w->on_changed, on_lb_policy_state_changed_locked, w, - grpc_combiner_scheduler(chand->combiner)); - w->state = current_state; - w->lb_policy = lb_policy; - grpc_lb_policy_notify_on_state_change_locked(exec_ctx, lb_policy, &w->state, - &w->on_changed); -} - -static void start_resolving_locked(grpc_exec_ctx *exec_ctx, - channel_data *chand) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: starting name resolution", chand); - } - GPR_ASSERT(!chand->started_resolving); - chand->started_resolving = true; - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); - grpc_resolver_next_locked(exec_ctx, chand->resolver, &chand->resolver_result, - &chand->on_resolver_result_changed); -} - -typedef struct { - char *server_name; - grpc_server_retry_throttle_data *retry_throttle_data; -} service_config_parsing_state; - -static void parse_retry_throttle_params(const grpc_json *field, void *arg) { - service_config_parsing_state *parsing_state = - (service_config_parsing_state *)arg; - if (strcmp(field->key, "retryThrottling") == 0) { - if (parsing_state->retry_throttle_data != NULL) return; // Duplicate. - if (field->type != GRPC_JSON_OBJECT) return; - int max_milli_tokens = 0; - int milli_token_ratio = 0; - for (grpc_json *sub_field = field->child; sub_field != NULL; - sub_field = sub_field->next) { - if (sub_field->key == NULL) return; - if (strcmp(sub_field->key, "maxTokens") == 0) { - if (max_milli_tokens != 0) return; // Duplicate. - if (sub_field->type != GRPC_JSON_NUMBER) return; - max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value); - if (max_milli_tokens == -1) return; - max_milli_tokens *= 1000; - } else if (strcmp(sub_field->key, "tokenRatio") == 0) { - if (milli_token_ratio != 0) return; // Duplicate. - if (sub_field->type != GRPC_JSON_NUMBER) return; - // We support up to 3 decimal digits. - size_t whole_len = strlen(sub_field->value); - uint32_t multiplier = 1; - uint32_t decimal_value = 0; - const char *decimal_point = strchr(sub_field->value, '.'); - if (decimal_point != NULL) { - whole_len = (size_t)(decimal_point - sub_field->value); - multiplier = 1000; - size_t decimal_len = strlen(decimal_point + 1); - if (decimal_len > 3) decimal_len = 3; - if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len, - &decimal_value)) { - return; - } - uint32_t decimal_multiplier = 1; - for (size_t i = 0; i < (3 - decimal_len); ++i) { - decimal_multiplier *= 10; - } - decimal_value *= decimal_multiplier; - } - uint32_t whole_value; - if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len, - &whole_value)) { - return; - } - milli_token_ratio = (int)((whole_value * multiplier) + decimal_value); - if (milli_token_ratio <= 0) return; - } - } - parsing_state->retry_throttle_data = - grpc_retry_throttle_map_get_data_for_server( - parsing_state->server_name, max_milli_tokens, milli_token_ratio); - } -} - -static void on_resolver_result_changed_locked(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error) { - channel_data *chand = (channel_data *)arg; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: got resolver result: error=%s", chand, - grpc_error_string(error)); - } - // Extract the following fields from the resolver result, if non-NULL. - bool lb_policy_updated = false; - char *lb_policy_name_dup = NULL; - bool lb_policy_name_changed = false; - grpc_lb_policy *new_lb_policy = NULL; - char *service_config_json = NULL; - grpc_server_retry_throttle_data *retry_throttle_data = NULL; - grpc_slice_hash_table *method_params_table = NULL; - if (chand->resolver_result != NULL) { - // Find LB policy name. - const char *lb_policy_name = NULL; - const grpc_arg *channel_arg = - grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_POLICY_NAME); - if (channel_arg != NULL) { - GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); - lb_policy_name = channel_arg->value.string; - } - // Special case: If at least one balancer address is present, we use - // the grpclb policy, regardless of what the resolver actually specified. - channel_arg = - grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_ADDRESSES); - if (channel_arg != NULL && channel_arg->type == GRPC_ARG_POINTER) { - grpc_lb_addresses *addresses = - (grpc_lb_addresses *)channel_arg->value.pointer.p; - bool found_balancer_address = false; - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (addresses->addresses[i].is_balancer) { - found_balancer_address = true; - break; - } - } - if (found_balancer_address) { - if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) { - gpr_log(GPR_INFO, - "resolver requested LB policy %s but provided at least one " - "balancer address -- forcing use of grpclb LB policy", - lb_policy_name); - } - lb_policy_name = "grpclb"; - } - } - // Use pick_first if nothing was specified and we didn't select grpclb - // above. - if (lb_policy_name == NULL) lb_policy_name = "pick_first"; - grpc_lb_policy_args lb_policy_args; - lb_policy_args.args = chand->resolver_result; - lb_policy_args.client_channel_factory = chand->client_channel_factory; - lb_policy_args.combiner = chand->combiner; - // Check to see if we're already using the right LB policy. - // Note: It's safe to use chand->info_lb_policy_name here without - // taking a lock on chand->info_mu, because this function is the - // only thing that modifies its value, and it can only be invoked - // once at any given time. - lb_policy_name_changed = - chand->info_lb_policy_name == NULL || - strcmp(chand->info_lb_policy_name, lb_policy_name) != 0; - if (chand->lb_policy != NULL && !lb_policy_name_changed) { - // Continue using the same LB policy. Update with new addresses. - lb_policy_updated = true; - grpc_lb_policy_update_locked(exec_ctx, chand->lb_policy, &lb_policy_args); - } else { - // Instantiate new LB policy. - new_lb_policy = - grpc_lb_policy_create(exec_ctx, lb_policy_name, &lb_policy_args); - if (new_lb_policy == NULL) { - gpr_log(GPR_ERROR, "could not create LB policy \"%s\"", lb_policy_name); - } - } - // Find service config. - channel_arg = - grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVICE_CONFIG); - if (channel_arg != NULL) { - GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); - service_config_json = gpr_strdup(channel_arg->value.string); - grpc_service_config *service_config = - grpc_service_config_create(service_config_json); - if (service_config != NULL) { - channel_arg = - grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVER_URI); - GPR_ASSERT(channel_arg != NULL); - GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); - grpc_uri *uri = - grpc_uri_parse(exec_ctx, channel_arg->value.string, true); - GPR_ASSERT(uri->path[0] != '\0'); - service_config_parsing_state parsing_state; - memset(&parsing_state, 0, sizeof(parsing_state)); - parsing_state.server_name = - uri->path[0] == '/' ? uri->path + 1 : uri->path; - grpc_service_config_parse_global_params( - service_config, parse_retry_throttle_params, &parsing_state); - grpc_uri_destroy(uri); - retry_throttle_data = parsing_state.retry_throttle_data; - method_params_table = grpc_service_config_create_method_config_table( - exec_ctx, service_config, method_parameters_create_from_json, - method_parameters_free); - grpc_service_config_destroy(service_config); - } - } - // Before we clean up, save a copy of lb_policy_name, since it might - // be pointing to data inside chand->resolver_result. - // The copy will be saved in chand->lb_policy_name below. - lb_policy_name_dup = gpr_strdup(lb_policy_name); - grpc_channel_args_destroy(exec_ctx, chand->resolver_result); - chand->resolver_result = NULL; - } - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p: resolver result: lb_policy_name=\"%s\"%s, " - "service_config=\"%s\"", - chand, lb_policy_name_dup, - lb_policy_name_changed ? " (changed)" : "", service_config_json); - } - // Now swap out fields in chand. Note that the new values may still - // be NULL if (e.g.) the resolver failed to return results or the - // results did not contain the necessary data. - // - // First, swap out the data used by cc_get_channel_info(). - gpr_mu_lock(&chand->info_mu); - if (lb_policy_name_dup != NULL) { - gpr_free(chand->info_lb_policy_name); - chand->info_lb_policy_name = lb_policy_name_dup; - } - if (service_config_json != NULL) { - gpr_free(chand->info_service_config_json); - chand->info_service_config_json = service_config_json; - } - gpr_mu_unlock(&chand->info_mu); - // Swap out the retry throttle data. - if (chand->retry_throttle_data != NULL) { - grpc_server_retry_throttle_data_unref(chand->retry_throttle_data); - } - chand->retry_throttle_data = retry_throttle_data; - // Swap out the method params table. - if (chand->method_params_table != NULL) { - grpc_slice_hash_table_unref(exec_ctx, chand->method_params_table); - } - chand->method_params_table = method_params_table; - // If we have a new LB policy or are shutting down (in which case - // new_lb_policy will be NULL), swap out the LB policy, unreffing the - // old one and removing its fds from chand->interested_parties. - // Note that we do NOT do this if either (a) we updated the existing - // LB policy above or (b) we failed to create the new LB policy (in - // which case we want to continue using the most recent one we had). - if (new_lb_policy != NULL || error != GRPC_ERROR_NONE || - chand->resolver == NULL) { - if (chand->lb_policy != NULL) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: unreffing lb_policy=%p", chand, - chand->lb_policy); - } - grpc_pollset_set_del_pollset_set(exec_ctx, - chand->lb_policy->interested_parties, - chand->interested_parties); - GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); - } - chand->lb_policy = new_lb_policy; - } - // Now that we've swapped out the relevant fields of chand, check for - // error or shutdown. - if (error != GRPC_ERROR_NONE || chand->resolver == NULL) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: shutting down", chand); - } - if (chand->resolver != NULL) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: shutting down resolver", chand); - } - grpc_resolver_shutdown_locked(exec_ctx, chand->resolver); - GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); - chand->resolver = NULL; - } - set_channel_connectivity_state_locked( - exec_ctx, chand, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Got resolver result after disconnection", &error, 1), - "resolver_gone"); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "resolver"); - grpc_closure_list_fail_all(&chand->waiting_for_resolver_result_closures, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Channel disconnected", &error, 1)); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, - &chand->waiting_for_resolver_result_closures); - } else { // Not shutting down. - grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE; - grpc_error *state_error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy"); - if (new_lb_policy != NULL) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p: initializing new LB policy", chand); - } - GRPC_ERROR_UNREF(state_error); - state = grpc_lb_policy_check_connectivity_locked(exec_ctx, new_lb_policy, - &state_error); - grpc_pollset_set_add_pollset_set(exec_ctx, - new_lb_policy->interested_parties, - chand->interested_parties); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, - &chand->waiting_for_resolver_result_closures); - if (chand->exit_idle_when_lb_policy_arrives) { - grpc_lb_policy_exit_idle_locked(exec_ctx, new_lb_policy); - chand->exit_idle_when_lb_policy_arrives = false; - } - watch_lb_policy_locked(exec_ctx, chand, new_lb_policy, state); - } - if (!lb_policy_updated) { - set_channel_connectivity_state_locked(exec_ctx, chand, state, - GRPC_ERROR_REF(state_error), - "new_lb+resolver"); - } - grpc_resolver_next_locked(exec_ctx, chand->resolver, - &chand->resolver_result, - &chand->on_resolver_result_changed); - GRPC_ERROR_UNREF(state_error); - } -} - -static void start_transport_op_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error_ignored) { - grpc_transport_op *op = (grpc_transport_op *)arg; - grpc_channel_element *elem = - (grpc_channel_element *)op->handler_private.extra_arg; - channel_data *chand = (channel_data *)elem->channel_data; - - if (op->on_connectivity_state_change != NULL) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &chand->state_tracker, op->connectivity_state, - op->on_connectivity_state_change); - op->on_connectivity_state_change = NULL; - op->connectivity_state = NULL; - } - - if (op->send_ping != NULL) { - if (chand->lb_policy == NULL) { - GRPC_CLOSURE_SCHED( - exec_ctx, op->send_ping, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Ping with no load balancing")); - } else { - grpc_lb_policy_ping_one_locked(exec_ctx, chand->lb_policy, op->send_ping); - op->bind_pollset = NULL; - } - op->send_ping = NULL; - } - - if (op->disconnect_with_error != GRPC_ERROR_NONE) { - if (chand->resolver != NULL) { - set_channel_connectivity_state_locked( - exec_ctx, chand, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_REF(op->disconnect_with_error), "disconnect"); - grpc_resolver_shutdown_locked(exec_ctx, chand->resolver); - GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); - chand->resolver = NULL; - if (!chand->started_resolving) { - grpc_closure_list_fail_all(&chand->waiting_for_resolver_result_closures, - GRPC_ERROR_REF(op->disconnect_with_error)); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, - &chand->waiting_for_resolver_result_closures); - } - if (chand->lb_policy != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, - chand->lb_policy->interested_parties, - chand->interested_parties); - GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); - chand->lb_policy = NULL; - } - } - GRPC_ERROR_UNREF(op->disconnect_with_error); - } - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "start_transport_op"); - - GRPC_CLOSURE_SCHED(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); -} - -static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_transport_op *op) { - channel_data *chand = (channel_data *)elem->channel_data; - - GPR_ASSERT(op->set_accept_stream == false); - if (op->bind_pollset != NULL) { - grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, - op->bind_pollset); - } - - op->handler_private.extra_arg = elem; - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op"); - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&op->handler_private.closure, start_transport_op_locked, - op, grpc_combiner_scheduler(chand->combiner)), - GRPC_ERROR_NONE); -} - -static void cc_get_channel_info(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - const grpc_channel_info *info) { - channel_data *chand = (channel_data *)elem->channel_data; - gpr_mu_lock(&chand->info_mu); - if (info->lb_policy_name != NULL) { - *info->lb_policy_name = chand->info_lb_policy_name == NULL - ? NULL - : gpr_strdup(chand->info_lb_policy_name); - } - if (info->service_config_json != NULL) { - *info->service_config_json = - chand->info_service_config_json == NULL - ? NULL - : gpr_strdup(chand->info_service_config_json); - } - gpr_mu_unlock(&chand->info_mu); -} - -/* Constructor for channel_data */ -static grpc_error *cc_init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = (channel_data *)elem->channel_data; - GPR_ASSERT(args->is_last); - GPR_ASSERT(elem->filter == &grpc_client_channel_filter); - // Initialize data members. - chand->combiner = grpc_combiner_create(); - gpr_mu_init(&chand->info_mu); - gpr_mu_init(&chand->external_connectivity_watcher_list_mu); - - gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); - chand->external_connectivity_watcher_list_head = NULL; - gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); - - chand->owning_stack = args->channel_stack; - GRPC_CLOSURE_INIT(&chand->on_resolver_result_changed, - on_resolver_result_changed_locked, chand, - grpc_combiner_scheduler(chand->combiner)); - chand->interested_parties = grpc_pollset_set_create(); - grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, - "client_channel"); - // Record client channel factory. - const grpc_arg *arg = grpc_channel_args_find(args->channel_args, - GRPC_ARG_CLIENT_CHANNEL_FACTORY); - if (arg == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Missing client channel factory in args for client channel filter"); - } - if (arg->type != GRPC_ARG_POINTER) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "client channel factory arg must be a pointer"); - } - grpc_client_channel_factory_ref( - (grpc_client_channel_factory *)arg->value.pointer.p); - chand->client_channel_factory = - (grpc_client_channel_factory *)arg->value.pointer.p; - // Get server name to resolve, using proxy mapper if needed. - arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI); - if (arg == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Missing server uri in args for client channel filter"); - } - if (arg->type != GRPC_ARG_STRING) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "server uri arg must be a string"); - } - char *proxy_name = NULL; - grpc_channel_args *new_args = NULL; - grpc_proxy_mappers_map_name(exec_ctx, arg->value.string, args->channel_args, - &proxy_name, &new_args); - // Instantiate resolver. - chand->resolver = grpc_resolver_create( - exec_ctx, proxy_name != NULL ? proxy_name : arg->value.string, - new_args != NULL ? new_args : args->channel_args, - chand->interested_parties, chand->combiner); - if (proxy_name != NULL) gpr_free(proxy_name); - if (new_args != NULL) grpc_channel_args_destroy(exec_ctx, new_args); - if (chand->resolver == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("resolver creation failed"); - } - chand->deadline_checking_enabled = - grpc_deadline_checking_enabled(args->channel_args); - return GRPC_ERROR_NONE; -} - -static void shutdown_resolver_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_resolver *resolver = (grpc_resolver *)arg; - grpc_resolver_shutdown_locked(exec_ctx, resolver); - GRPC_RESOLVER_UNREF(exec_ctx, resolver, "channel"); -} - -/* Destructor for channel_data */ -static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - if (chand->resolver != NULL) { - GRPC_CLOSURE_SCHED( - exec_ctx, GRPC_CLOSURE_CREATE(shutdown_resolver_locked, chand->resolver, - grpc_combiner_scheduler(chand->combiner)), - GRPC_ERROR_NONE); - } - if (chand->client_channel_factory != NULL) { - grpc_client_channel_factory_unref(exec_ctx, chand->client_channel_factory); - } - if (chand->lb_policy != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, - chand->lb_policy->interested_parties, - chand->interested_parties); - GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); - } - gpr_free(chand->info_lb_policy_name); - gpr_free(chand->info_service_config_json); - if (chand->retry_throttle_data != NULL) { - grpc_server_retry_throttle_data_unref(chand->retry_throttle_data); - } - if (chand->method_params_table != NULL) { - grpc_slice_hash_table_unref(exec_ctx, chand->method_params_table); - } - grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); - grpc_pollset_set_destroy(exec_ctx, chand->interested_parties); - GRPC_COMBINER_UNREF(exec_ctx, chand->combiner, "client_channel"); - gpr_mu_destroy(&chand->info_mu); - gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu); -} - -/************************************************************************* - * PER-CALL FUNCTIONS - */ - -// Max number of batches that can be pending on a call at any given -// time. This includes: -// recv_initial_metadata -// send_initial_metadata -// recv_message -// send_message -// recv_trailing_metadata -// send_trailing_metadata -// We also add room for a single cancel_stream batch. -#define MAX_WAITING_BATCHES 7 - -/** Call data. Holds a pointer to grpc_subchannel_call and the - associated machinery to create such a pointer. - Handles queueing of stream ops until a call object is ready, waiting - for initial metadata before trying to create a call object, - and handling cancellation gracefully. */ -typedef struct client_channel_call_data { - // State for handling deadlines. - // The code in deadline_filter.c requires this to be the first field. - // TODO(roth): This is slightly sub-optimal in that grpc_deadline_state - // and this struct both independently store pointers to the call stack - // and call combiner. If/when we have time, find a way to avoid this - // without breaking the grpc_deadline_state abstraction. - grpc_deadline_state deadline_state; - - grpc_slice path; // Request path. - gpr_timespec call_start_time; - gpr_timespec deadline; - gpr_arena *arena; - grpc_call_stack *owning_call; - grpc_call_combiner *call_combiner; - - grpc_server_retry_throttle_data *retry_throttle_data; - method_parameters *method_params; - - grpc_subchannel_call *subchannel_call; - grpc_error *error; - - grpc_lb_policy *lb_policy; // Holds ref while LB pick is pending. - grpc_closure lb_pick_closure; - grpc_closure lb_pick_cancel_closure; - - grpc_connected_subchannel *connected_subchannel; - grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT]; - grpc_polling_entity *pollent; - - grpc_transport_stream_op_batch *waiting_for_pick_batches[MAX_WAITING_BATCHES]; - size_t waiting_for_pick_batches_count; - grpc_closure handle_pending_batch_in_call_combiner[MAX_WAITING_BATCHES]; - - grpc_transport_stream_op_batch *initial_metadata_batch; - - grpc_linked_mdelem lb_token_mdelem; - - grpc_closure on_complete; - grpc_closure *original_on_complete; -} call_data; - -grpc_subchannel_call *grpc_client_channel_get_subchannel_call( - grpc_call_element *elem) { - call_data *calld = (call_data *)elem->call_data; - return calld->subchannel_call; -} - -// This is called via the call combiner, so access to calld is synchronized. -static void waiting_for_pick_batches_add( - call_data *calld, grpc_transport_stream_op_batch *batch) { - if (batch->send_initial_metadata) { - GPR_ASSERT(calld->initial_metadata_batch == NULL); - calld->initial_metadata_batch = batch; - } else { - GPR_ASSERT(calld->waiting_for_pick_batches_count < MAX_WAITING_BATCHES); - calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count++] = - batch; - } -} - -// This is called via the call combiner, so access to calld is synchronized. -static void fail_pending_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error) { - call_data *calld = (call_data *)arg; - if (calld->waiting_for_pick_batches_count > 0) { - --calld->waiting_for_pick_batches_count; - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, - calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count], - GRPC_ERROR_REF(error), calld->call_combiner); - } -} - -// This is called via the call combiner, so access to calld is synchronized. -static void waiting_for_pick_batches_fail(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_error *error) { - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: failing %" PRIdPTR " pending batches: %s", - elem->channel_data, calld, calld->waiting_for_pick_batches_count, - grpc_error_string(error)); - } - for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) { - GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i], - fail_pending_batch_in_call_combiner, calld, - grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START(exec_ctx, calld->call_combiner, - &calld->handle_pending_batch_in_call_combiner[i], - GRPC_ERROR_REF(error), - "waiting_for_pick_batches_fail"); - } - if (calld->initial_metadata_batch != NULL) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, calld->initial_metadata_batch, GRPC_ERROR_REF(error), - calld->call_combiner); - } else { - GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, - "waiting_for_pick_batches_fail"); - } - GRPC_ERROR_UNREF(error); -} - -// This is called via the call combiner, so access to calld is synchronized. -static void run_pending_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *ignored) { - call_data *calld = (call_data *)arg; - if (calld->waiting_for_pick_batches_count > 0) { - --calld->waiting_for_pick_batches_count; - grpc_subchannel_call_process_op( - exec_ctx, calld->subchannel_call, - calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count]); - } -} - -// This is called via the call combiner, so access to calld is synchronized. -static void waiting_for_pick_batches_resume(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: sending %" PRIdPTR - " pending batches to subchannel_call=%p", - chand, calld, calld->waiting_for_pick_batches_count, - calld->subchannel_call); - } - for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) { - GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i], - run_pending_batch_in_call_combiner, calld, - grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START(exec_ctx, calld->call_combiner, - &calld->handle_pending_batch_in_call_combiner[i], - GRPC_ERROR_NONE, - "waiting_for_pick_batches_resume"); - } - GPR_ASSERT(calld->initial_metadata_batch != NULL); - grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, - calld->initial_metadata_batch); -} - -// Applies service config to the call. Must be invoked once we know -// that the resolver has returned results to the channel. -static void apply_service_config_to_call_locked(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call", - chand, calld); - } - if (chand->retry_throttle_data != NULL) { - calld->retry_throttle_data = - grpc_server_retry_throttle_data_ref(chand->retry_throttle_data); - } - if (chand->method_params_table != NULL) { - calld->method_params = (method_parameters *)grpc_method_config_table_get( - exec_ctx, chand->method_params_table, calld->path); - if (calld->method_params != NULL) { - method_parameters_ref(calld->method_params); - // If the deadline from the service config is shorter than the one - // from the client API, reset the deadline timer. - if (chand->deadline_checking_enabled && - gpr_time_cmp(calld->method_params->timeout, - gpr_time_0(GPR_TIMESPAN)) != 0) { - const gpr_timespec per_method_deadline = - gpr_time_add(calld->call_start_time, calld->method_params->timeout); - if (gpr_time_cmp(per_method_deadline, calld->deadline) < 0) { - calld->deadline = per_method_deadline; - grpc_deadline_state_reset(exec_ctx, elem, calld->deadline); - } - } - } - } -} - -static void create_subchannel_call_locked(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_error *error) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - const grpc_connected_subchannel_call_args call_args = { - .pollent = calld->pollent, - .path = calld->path, - .start_time = calld->call_start_time, - .deadline = calld->deadline, - .arena = calld->arena, - .context = calld->subchannel_call_context, - .call_combiner = calld->call_combiner}; - grpc_error *new_error = grpc_connected_subchannel_create_call( - exec_ctx, calld->connected_subchannel, &call_args, - &calld->subchannel_call); - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: create subchannel_call=%p: error=%s", - chand, calld, calld->subchannel_call, grpc_error_string(new_error)); - } - if (new_error != GRPC_ERROR_NONE) { - new_error = grpc_error_add_child(new_error, error); - waiting_for_pick_batches_fail(exec_ctx, elem, new_error); - } else { - waiting_for_pick_batches_resume(exec_ctx, elem); - } - GRPC_ERROR_UNREF(error); -} - -// Invoked when a pick is completed, on both success or failure. -static void pick_done_locked(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_error *error) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (calld->connected_subchannel == NULL) { - // Failed to create subchannel. - GRPC_ERROR_UNREF(calld->error); - calld->error = error == GRPC_ERROR_NONE - ? GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Call dropped by load balancing policy") - : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to create subchannel", &error, 1); - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: failed to create subchannel: error=%s", chand, - calld, grpc_error_string(calld->error)); - } - waiting_for_pick_batches_fail(exec_ctx, elem, GRPC_ERROR_REF(calld->error)); - } else { - /* Create call on subchannel. */ - create_subchannel_call_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); - } - GRPC_ERROR_UNREF(error); -} - -// A wrapper around pick_done_locked() that is used in cases where -// either (a) the pick was deferred pending a resolver result or (b) the -// pick was done asynchronously. Removes the call's polling entity from -// chand->interested_parties before invoking pick_done_locked(). -static void async_pick_done_locked(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, grpc_error *error) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - grpc_polling_entity_del_from_pollset_set(exec_ctx, calld->pollent, - chand->interested_parties); - pick_done_locked(exec_ctx, elem, error); -} - -// Note: This runs under the client_channel combiner, but will NOT be -// holding the call combiner. -static void pick_callback_cancel_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (calld->lb_policy != NULL) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p", - chand, calld, calld->lb_policy); - } - grpc_lb_policy_cancel_pick_locked(exec_ctx, calld->lb_policy, - &calld->connected_subchannel, - GRPC_ERROR_REF(error)); - } - GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_callback_cancel"); -} - -// Callback invoked by grpc_lb_policy_pick_locked() for async picks. -// Unrefs the LB policy and invokes async_pick_done_locked(). -static void pick_callback_done_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed asynchronously", - chand, calld); - } - GPR_ASSERT(calld->lb_policy != NULL); - GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); - calld->lb_policy = NULL; - async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); -} - -// Takes a ref to chand->lb_policy and calls grpc_lb_policy_pick_locked(). -// If the pick was completed synchronously, unrefs the LB policy and -// returns true. -static bool pick_callback_start_locked(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p", - chand, calld, chand->lb_policy); - } - apply_service_config_to_call_locked(exec_ctx, elem); - // If the application explicitly set wait_for_ready, use that. - // Otherwise, if the service config specified a value for this - // method, use that. - uint32_t initial_metadata_flags = - calld->initial_metadata_batch->payload->send_initial_metadata - .send_initial_metadata_flags; - const bool wait_for_ready_set_from_api = - initial_metadata_flags & - GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET; - const bool wait_for_ready_set_from_service_config = - calld->method_params != NULL && - calld->method_params->wait_for_ready != WAIT_FOR_READY_UNSET; - if (!wait_for_ready_set_from_api && wait_for_ready_set_from_service_config) { - if (calld->method_params->wait_for_ready == WAIT_FOR_READY_TRUE) { - initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; - } else { - initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; - } - } - const grpc_lb_policy_pick_args inputs = { - calld->initial_metadata_batch->payload->send_initial_metadata - .send_initial_metadata, - initial_metadata_flags, &calld->lb_token_mdelem}; - // Keep a ref to the LB policy in calld while the pick is pending. - GRPC_LB_POLICY_REF(chand->lb_policy, "pick_subchannel"); - calld->lb_policy = chand->lb_policy; - GRPC_CLOSURE_INIT(&calld->lb_pick_closure, pick_callback_done_locked, elem, - grpc_combiner_scheduler(chand->combiner)); - const bool pick_done = grpc_lb_policy_pick_locked( - exec_ctx, chand->lb_policy, &inputs, &calld->connected_subchannel, - calld->subchannel_call_context, NULL, &calld->lb_pick_closure); - if (pick_done) { - /* synchronous grpc_lb_policy_pick call. Unref the LB policy. */ - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously", - chand, calld); - } - GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); - calld->lb_policy = NULL; - } else { - GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback_cancel"); - grpc_call_combiner_set_notify_on_cancel( - exec_ctx, calld->call_combiner, - GRPC_CLOSURE_INIT(&calld->lb_pick_cancel_closure, - pick_callback_cancel_locked, elem, - grpc_combiner_scheduler(chand->combiner))); - } - return pick_done; -} - -typedef struct { - grpc_call_element *elem; - bool finished; - grpc_closure closure; - grpc_closure cancel_closure; -} pick_after_resolver_result_args; - -// Note: This runs under the client_channel combiner, but will NOT be -// holding the call combiner. -static void pick_after_resolver_result_cancel_locked(grpc_exec_ctx *exec_ctx, - void *arg, - grpc_error *error) { - pick_after_resolver_result_args *args = - (pick_after_resolver_result_args *)arg; - if (args->finished) { - gpr_free(args); - return; - } - // If we don't yet have a resolver result, then a closure for - // pick_after_resolver_result_done_locked() will have been added to - // chand->waiting_for_resolver_result_closures, and it may not be invoked - // until after this call has been destroyed. We mark the operation as - // finished, so that when pick_after_resolver_result_done_locked() - // is called, it will be a no-op. We also immediately invoke - // async_pick_done_locked() to propagate the error back to the caller. - args->finished = true; - grpc_call_element *elem = args->elem; - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: cancelling pick waiting for resolver result", - chand, calld); - } - // Note: Although we are not in the call combiner here, we are - // basically stealing the call combiner from the pending pick, so - // it's safe to call async_pick_done_locked() here -- we are - // essentially calling it here instead of calling it in - // pick_after_resolver_result_done_locked(). - async_pick_done_locked(exec_ctx, elem, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick cancelled", &error, 1)); -} - -static void pick_after_resolver_result_done_locked(grpc_exec_ctx *exec_ctx, - void *arg, - grpc_error *error) { - pick_after_resolver_result_args *args = - (pick_after_resolver_result_args *)arg; - if (args->finished) { - /* cancelled, do nothing */ - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "call cancelled before resolver result"); - } - gpr_free(args); - return; - } - args->finished = true; - grpc_call_element *elem = args->elem; - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (error != GRPC_ERROR_NONE) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver failed to return data", - chand, calld); - } - async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); - } else { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver returned, doing pick", - chand, calld); - } - if (pick_callback_start_locked(exec_ctx, elem)) { - // Even if the LB policy returns a result synchronously, we have - // already added our polling entity to chand->interested_parties - // in order to wait for the resolver result, so we need to - // remove it here. Therefore, we call async_pick_done_locked() - // instead of pick_done_locked(). - async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_NONE); - } - } -} - -static void pick_after_resolver_result_start_locked(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: deferring pick pending resolver result", chand, - calld); - } - pick_after_resolver_result_args *args = - (pick_after_resolver_result_args *)gpr_zalloc(sizeof(*args)); - args->elem = elem; - GRPC_CLOSURE_INIT(&args->closure, pick_after_resolver_result_done_locked, - args, grpc_combiner_scheduler(chand->combiner)); - grpc_closure_list_append(&chand->waiting_for_resolver_result_closures, - &args->closure, GRPC_ERROR_NONE); - grpc_call_combiner_set_notify_on_cancel( - exec_ctx, calld->call_combiner, - GRPC_CLOSURE_INIT(&args->cancel_closure, - pick_after_resolver_result_cancel_locked, args, - grpc_combiner_scheduler(chand->combiner))); -} - -static void start_pick_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *ignored) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - GPR_ASSERT(calld->connected_subchannel == NULL); - if (chand->lb_policy != NULL) { - // We already have an LB policy, so ask it for a pick. - if (pick_callback_start_locked(exec_ctx, elem)) { - // Pick completed synchronously. - pick_done_locked(exec_ctx, elem, GRPC_ERROR_NONE); - return; - } - } else { - // We do not yet have an LB policy, so wait for a resolver result. - if (chand->resolver == NULL) { - pick_done_locked(exec_ctx, elem, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected")); - return; - } - if (!chand->started_resolving) { - start_resolving_locked(exec_ctx, chand); - } - pick_after_resolver_result_start_locked(exec_ctx, elem); - } - // We need to wait for either a resolver result or for an async result - // from the LB policy. Add the polling entity from call_data to the - // channel_data's interested_parties, so that the I/O of the LB policy - // and resolver can be done under it. The polling entity will be - // removed in async_pick_done_locked(). - grpc_polling_entity_add_to_pollset_set(exec_ctx, calld->pollent, - chand->interested_parties); -} - -static void on_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - if (calld->retry_throttle_data != NULL) { - if (error == GRPC_ERROR_NONE) { - grpc_server_retry_throttle_data_record_success( - calld->retry_throttle_data); - } else { - // TODO(roth): In a subsequent PR, check the return value here and - // decide whether or not to retry. Note that we should only - // record failures whose statuses match the configured retryable - // or non-fatal status codes. - grpc_server_retry_throttle_data_record_failure( - calld->retry_throttle_data); - } - } - GRPC_CLOSURE_RUN(exec_ctx, calld->original_on_complete, - GRPC_ERROR_REF(error)); -} - -static void cc_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (chand->deadline_checking_enabled) { - grpc_deadline_state_client_start_transport_stream_op_batch(exec_ctx, elem, - batch); - } - GPR_TIMER_BEGIN("cc_start_transport_stream_op_batch", 0); - // If we've previously been cancelled, immediately fail any new batches. - if (calld->error != GRPC_ERROR_NONE) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing batch with error: %s", - chand, calld, grpc_error_string(calld->error)); - } - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, batch, GRPC_ERROR_REF(calld->error), calld->call_combiner); - goto done; - } - if (batch->cancel_stream) { - // Stash a copy of cancel_error in our call data, so that we can use - // it for subsequent operations. This ensures that if the call is - // cancelled before any batches are passed down (e.g., if the deadline - // is in the past when the call starts), we can return the right - // error to the caller when the first batch does get passed down. - GRPC_ERROR_UNREF(calld->error); - calld->error = GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: recording cancel_error=%s", chand, - calld, grpc_error_string(calld->error)); - } - // If we have a subchannel call, send the cancellation batch down. - // Otherwise, fail all pending batches. - if (calld->subchannel_call != NULL) { - grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, batch); - } else { - waiting_for_pick_batches_add(calld, batch); - waiting_for_pick_batches_fail(exec_ctx, elem, - GRPC_ERROR_REF(calld->error)); - } - goto done; - } - // Intercept on_complete for recv_trailing_metadata so that we can - // check retry throttle status. - if (batch->recv_trailing_metadata) { - GPR_ASSERT(batch->on_complete != NULL); - calld->original_on_complete = batch->on_complete; - GRPC_CLOSURE_INIT(&calld->on_complete, on_complete, elem, - grpc_schedule_on_exec_ctx); - batch->on_complete = &calld->on_complete; - } - // Check if we've already gotten a subchannel call. - // Note that once we have completed the pick, we do not need to enter - // the channel combiner, which is more efficient (especially for - // streaming calls). - if (calld->subchannel_call != NULL) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: sending batch to subchannel_call=%p", chand, - calld, calld->subchannel_call); - } - grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, batch); - goto done; - } - // We do not yet have a subchannel call. - // Add the batch to the waiting-for-pick list. - waiting_for_pick_batches_add(calld, batch); - // For batches containing a send_initial_metadata op, enter the channel - // combiner to start a pick. - if (batch->send_initial_metadata) { - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, "chand=%p calld=%p: entering client_channel combiner", - chand, calld); - } - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&batch->handler_private.closure, start_pick_locked, - elem, grpc_combiner_scheduler(chand->combiner)), - GRPC_ERROR_NONE); - } else { - // For all other batches, release the call combiner. - if (GRPC_TRACER_ON(grpc_client_channel_trace)) { - gpr_log(GPR_DEBUG, - "chand=%p calld=%p: saved batch, yeilding call combiner", chand, - calld); - } - GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, - "batch does not include send_initial_metadata"); - } -done: - GPR_TIMER_END("cc_start_transport_stream_op_batch", 0); -} - -/* Constructor for call_data */ -static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - // Initialize data members. - calld->path = grpc_slice_ref_internal(args->path); - calld->call_start_time = args->start_time; - calld->deadline = gpr_convert_clock_type(args->deadline, GPR_CLOCK_MONOTONIC); - calld->arena = args->arena; - calld->owning_call = args->call_stack; - calld->call_combiner = args->call_combiner; - if (chand->deadline_checking_enabled) { - grpc_deadline_state_init(exec_ctx, elem, args->call_stack, - args->call_combiner, calld->deadline); - } - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data */ -static void cc_destroy_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *then_schedule_closure) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (chand->deadline_checking_enabled) { - grpc_deadline_state_destroy(exec_ctx, elem); - } - grpc_slice_unref_internal(exec_ctx, calld->path); - if (calld->method_params != NULL) { - method_parameters_unref(calld->method_params); - } - GRPC_ERROR_UNREF(calld->error); - if (calld->subchannel_call != NULL) { - grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call, - then_schedule_closure); - then_schedule_closure = NULL; - GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, calld->subchannel_call, - "client_channel_destroy_call"); - } - GPR_ASSERT(calld->lb_policy == NULL); - GPR_ASSERT(calld->waiting_for_pick_batches_count == 0); - if (calld->connected_subchannel != NULL) { - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, calld->connected_subchannel, - "picked"); - } - for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) { - if (calld->subchannel_call_context[i].value != NULL) { - calld->subchannel_call_context[i].destroy( - calld->subchannel_call_context[i].value); - } - } - GRPC_CLOSURE_SCHED(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE); -} - -static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_polling_entity *pollent) { - call_data *calld = (call_data *)elem->call_data; - calld->pollent = pollent; -} - -/************************************************************************* - * EXPORTED SYMBOLS - */ - -const grpc_channel_filter grpc_client_channel_filter = { - cc_start_transport_stream_op_batch, - cc_start_transport_op, - sizeof(call_data), - cc_init_call_elem, - cc_set_pollset_or_pollset_set, - cc_destroy_call_elem, - sizeof(channel_data), - cc_init_channel_elem, - cc_destroy_channel_elem, - cc_get_channel_info, - "client-channel", -}; - -static void try_to_connect_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error_ignored) { - channel_data *chand = (channel_data *)arg; - if (chand->lb_policy != NULL) { - grpc_lb_policy_exit_idle_locked(exec_ctx, chand->lb_policy); - } else { - chand->exit_idle_when_lb_policy_arrives = true; - if (!chand->started_resolving && chand->resolver != NULL) { - start_resolving_locked(exec_ctx, chand); - } - } - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "try_to_connect"); -} - -grpc_connectivity_state grpc_client_channel_check_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) { - channel_data *chand = (channel_data *)elem->channel_data; - grpc_connectivity_state out = - grpc_connectivity_state_check(&chand->state_tracker); - if (out == GRPC_CHANNEL_IDLE && try_to_connect) { - GRPC_CHANNEL_STACK_REF(chand->owning_stack, "try_to_connect"); - GRPC_CLOSURE_SCHED( - exec_ctx, GRPC_CLOSURE_CREATE(try_to_connect_locked, chand, - grpc_combiner_scheduler(chand->combiner)), - GRPC_ERROR_NONE); - } - return out; -} - -typedef struct external_connectivity_watcher { - channel_data *chand; - grpc_polling_entity pollent; - grpc_closure *on_complete; - grpc_closure *watcher_timer_init; - grpc_connectivity_state *state; - grpc_closure my_closure; - struct external_connectivity_watcher *next; -} external_connectivity_watcher; - -static external_connectivity_watcher *lookup_external_connectivity_watcher( - channel_data *chand, grpc_closure *on_complete) { - gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); - external_connectivity_watcher *w = - chand->external_connectivity_watcher_list_head; - while (w != NULL && w->on_complete != on_complete) { - w = w->next; - } - gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); - return w; -} - -static void external_connectivity_watcher_list_append( - channel_data *chand, external_connectivity_watcher *w) { - GPR_ASSERT(!lookup_external_connectivity_watcher(chand, w->on_complete)); - - gpr_mu_lock(&w->chand->external_connectivity_watcher_list_mu); - GPR_ASSERT(!w->next); - w->next = chand->external_connectivity_watcher_list_head; - chand->external_connectivity_watcher_list_head = w; - gpr_mu_unlock(&w->chand->external_connectivity_watcher_list_mu); -} - -static void external_connectivity_watcher_list_remove( - channel_data *chand, external_connectivity_watcher *too_remove) { - GPR_ASSERT( - lookup_external_connectivity_watcher(chand, too_remove->on_complete)); - gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); - if (too_remove == chand->external_connectivity_watcher_list_head) { - chand->external_connectivity_watcher_list_head = too_remove->next; - gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); - return; - } - external_connectivity_watcher *w = - chand->external_connectivity_watcher_list_head; - while (w != NULL) { - if (w->next == too_remove) { - w->next = w->next->next; - gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); - return; - } - w = w->next; - } - GPR_UNREACHABLE_CODE(return ); -} - -int grpc_client_channel_num_external_connectivity_watchers( - grpc_channel_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - int count = 0; - - gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); - external_connectivity_watcher *w = - chand->external_connectivity_watcher_list_head; - while (w != NULL) { - count++; - w = w->next; - } - gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); - - return count; -} - -static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - external_connectivity_watcher *w = (external_connectivity_watcher *)arg; - grpc_closure *follow_up = w->on_complete; - grpc_polling_entity_del_from_pollset_set(exec_ctx, &w->pollent, - w->chand->interested_parties); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, - "external_connectivity_watcher"); - external_connectivity_watcher_list_remove(w->chand, w); - gpr_free(w); - GRPC_CLOSURE_RUN(exec_ctx, follow_up, GRPC_ERROR_REF(error)); -} - -static void watch_connectivity_state_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error_ignored) { - external_connectivity_watcher *w = (external_connectivity_watcher *)arg; - external_connectivity_watcher *found = NULL; - if (w->state != NULL) { - external_connectivity_watcher_list_append(w->chand, w); - GRPC_CLOSURE_RUN(exec_ctx, w->watcher_timer_init, GRPC_ERROR_NONE); - GRPC_CLOSURE_INIT(&w->my_closure, on_external_watch_complete, w, - grpc_schedule_on_exec_ctx); - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &w->chand->state_tracker, w->state, &w->my_closure); - } else { - GPR_ASSERT(w->watcher_timer_init == NULL); - found = lookup_external_connectivity_watcher(w->chand, w->on_complete); - if (found) { - GPR_ASSERT(found->on_complete == w->on_complete); - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &found->chand->state_tracker, NULL, &found->my_closure); - } - grpc_polling_entity_del_from_pollset_set(exec_ctx, &w->pollent, - w->chand->interested_parties); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, - "external_connectivity_watcher"); - gpr_free(w); - } -} - -void grpc_client_channel_watch_connectivity_state( - grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, - grpc_polling_entity pollent, grpc_connectivity_state *state, - grpc_closure *closure, grpc_closure *watcher_timer_init) { - channel_data *chand = (channel_data *)elem->channel_data; - external_connectivity_watcher *w = - (external_connectivity_watcher *)gpr_zalloc(sizeof(*w)); - w->chand = chand; - w->pollent = pollent; - w->on_complete = closure; - w->state = state; - w->watcher_timer_init = watcher_timer_init; - grpc_polling_entity_add_to_pollset_set(exec_ctx, &w->pollent, - chand->interested_parties); - GRPC_CHANNEL_STACK_REF(w->chand->owning_stack, - "external_connectivity_watcher"); - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&w->my_closure, watch_connectivity_state_locked, w, - grpc_combiner_scheduler(chand->combiner)), - GRPC_ERROR_NONE); -} diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc new file mode 100644 index 0000000000..016199b1f4 --- /dev/null +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -0,0 +1,1657 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/client_channel.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/deadline/deadline_filter.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/service_config.h" +#include "src/core/lib/transport/static_metadata.h" + +/* Client channel implementation */ + +grpc_tracer_flag grpc_client_channel_trace = + GRPC_TRACER_INITIALIZER(false, "client_channel"); + +/************************************************************************* + * METHOD-CONFIG TABLE + */ + +typedef enum { + /* zero so it can be default initialized */ + WAIT_FOR_READY_UNSET = 0, + WAIT_FOR_READY_FALSE, + WAIT_FOR_READY_TRUE +} wait_for_ready_value; + +typedef struct { + gpr_refcount refs; + gpr_timespec timeout; + wait_for_ready_value wait_for_ready; +} method_parameters; + +static method_parameters *method_parameters_ref( + method_parameters *method_params) { + gpr_ref(&method_params->refs); + return method_params; +} + +static void method_parameters_unref(method_parameters *method_params) { + if (gpr_unref(&method_params->refs)) { + gpr_free(method_params); + } +} + +static void method_parameters_free(grpc_exec_ctx *exec_ctx, void *value) { + method_parameters_unref((method_parameters *)value); +} + +static bool parse_wait_for_ready(grpc_json *field, + wait_for_ready_value *wait_for_ready) { + if (field->type != GRPC_JSON_TRUE && field->type != GRPC_JSON_FALSE) { + return false; + } + *wait_for_ready = field->type == GRPC_JSON_TRUE ? WAIT_FOR_READY_TRUE + : WAIT_FOR_READY_FALSE; + return true; +} + +static bool parse_timeout(grpc_json *field, gpr_timespec *timeout) { + if (field->type != GRPC_JSON_STRING) return false; + size_t len = strlen(field->value); + if (field->value[len - 1] != 's') return false; + char *buf = gpr_strdup(field->value); + buf[len - 1] = '\0'; // Remove trailing 's'. + char *decimal_point = strchr(buf, '.'); + if (decimal_point != NULL) { + *decimal_point = '\0'; + timeout->tv_nsec = gpr_parse_nonnegative_int(decimal_point + 1); + if (timeout->tv_nsec == -1) { + gpr_free(buf); + return false; + } + // There should always be exactly 3, 6, or 9 fractional digits. + int multiplier = 1; + switch (strlen(decimal_point + 1)) { + case 9: + break; + case 6: + multiplier *= 1000; + break; + case 3: + multiplier *= 1000000; + break; + default: // Unsupported number of digits. + gpr_free(buf); + return false; + } + timeout->tv_nsec *= multiplier; + } + timeout->tv_sec = gpr_parse_nonnegative_int(buf); + gpr_free(buf); + if (timeout->tv_sec == -1) return false; + return true; +} + +static void *method_parameters_create_from_json(const grpc_json *json) { + wait_for_ready_value wait_for_ready = WAIT_FOR_READY_UNSET; + gpr_timespec timeout = {0, 0, GPR_TIMESPAN}; + for (grpc_json *field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) continue; + if (strcmp(field->key, "waitForReady") == 0) { + if (wait_for_ready != WAIT_FOR_READY_UNSET) return NULL; // Duplicate. + if (!parse_wait_for_ready(field, &wait_for_ready)) return NULL; + } else if (strcmp(field->key, "timeout") == 0) { + if (timeout.tv_sec > 0 || timeout.tv_nsec > 0) return NULL; // Duplicate. + if (!parse_timeout(field, &timeout)) return NULL; + } + } + method_parameters *value = + (method_parameters *)gpr_malloc(sizeof(method_parameters)); + gpr_ref_init(&value->refs, 1); + value->timeout = timeout; + value->wait_for_ready = wait_for_ready; + return value; +} + +struct external_connectivity_watcher; + +/************************************************************************* + * CHANNEL-WIDE FUNCTIONS + */ + +typedef struct client_channel_channel_data { + /** resolver for this channel */ + grpc_resolver *resolver; + /** have we started resolving this channel */ + bool started_resolving; + /** is deadline checking enabled? */ + bool deadline_checking_enabled; + /** client channel factory */ + grpc_client_channel_factory *client_channel_factory; + + /** combiner protecting all variables below in this data structure */ + grpc_combiner *combiner; + /** currently active load balancer */ + grpc_lb_policy *lb_policy; + /** retry throttle data */ + grpc_server_retry_throttle_data *retry_throttle_data; + /** maps method names to method_parameters structs */ + grpc_slice_hash_table *method_params_table; + /** incoming resolver result - set by resolver.next() */ + grpc_channel_args *resolver_result; + /** a list of closures that are all waiting for resolver result to come in */ + grpc_closure_list waiting_for_resolver_result_closures; + /** resolver callback */ + grpc_closure on_resolver_result_changed; + /** connectivity state being tracked */ + grpc_connectivity_state_tracker state_tracker; + /** when an lb_policy arrives, should we try to exit idle */ + bool exit_idle_when_lb_policy_arrives; + /** owning stack */ + grpc_channel_stack *owning_stack; + /** interested parties (owned) */ + grpc_pollset_set *interested_parties; + + /* external_connectivity_watcher_list head is guarded by its own mutex, since + * counts need to be grabbed immediately without polling on a cq */ + gpr_mu external_connectivity_watcher_list_mu; + struct external_connectivity_watcher *external_connectivity_watcher_list_head; + + /* the following properties are guarded by a mutex since API's require them + to be instantaneously available */ + gpr_mu info_mu; + char *info_lb_policy_name; + /** service config in JSON form */ + char *info_service_config_json; +} channel_data; + +/** We create one watcher for each new lb_policy that is returned from a + resolver, to watch for state changes from the lb_policy. When a state + change is seen, we update the channel, and create a new watcher. */ +typedef struct { + channel_data *chand; + grpc_closure on_changed; + grpc_connectivity_state state; + grpc_lb_policy *lb_policy; +} lb_policy_connectivity_watcher; + +static void watch_lb_policy_locked(grpc_exec_ctx *exec_ctx, channel_data *chand, + grpc_lb_policy *lb_policy, + grpc_connectivity_state current_state); + +static void set_channel_connectivity_state_locked(grpc_exec_ctx *exec_ctx, + channel_data *chand, + grpc_connectivity_state state, + grpc_error *error, + const char *reason) { + /* TODO: Improve failure handling: + * - Make it possible for policies to return GRPC_CHANNEL_TRANSIENT_FAILURE. + * - Hand over pending picks from old policies during the switch that happens + * when resolver provides an update. */ + if (chand->lb_policy != NULL) { + if (state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + /* cancel picks with wait_for_ready=false */ + grpc_lb_policy_cancel_picks_locked( + exec_ctx, chand->lb_policy, + /* mask= */ GRPC_INITIAL_METADATA_WAIT_FOR_READY, + /* check= */ 0, GRPC_ERROR_REF(error)); + } else if (state == GRPC_CHANNEL_SHUTDOWN) { + /* cancel all picks */ + grpc_lb_policy_cancel_picks_locked(exec_ctx, chand->lb_policy, + /* mask= */ 0, /* check= */ 0, + GRPC_ERROR_REF(error)); + } + } + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: setting connectivity state to %s", chand, + grpc_connectivity_state_name(state)); + } + grpc_connectivity_state_set(exec_ctx, &chand->state_tracker, state, error, + reason); +} + +static void on_lb_policy_state_changed_locked(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error) { + lb_policy_connectivity_watcher *w = (lb_policy_connectivity_watcher *)arg; + grpc_connectivity_state publish_state = w->state; + /* check if the notification is for the latest policy */ + if (w->lb_policy == w->chand->lb_policy) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: lb_policy=%p state changed to %s", w->chand, + w->lb_policy, grpc_connectivity_state_name(w->state)); + } + if (publish_state == GRPC_CHANNEL_SHUTDOWN && w->chand->resolver != NULL) { + publish_state = GRPC_CHANNEL_TRANSIENT_FAILURE; + grpc_resolver_channel_saw_error_locked(exec_ctx, w->chand->resolver); + GRPC_LB_POLICY_UNREF(exec_ctx, w->chand->lb_policy, "channel"); + w->chand->lb_policy = NULL; + } + set_channel_connectivity_state_locked(exec_ctx, w->chand, publish_state, + GRPC_ERROR_REF(error), "lb_changed"); + if (w->state != GRPC_CHANNEL_SHUTDOWN) { + watch_lb_policy_locked(exec_ctx, w->chand, w->lb_policy, w->state); + } + } + GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, "watch_lb_policy"); + gpr_free(w); +} + +static void watch_lb_policy_locked(grpc_exec_ctx *exec_ctx, channel_data *chand, + grpc_lb_policy *lb_policy, + grpc_connectivity_state current_state) { + lb_policy_connectivity_watcher *w = + (lb_policy_connectivity_watcher *)gpr_malloc(sizeof(*w)); + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "watch_lb_policy"); + w->chand = chand; + GRPC_CLOSURE_INIT(&w->on_changed, on_lb_policy_state_changed_locked, w, + grpc_combiner_scheduler(chand->combiner)); + w->state = current_state; + w->lb_policy = lb_policy; + grpc_lb_policy_notify_on_state_change_locked(exec_ctx, lb_policy, &w->state, + &w->on_changed); +} + +static void start_resolving_locked(grpc_exec_ctx *exec_ctx, + channel_data *chand) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: starting name resolution", chand); + } + GPR_ASSERT(!chand->started_resolving); + chand->started_resolving = true; + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "resolver"); + grpc_resolver_next_locked(exec_ctx, chand->resolver, &chand->resolver_result, + &chand->on_resolver_result_changed); +} + +typedef struct { + char *server_name; + grpc_server_retry_throttle_data *retry_throttle_data; +} service_config_parsing_state; + +static void parse_retry_throttle_params(const grpc_json *field, void *arg) { + service_config_parsing_state *parsing_state = + (service_config_parsing_state *)arg; + if (strcmp(field->key, "retryThrottling") == 0) { + if (parsing_state->retry_throttle_data != NULL) return; // Duplicate. + if (field->type != GRPC_JSON_OBJECT) return; + int max_milli_tokens = 0; + int milli_token_ratio = 0; + for (grpc_json *sub_field = field->child; sub_field != NULL; + sub_field = sub_field->next) { + if (sub_field->key == NULL) return; + if (strcmp(sub_field->key, "maxTokens") == 0) { + if (max_milli_tokens != 0) return; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return; + max_milli_tokens = gpr_parse_nonnegative_int(sub_field->value); + if (max_milli_tokens == -1) return; + max_milli_tokens *= 1000; + } else if (strcmp(sub_field->key, "tokenRatio") == 0) { + if (milli_token_ratio != 0) return; // Duplicate. + if (sub_field->type != GRPC_JSON_NUMBER) return; + // We support up to 3 decimal digits. + size_t whole_len = strlen(sub_field->value); + uint32_t multiplier = 1; + uint32_t decimal_value = 0; + const char *decimal_point = strchr(sub_field->value, '.'); + if (decimal_point != NULL) { + whole_len = (size_t)(decimal_point - sub_field->value); + multiplier = 1000; + size_t decimal_len = strlen(decimal_point + 1); + if (decimal_len > 3) decimal_len = 3; + if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len, + &decimal_value)) { + return; + } + uint32_t decimal_multiplier = 1; + for (size_t i = 0; i < (3 - decimal_len); ++i) { + decimal_multiplier *= 10; + } + decimal_value *= decimal_multiplier; + } + uint32_t whole_value; + if (!gpr_parse_bytes_to_uint32(sub_field->value, whole_len, + &whole_value)) { + return; + } + milli_token_ratio = (int)((whole_value * multiplier) + decimal_value); + if (milli_token_ratio <= 0) return; + } + } + parsing_state->retry_throttle_data = + grpc_retry_throttle_map_get_data_for_server( + parsing_state->server_name, max_milli_tokens, milli_token_ratio); + } +} + +static void on_resolver_result_changed_locked(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error) { + channel_data *chand = (channel_data *)arg; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: got resolver result: error=%s", chand, + grpc_error_string(error)); + } + // Extract the following fields from the resolver result, if non-NULL. + bool lb_policy_updated = false; + char *lb_policy_name_dup = NULL; + bool lb_policy_name_changed = false; + grpc_lb_policy *new_lb_policy = NULL; + char *service_config_json = NULL; + grpc_server_retry_throttle_data *retry_throttle_data = NULL; + grpc_slice_hash_table *method_params_table = NULL; + if (chand->resolver_result != NULL) { + // Find LB policy name. + const char *lb_policy_name = NULL; + const grpc_arg *channel_arg = + grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_POLICY_NAME); + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + lb_policy_name = channel_arg->value.string; + } + // Special case: If at least one balancer address is present, we use + // the grpclb policy, regardless of what the resolver actually specified. + channel_arg = + grpc_channel_args_find(chand->resolver_result, GRPC_ARG_LB_ADDRESSES); + if (channel_arg != NULL && channel_arg->type == GRPC_ARG_POINTER) { + grpc_lb_addresses *addresses = + (grpc_lb_addresses *)channel_arg->value.pointer.p; + bool found_balancer_address = false; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (addresses->addresses[i].is_balancer) { + found_balancer_address = true; + break; + } + } + if (found_balancer_address) { + if (lb_policy_name != NULL && strcmp(lb_policy_name, "grpclb") != 0) { + gpr_log(GPR_INFO, + "resolver requested LB policy %s but provided at least one " + "balancer address -- forcing use of grpclb LB policy", + lb_policy_name); + } + lb_policy_name = "grpclb"; + } + } + // Use pick_first if nothing was specified and we didn't select grpclb + // above. + if (lb_policy_name == NULL) lb_policy_name = "pick_first"; + grpc_lb_policy_args lb_policy_args; + lb_policy_args.args = chand->resolver_result; + lb_policy_args.client_channel_factory = chand->client_channel_factory; + lb_policy_args.combiner = chand->combiner; + // Check to see if we're already using the right LB policy. + // Note: It's safe to use chand->info_lb_policy_name here without + // taking a lock on chand->info_mu, because this function is the + // only thing that modifies its value, and it can only be invoked + // once at any given time. + lb_policy_name_changed = + chand->info_lb_policy_name == NULL || + strcmp(chand->info_lb_policy_name, lb_policy_name) != 0; + if (chand->lb_policy != NULL && !lb_policy_name_changed) { + // Continue using the same LB policy. Update with new addresses. + lb_policy_updated = true; + grpc_lb_policy_update_locked(exec_ctx, chand->lb_policy, &lb_policy_args); + } else { + // Instantiate new LB policy. + new_lb_policy = + grpc_lb_policy_create(exec_ctx, lb_policy_name, &lb_policy_args); + if (new_lb_policy == NULL) { + gpr_log(GPR_ERROR, "could not create LB policy \"%s\"", lb_policy_name); + } + } + // Find service config. + channel_arg = + grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVICE_CONFIG); + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + service_config_json = gpr_strdup(channel_arg->value.string); + grpc_service_config *service_config = + grpc_service_config_create(service_config_json); + if (service_config != NULL) { + channel_arg = + grpc_channel_args_find(chand->resolver_result, GRPC_ARG_SERVER_URI); + GPR_ASSERT(channel_arg != NULL); + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + grpc_uri *uri = + grpc_uri_parse(exec_ctx, channel_arg->value.string, true); + GPR_ASSERT(uri->path[0] != '\0'); + service_config_parsing_state parsing_state; + memset(&parsing_state, 0, sizeof(parsing_state)); + parsing_state.server_name = + uri->path[0] == '/' ? uri->path + 1 : uri->path; + grpc_service_config_parse_global_params( + service_config, parse_retry_throttle_params, &parsing_state); + grpc_uri_destroy(uri); + retry_throttle_data = parsing_state.retry_throttle_data; + method_params_table = grpc_service_config_create_method_config_table( + exec_ctx, service_config, method_parameters_create_from_json, + method_parameters_free); + grpc_service_config_destroy(service_config); + } + } + // Before we clean up, save a copy of lb_policy_name, since it might + // be pointing to data inside chand->resolver_result. + // The copy will be saved in chand->lb_policy_name below. + lb_policy_name_dup = gpr_strdup(lb_policy_name); + grpc_channel_args_destroy(exec_ctx, chand->resolver_result); + chand->resolver_result = NULL; + } + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p: resolver result: lb_policy_name=\"%s\"%s, " + "service_config=\"%s\"", + chand, lb_policy_name_dup, + lb_policy_name_changed ? " (changed)" : "", service_config_json); + } + // Now swap out fields in chand. Note that the new values may still + // be NULL if (e.g.) the resolver failed to return results or the + // results did not contain the necessary data. + // + // First, swap out the data used by cc_get_channel_info(). + gpr_mu_lock(&chand->info_mu); + if (lb_policy_name_dup != NULL) { + gpr_free(chand->info_lb_policy_name); + chand->info_lb_policy_name = lb_policy_name_dup; + } + if (service_config_json != NULL) { + gpr_free(chand->info_service_config_json); + chand->info_service_config_json = service_config_json; + } + gpr_mu_unlock(&chand->info_mu); + // Swap out the retry throttle data. + if (chand->retry_throttle_data != NULL) { + grpc_server_retry_throttle_data_unref(chand->retry_throttle_data); + } + chand->retry_throttle_data = retry_throttle_data; + // Swap out the method params table. + if (chand->method_params_table != NULL) { + grpc_slice_hash_table_unref(exec_ctx, chand->method_params_table); + } + chand->method_params_table = method_params_table; + // If we have a new LB policy or are shutting down (in which case + // new_lb_policy will be NULL), swap out the LB policy, unreffing the + // old one and removing its fds from chand->interested_parties. + // Note that we do NOT do this if either (a) we updated the existing + // LB policy above or (b) we failed to create the new LB policy (in + // which case we want to continue using the most recent one we had). + if (new_lb_policy != NULL || error != GRPC_ERROR_NONE || + chand->resolver == NULL) { + if (chand->lb_policy != NULL) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: unreffing lb_policy=%p", chand, + chand->lb_policy); + } + grpc_pollset_set_del_pollset_set(exec_ctx, + chand->lb_policy->interested_parties, + chand->interested_parties); + GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); + } + chand->lb_policy = new_lb_policy; + } + // Now that we've swapped out the relevant fields of chand, check for + // error or shutdown. + if (error != GRPC_ERROR_NONE || chand->resolver == NULL) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: shutting down", chand); + } + if (chand->resolver != NULL) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: shutting down resolver", chand); + } + grpc_resolver_shutdown_locked(exec_ctx, chand->resolver); + GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); + chand->resolver = NULL; + } + set_channel_connectivity_state_locked( + exec_ctx, chand, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Got resolver result after disconnection", &error, 1), + "resolver_gone"); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "resolver"); + grpc_closure_list_fail_all(&chand->waiting_for_resolver_result_closures, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Channel disconnected", &error, 1)); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, + &chand->waiting_for_resolver_result_closures); + } else { // Not shutting down. + grpc_connectivity_state state = GRPC_CHANNEL_TRANSIENT_FAILURE; + grpc_error *state_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("No load balancing policy"); + if (new_lb_policy != NULL) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p: initializing new LB policy", chand); + } + GRPC_ERROR_UNREF(state_error); + state = grpc_lb_policy_check_connectivity_locked(exec_ctx, new_lb_policy, + &state_error); + grpc_pollset_set_add_pollset_set(exec_ctx, + new_lb_policy->interested_parties, + chand->interested_parties); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, + &chand->waiting_for_resolver_result_closures); + if (chand->exit_idle_when_lb_policy_arrives) { + grpc_lb_policy_exit_idle_locked(exec_ctx, new_lb_policy); + chand->exit_idle_when_lb_policy_arrives = false; + } + watch_lb_policy_locked(exec_ctx, chand, new_lb_policy, state); + } + if (!lb_policy_updated) { + set_channel_connectivity_state_locked(exec_ctx, chand, state, + GRPC_ERROR_REF(state_error), + "new_lb+resolver"); + } + grpc_resolver_next_locked(exec_ctx, chand->resolver, + &chand->resolver_result, + &chand->on_resolver_result_changed); + GRPC_ERROR_UNREF(state_error); + } +} + +static void start_transport_op_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error_ignored) { + grpc_transport_op *op = (grpc_transport_op *)arg; + grpc_channel_element *elem = + (grpc_channel_element *)op->handler_private.extra_arg; + channel_data *chand = (channel_data *)elem->channel_data; + + if (op->on_connectivity_state_change != NULL) { + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &chand->state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + op->on_connectivity_state_change = NULL; + op->connectivity_state = NULL; + } + + if (op->send_ping != NULL) { + if (chand->lb_policy == NULL) { + GRPC_CLOSURE_SCHED( + exec_ctx, op->send_ping, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Ping with no load balancing")); + } else { + grpc_lb_policy_ping_one_locked(exec_ctx, chand->lb_policy, op->send_ping); + op->bind_pollset = NULL; + } + op->send_ping = NULL; + } + + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + if (chand->resolver != NULL) { + set_channel_connectivity_state_locked( + exec_ctx, chand, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_REF(op->disconnect_with_error), "disconnect"); + grpc_resolver_shutdown_locked(exec_ctx, chand->resolver); + GRPC_RESOLVER_UNREF(exec_ctx, chand->resolver, "channel"); + chand->resolver = NULL; + if (!chand->started_resolving) { + grpc_closure_list_fail_all(&chand->waiting_for_resolver_result_closures, + GRPC_ERROR_REF(op->disconnect_with_error)); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, + &chand->waiting_for_resolver_result_closures); + } + if (chand->lb_policy != NULL) { + grpc_pollset_set_del_pollset_set(exec_ctx, + chand->lb_policy->interested_parties, + chand->interested_parties); + GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); + chand->lb_policy = NULL; + } + } + GRPC_ERROR_UNREF(op->disconnect_with_error); + } + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "start_transport_op"); + + GRPC_CLOSURE_SCHED(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); +} + +static void cc_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_transport_op *op) { + channel_data *chand = (channel_data *)elem->channel_data; + + GPR_ASSERT(op->set_accept_stream == false); + if (op->bind_pollset != NULL) { + grpc_pollset_set_add_pollset(exec_ctx, chand->interested_parties, + op->bind_pollset); + } + + op->handler_private.extra_arg = elem; + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "start_transport_op"); + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&op->handler_private.closure, start_transport_op_locked, + op, grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); +} + +static void cc_get_channel_info(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + const grpc_channel_info *info) { + channel_data *chand = (channel_data *)elem->channel_data; + gpr_mu_lock(&chand->info_mu); + if (info->lb_policy_name != NULL) { + *info->lb_policy_name = chand->info_lb_policy_name == NULL + ? NULL + : gpr_strdup(chand->info_lb_policy_name); + } + if (info->service_config_json != NULL) { + *info->service_config_json = + chand->info_service_config_json == NULL + ? NULL + : gpr_strdup(chand->info_service_config_json); + } + gpr_mu_unlock(&chand->info_mu); +} + +/* Constructor for channel_data */ +static grpc_error *cc_init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + channel_data *chand = (channel_data *)elem->channel_data; + GPR_ASSERT(args->is_last); + GPR_ASSERT(elem->filter == &grpc_client_channel_filter); + // Initialize data members. + chand->combiner = grpc_combiner_create(); + gpr_mu_init(&chand->info_mu); + gpr_mu_init(&chand->external_connectivity_watcher_list_mu); + + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + chand->external_connectivity_watcher_list_head = NULL; + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + + chand->owning_stack = args->channel_stack; + GRPC_CLOSURE_INIT(&chand->on_resolver_result_changed, + on_resolver_result_changed_locked, chand, + grpc_combiner_scheduler(chand->combiner)); + chand->interested_parties = grpc_pollset_set_create(); + grpc_connectivity_state_init(&chand->state_tracker, GRPC_CHANNEL_IDLE, + "client_channel"); + // Record client channel factory. + const grpc_arg *arg = grpc_channel_args_find(args->channel_args, + GRPC_ARG_CLIENT_CHANNEL_FACTORY); + if (arg == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Missing client channel factory in args for client channel filter"); + } + if (arg->type != GRPC_ARG_POINTER) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "client channel factory arg must be a pointer"); + } + grpc_client_channel_factory_ref( + (grpc_client_channel_factory *)arg->value.pointer.p); + chand->client_channel_factory = + (grpc_client_channel_factory *)arg->value.pointer.p; + // Get server name to resolve, using proxy mapper if needed. + arg = grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVER_URI); + if (arg == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Missing server uri in args for client channel filter"); + } + if (arg->type != GRPC_ARG_STRING) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "server uri arg must be a string"); + } + char *proxy_name = NULL; + grpc_channel_args *new_args = NULL; + grpc_proxy_mappers_map_name(exec_ctx, arg->value.string, args->channel_args, + &proxy_name, &new_args); + // Instantiate resolver. + chand->resolver = grpc_resolver_create( + exec_ctx, proxy_name != NULL ? proxy_name : arg->value.string, + new_args != NULL ? new_args : args->channel_args, + chand->interested_parties, chand->combiner); + if (proxy_name != NULL) gpr_free(proxy_name); + if (new_args != NULL) grpc_channel_args_destroy(exec_ctx, new_args); + if (chand->resolver == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("resolver creation failed"); + } + chand->deadline_checking_enabled = + grpc_deadline_checking_enabled(args->channel_args); + return GRPC_ERROR_NONE; +} + +static void shutdown_resolver_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_resolver *resolver = (grpc_resolver *)arg; + grpc_resolver_shutdown_locked(exec_ctx, resolver); + GRPC_RESOLVER_UNREF(exec_ctx, resolver, "channel"); +} + +/* Destructor for channel_data */ +static void cc_destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + if (chand->resolver != NULL) { + GRPC_CLOSURE_SCHED( + exec_ctx, GRPC_CLOSURE_CREATE(shutdown_resolver_locked, chand->resolver, + grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); + } + if (chand->client_channel_factory != NULL) { + grpc_client_channel_factory_unref(exec_ctx, chand->client_channel_factory); + } + if (chand->lb_policy != NULL) { + grpc_pollset_set_del_pollset_set(exec_ctx, + chand->lb_policy->interested_parties, + chand->interested_parties); + GRPC_LB_POLICY_UNREF(exec_ctx, chand->lb_policy, "channel"); + } + gpr_free(chand->info_lb_policy_name); + gpr_free(chand->info_service_config_json); + if (chand->retry_throttle_data != NULL) { + grpc_server_retry_throttle_data_unref(chand->retry_throttle_data); + } + if (chand->method_params_table != NULL) { + grpc_slice_hash_table_unref(exec_ctx, chand->method_params_table); + } + grpc_connectivity_state_destroy(exec_ctx, &chand->state_tracker); + grpc_pollset_set_destroy(exec_ctx, chand->interested_parties); + GRPC_COMBINER_UNREF(exec_ctx, chand->combiner, "client_channel"); + gpr_mu_destroy(&chand->info_mu); + gpr_mu_destroy(&chand->external_connectivity_watcher_list_mu); +} + +/************************************************************************* + * PER-CALL FUNCTIONS + */ + +// Max number of batches that can be pending on a call at any given +// time. This includes: +// recv_initial_metadata +// send_initial_metadata +// recv_message +// send_message +// recv_trailing_metadata +// send_trailing_metadata +// We also add room for a single cancel_stream batch. +#define MAX_WAITING_BATCHES 7 + +/** Call data. Holds a pointer to grpc_subchannel_call and the + associated machinery to create such a pointer. + Handles queueing of stream ops until a call object is ready, waiting + for initial metadata before trying to create a call object, + and handling cancellation gracefully. */ +typedef struct client_channel_call_data { + // State for handling deadlines. + // The code in deadline_filter.c requires this to be the first field. + // TODO(roth): This is slightly sub-optimal in that grpc_deadline_state + // and this struct both independently store pointers to the call stack + // and call combiner. If/when we have time, find a way to avoid this + // without breaking the grpc_deadline_state abstraction. + grpc_deadline_state deadline_state; + + grpc_slice path; // Request path. + gpr_timespec call_start_time; + gpr_timespec deadline; + gpr_arena *arena; + grpc_call_stack *owning_call; + grpc_call_combiner *call_combiner; + + grpc_server_retry_throttle_data *retry_throttle_data; + method_parameters *method_params; + + grpc_subchannel_call *subchannel_call; + grpc_error *error; + + grpc_lb_policy *lb_policy; // Holds ref while LB pick is pending. + grpc_closure lb_pick_closure; + grpc_closure lb_pick_cancel_closure; + + grpc_connected_subchannel *connected_subchannel; + grpc_call_context_element subchannel_call_context[GRPC_CONTEXT_COUNT]; + grpc_polling_entity *pollent; + + grpc_transport_stream_op_batch *waiting_for_pick_batches[MAX_WAITING_BATCHES]; + size_t waiting_for_pick_batches_count; + grpc_closure handle_pending_batch_in_call_combiner[MAX_WAITING_BATCHES]; + + grpc_transport_stream_op_batch *initial_metadata_batch; + + grpc_linked_mdelem lb_token_mdelem; + + grpc_closure on_complete; + grpc_closure *original_on_complete; +} call_data; + +grpc_subchannel_call *grpc_client_channel_get_subchannel_call( + grpc_call_element *elem) { + call_data *calld = (call_data *)elem->call_data; + return calld->subchannel_call; +} + +// This is called via the call combiner, so access to calld is synchronized. +static void waiting_for_pick_batches_add( + call_data *calld, grpc_transport_stream_op_batch *batch) { + if (batch->send_initial_metadata) { + GPR_ASSERT(calld->initial_metadata_batch == NULL); + calld->initial_metadata_batch = batch; + } else { + GPR_ASSERT(calld->waiting_for_pick_batches_count < MAX_WAITING_BATCHES); + calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count++] = + batch; + } +} + +// This is called via the call combiner, so access to calld is synchronized. +static void fail_pending_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error) { + call_data *calld = (call_data *)arg; + if (calld->waiting_for_pick_batches_count > 0) { + --calld->waiting_for_pick_batches_count; + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, + calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count], + GRPC_ERROR_REF(error), calld->call_combiner); + } +} + +// This is called via the call combiner, so access to calld is synchronized. +static void waiting_for_pick_batches_fail(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_error *error) { + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: failing %" PRIdPTR " pending batches: %s", + elem->channel_data, calld, calld->waiting_for_pick_batches_count, + grpc_error_string(error)); + } + for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) { + GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i], + fail_pending_batch_in_call_combiner, calld, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(exec_ctx, calld->call_combiner, + &calld->handle_pending_batch_in_call_combiner[i], + GRPC_ERROR_REF(error), + "waiting_for_pick_batches_fail"); + } + if (calld->initial_metadata_batch != NULL) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, calld->initial_metadata_batch, GRPC_ERROR_REF(error), + calld->call_combiner); + } else { + GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, + "waiting_for_pick_batches_fail"); + } + GRPC_ERROR_UNREF(error); +} + +// This is called via the call combiner, so access to calld is synchronized. +static void run_pending_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *ignored) { + call_data *calld = (call_data *)arg; + if (calld->waiting_for_pick_batches_count > 0) { + --calld->waiting_for_pick_batches_count; + grpc_subchannel_call_process_op( + exec_ctx, calld->subchannel_call, + calld->waiting_for_pick_batches[calld->waiting_for_pick_batches_count]); + } +} + +// This is called via the call combiner, so access to calld is synchronized. +static void waiting_for_pick_batches_resume(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: sending %" PRIdPTR + " pending batches to subchannel_call=%p", + chand, calld, calld->waiting_for_pick_batches_count, + calld->subchannel_call); + } + for (size_t i = 0; i < calld->waiting_for_pick_batches_count; ++i) { + GRPC_CLOSURE_INIT(&calld->handle_pending_batch_in_call_combiner[i], + run_pending_batch_in_call_combiner, calld, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(exec_ctx, calld->call_combiner, + &calld->handle_pending_batch_in_call_combiner[i], + GRPC_ERROR_NONE, + "waiting_for_pick_batches_resume"); + } + GPR_ASSERT(calld->initial_metadata_batch != NULL); + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, + calld->initial_metadata_batch); +} + +// Applies service config to the call. Must be invoked once we know +// that the resolver has returned results to the channel. +static void apply_service_config_to_call_locked(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: applying service config to call", + chand, calld); + } + if (chand->retry_throttle_data != NULL) { + calld->retry_throttle_data = + grpc_server_retry_throttle_data_ref(chand->retry_throttle_data); + } + if (chand->method_params_table != NULL) { + calld->method_params = (method_parameters *)grpc_method_config_table_get( + exec_ctx, chand->method_params_table, calld->path); + if (calld->method_params != NULL) { + method_parameters_ref(calld->method_params); + // If the deadline from the service config is shorter than the one + // from the client API, reset the deadline timer. + if (chand->deadline_checking_enabled && + gpr_time_cmp(calld->method_params->timeout, + gpr_time_0(GPR_TIMESPAN)) != 0) { + const gpr_timespec per_method_deadline = + gpr_time_add(calld->call_start_time, calld->method_params->timeout); + if (gpr_time_cmp(per_method_deadline, calld->deadline) < 0) { + calld->deadline = per_method_deadline; + grpc_deadline_state_reset(exec_ctx, elem, calld->deadline); + } + } + } + } +} + +static void create_subchannel_call_locked(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_error *error) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + const grpc_connected_subchannel_call_args call_args = { + .pollent = calld->pollent, + .path = calld->path, + .start_time = calld->call_start_time, + .deadline = calld->deadline, + .arena = calld->arena, + .context = calld->subchannel_call_context, + .call_combiner = calld->call_combiner}; + grpc_error *new_error = grpc_connected_subchannel_create_call( + exec_ctx, calld->connected_subchannel, &call_args, + &calld->subchannel_call); + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: create subchannel_call=%p: error=%s", + chand, calld, calld->subchannel_call, grpc_error_string(new_error)); + } + if (new_error != GRPC_ERROR_NONE) { + new_error = grpc_error_add_child(new_error, error); + waiting_for_pick_batches_fail(exec_ctx, elem, new_error); + } else { + waiting_for_pick_batches_resume(exec_ctx, elem); + } + GRPC_ERROR_UNREF(error); +} + +// Invoked when a pick is completed, on both success or failure. +static void pick_done_locked(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_error *error) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (calld->connected_subchannel == NULL) { + // Failed to create subchannel. + GRPC_ERROR_UNREF(calld->error); + calld->error = error == GRPC_ERROR_NONE + ? GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Call dropped by load balancing policy") + : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to create subchannel", &error, 1); + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: failed to create subchannel: error=%s", chand, + calld, grpc_error_string(calld->error)); + } + waiting_for_pick_batches_fail(exec_ctx, elem, GRPC_ERROR_REF(calld->error)); + } else { + /* Create call on subchannel. */ + create_subchannel_call_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +// A wrapper around pick_done_locked() that is used in cases where +// either (a) the pick was deferred pending a resolver result or (b) the +// pick was done asynchronously. Removes the call's polling entity from +// chand->interested_parties before invoking pick_done_locked(). +static void async_pick_done_locked(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, grpc_error *error) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + grpc_polling_entity_del_from_pollset_set(exec_ctx, calld->pollent, + chand->interested_parties); + pick_done_locked(exec_ctx, elem, error); +} + +// Note: This runs under the client_channel combiner, but will NOT be +// holding the call combiner. +static void pick_callback_cancel_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (calld->lb_policy != NULL) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: cancelling pick from LB policy %p", + chand, calld, calld->lb_policy); + } + grpc_lb_policy_cancel_pick_locked(exec_ctx, calld->lb_policy, + &calld->connected_subchannel, + GRPC_ERROR_REF(error)); + } + GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "pick_callback_cancel"); +} + +// Callback invoked by grpc_lb_policy_pick_locked() for async picks. +// Unrefs the LB policy and invokes async_pick_done_locked(). +static void pick_callback_done_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed asynchronously", + chand, calld); + } + GPR_ASSERT(calld->lb_policy != NULL); + GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); + calld->lb_policy = NULL; + async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); +} + +// Takes a ref to chand->lb_policy and calls grpc_lb_policy_pick_locked(). +// If the pick was completed synchronously, unrefs the LB policy and +// returns true. +static bool pick_callback_start_locked(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: starting pick on lb_policy=%p", + chand, calld, chand->lb_policy); + } + apply_service_config_to_call_locked(exec_ctx, elem); + // If the application explicitly set wait_for_ready, use that. + // Otherwise, if the service config specified a value for this + // method, use that. + uint32_t initial_metadata_flags = + calld->initial_metadata_batch->payload->send_initial_metadata + .send_initial_metadata_flags; + const bool wait_for_ready_set_from_api = + initial_metadata_flags & + GRPC_INITIAL_METADATA_WAIT_FOR_READY_EXPLICITLY_SET; + const bool wait_for_ready_set_from_service_config = + calld->method_params != NULL && + calld->method_params->wait_for_ready != WAIT_FOR_READY_UNSET; + if (!wait_for_ready_set_from_api && wait_for_ready_set_from_service_config) { + if (calld->method_params->wait_for_ready == WAIT_FOR_READY_TRUE) { + initial_metadata_flags |= GRPC_INITIAL_METADATA_WAIT_FOR_READY; + } else { + initial_metadata_flags &= ~GRPC_INITIAL_METADATA_WAIT_FOR_READY; + } + } + const grpc_lb_policy_pick_args inputs = { + calld->initial_metadata_batch->payload->send_initial_metadata + .send_initial_metadata, + initial_metadata_flags, &calld->lb_token_mdelem}; + // Keep a ref to the LB policy in calld while the pick is pending. + GRPC_LB_POLICY_REF(chand->lb_policy, "pick_subchannel"); + calld->lb_policy = chand->lb_policy; + GRPC_CLOSURE_INIT(&calld->lb_pick_closure, pick_callback_done_locked, elem, + grpc_combiner_scheduler(chand->combiner)); + const bool pick_done = grpc_lb_policy_pick_locked( + exec_ctx, chand->lb_policy, &inputs, &calld->connected_subchannel, + calld->subchannel_call_context, NULL, &calld->lb_pick_closure); + if (pick_done) { + /* synchronous grpc_lb_policy_pick call. Unref the LB policy. */ + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: pick completed synchronously", + chand, calld); + } + GRPC_LB_POLICY_UNREF(exec_ctx, calld->lb_policy, "pick_subchannel"); + calld->lb_policy = NULL; + } else { + GRPC_CALL_STACK_REF(calld->owning_call, "pick_callback_cancel"); + grpc_call_combiner_set_notify_on_cancel( + exec_ctx, calld->call_combiner, + GRPC_CLOSURE_INIT(&calld->lb_pick_cancel_closure, + pick_callback_cancel_locked, elem, + grpc_combiner_scheduler(chand->combiner))); + } + return pick_done; +} + +typedef struct { + grpc_call_element *elem; + bool finished; + grpc_closure closure; + grpc_closure cancel_closure; +} pick_after_resolver_result_args; + +// Note: This runs under the client_channel combiner, but will NOT be +// holding the call combiner. +static void pick_after_resolver_result_cancel_locked(grpc_exec_ctx *exec_ctx, + void *arg, + grpc_error *error) { + pick_after_resolver_result_args *args = + (pick_after_resolver_result_args *)arg; + if (args->finished) { + gpr_free(args); + return; + } + // If we don't yet have a resolver result, then a closure for + // pick_after_resolver_result_done_locked() will have been added to + // chand->waiting_for_resolver_result_closures, and it may not be invoked + // until after this call has been destroyed. We mark the operation as + // finished, so that when pick_after_resolver_result_done_locked() + // is called, it will be a no-op. We also immediately invoke + // async_pick_done_locked() to propagate the error back to the caller. + args->finished = true; + grpc_call_element *elem = args->elem; + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: cancelling pick waiting for resolver result", + chand, calld); + } + // Note: Although we are not in the call combiner here, we are + // basically stealing the call combiner from the pending pick, so + // it's safe to call async_pick_done_locked() here -- we are + // essentially calling it here instead of calling it in + // pick_after_resolver_result_done_locked(). + async_pick_done_locked(exec_ctx, elem, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick cancelled", &error, 1)); +} + +static void pick_after_resolver_result_done_locked(grpc_exec_ctx *exec_ctx, + void *arg, + grpc_error *error) { + pick_after_resolver_result_args *args = + (pick_after_resolver_result_args *)arg; + if (args->finished) { + /* cancelled, do nothing */ + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "call cancelled before resolver result"); + } + gpr_free(args); + return; + } + args->finished = true; + grpc_call_element *elem = args->elem; + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (error != GRPC_ERROR_NONE) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver failed to return data", + chand, calld); + } + async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_REF(error)); + } else { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: resolver returned, doing pick", + chand, calld); + } + if (pick_callback_start_locked(exec_ctx, elem)) { + // Even if the LB policy returns a result synchronously, we have + // already added our polling entity to chand->interested_parties + // in order to wait for the resolver result, so we need to + // remove it here. Therefore, we call async_pick_done_locked() + // instead of pick_done_locked(). + async_pick_done_locked(exec_ctx, elem, GRPC_ERROR_NONE); + } + } +} + +static void pick_after_resolver_result_start_locked(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: deferring pick pending resolver result", chand, + calld); + } + pick_after_resolver_result_args *args = + (pick_after_resolver_result_args *)gpr_zalloc(sizeof(*args)); + args->elem = elem; + GRPC_CLOSURE_INIT(&args->closure, pick_after_resolver_result_done_locked, + args, grpc_combiner_scheduler(chand->combiner)); + grpc_closure_list_append(&chand->waiting_for_resolver_result_closures, + &args->closure, GRPC_ERROR_NONE); + grpc_call_combiner_set_notify_on_cancel( + exec_ctx, calld->call_combiner, + GRPC_CLOSURE_INIT(&args->cancel_closure, + pick_after_resolver_result_cancel_locked, args, + grpc_combiner_scheduler(chand->combiner))); +} + +static void start_pick_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *ignored) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + GPR_ASSERT(calld->connected_subchannel == NULL); + if (chand->lb_policy != NULL) { + // We already have an LB policy, so ask it for a pick. + if (pick_callback_start_locked(exec_ctx, elem)) { + // Pick completed synchronously. + pick_done_locked(exec_ctx, elem, GRPC_ERROR_NONE); + return; + } + } else { + // We do not yet have an LB policy, so wait for a resolver result. + if (chand->resolver == NULL) { + pick_done_locked(exec_ctx, elem, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Disconnected")); + return; + } + if (!chand->started_resolving) { + start_resolving_locked(exec_ctx, chand); + } + pick_after_resolver_result_start_locked(exec_ctx, elem); + } + // We need to wait for either a resolver result or for an async result + // from the LB policy. Add the polling entity from call_data to the + // channel_data's interested_parties, so that the I/O of the LB policy + // and resolver can be done under it. The polling entity will be + // removed in async_pick_done_locked(). + grpc_polling_entity_add_to_pollset_set(exec_ctx, calld->pollent, + chand->interested_parties); +} + +static void on_complete(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + if (calld->retry_throttle_data != NULL) { + if (error == GRPC_ERROR_NONE) { + grpc_server_retry_throttle_data_record_success( + calld->retry_throttle_data); + } else { + // TODO(roth): In a subsequent PR, check the return value here and + // decide whether or not to retry. Note that we should only + // record failures whose statuses match the configured retryable + // or non-fatal status codes. + grpc_server_retry_throttle_data_record_failure( + calld->retry_throttle_data); + } + } + GRPC_CLOSURE_RUN(exec_ctx, calld->original_on_complete, + GRPC_ERROR_REF(error)); +} + +static void cc_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (chand->deadline_checking_enabled) { + grpc_deadline_state_client_start_transport_stream_op_batch(exec_ctx, elem, + batch); + } + GPR_TIMER_BEGIN("cc_start_transport_stream_op_batch", 0); + // If we've previously been cancelled, immediately fail any new batches. + if (calld->error != GRPC_ERROR_NONE) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: failing batch with error: %s", + chand, calld, grpc_error_string(calld->error)); + } + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, batch, GRPC_ERROR_REF(calld->error), calld->call_combiner); + goto done; + } + if (batch->cancel_stream) { + // Stash a copy of cancel_error in our call data, so that we can use + // it for subsequent operations. This ensures that if the call is + // cancelled before any batches are passed down (e.g., if the deadline + // is in the past when the call starts), we can return the right + // error to the caller when the first batch does get passed down. + GRPC_ERROR_UNREF(calld->error); + calld->error = GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: recording cancel_error=%s", chand, + calld, grpc_error_string(calld->error)); + } + // If we have a subchannel call, send the cancellation batch down. + // Otherwise, fail all pending batches. + if (calld->subchannel_call != NULL) { + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, batch); + } else { + waiting_for_pick_batches_add(calld, batch); + waiting_for_pick_batches_fail(exec_ctx, elem, + GRPC_ERROR_REF(calld->error)); + } + goto done; + } + // Intercept on_complete for recv_trailing_metadata so that we can + // check retry throttle status. + if (batch->recv_trailing_metadata) { + GPR_ASSERT(batch->on_complete != NULL); + calld->original_on_complete = batch->on_complete; + GRPC_CLOSURE_INIT(&calld->on_complete, on_complete, elem, + grpc_schedule_on_exec_ctx); + batch->on_complete = &calld->on_complete; + } + // Check if we've already gotten a subchannel call. + // Note that once we have completed the pick, we do not need to enter + // the channel combiner, which is more efficient (especially for + // streaming calls). + if (calld->subchannel_call != NULL) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: sending batch to subchannel_call=%p", chand, + calld, calld->subchannel_call); + } + grpc_subchannel_call_process_op(exec_ctx, calld->subchannel_call, batch); + goto done; + } + // We do not yet have a subchannel call. + // Add the batch to the waiting-for-pick list. + waiting_for_pick_batches_add(calld, batch); + // For batches containing a send_initial_metadata op, enter the channel + // combiner to start a pick. + if (batch->send_initial_metadata) { + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, "chand=%p calld=%p: entering client_channel combiner", + chand, calld); + } + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&batch->handler_private.closure, start_pick_locked, + elem, grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); + } else { + // For all other batches, release the call combiner. + if (GRPC_TRACER_ON(grpc_client_channel_trace)) { + gpr_log(GPR_DEBUG, + "chand=%p calld=%p: saved batch, yeilding call combiner", chand, + calld); + } + GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, + "batch does not include send_initial_metadata"); + } +done: + GPR_TIMER_END("cc_start_transport_stream_op_batch", 0); +} + +/* Constructor for call_data */ +static grpc_error *cc_init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + // Initialize data members. + calld->path = grpc_slice_ref_internal(args->path); + calld->call_start_time = args->start_time; + calld->deadline = gpr_convert_clock_type(args->deadline, GPR_CLOCK_MONOTONIC); + calld->arena = args->arena; + calld->owning_call = args->call_stack; + calld->call_combiner = args->call_combiner; + if (chand->deadline_checking_enabled) { + grpc_deadline_state_init(exec_ctx, elem, args->call_stack, + args->call_combiner, calld->deadline); + } + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void cc_destroy_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *then_schedule_closure) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (chand->deadline_checking_enabled) { + grpc_deadline_state_destroy(exec_ctx, elem); + } + grpc_slice_unref_internal(exec_ctx, calld->path); + if (calld->method_params != NULL) { + method_parameters_unref(calld->method_params); + } + GRPC_ERROR_UNREF(calld->error); + if (calld->subchannel_call != NULL) { + grpc_subchannel_call_set_cleanup_closure(calld->subchannel_call, + then_schedule_closure); + then_schedule_closure = NULL; + GRPC_SUBCHANNEL_CALL_UNREF(exec_ctx, calld->subchannel_call, + "client_channel_destroy_call"); + } + GPR_ASSERT(calld->lb_policy == NULL); + GPR_ASSERT(calld->waiting_for_pick_batches_count == 0); + if (calld->connected_subchannel != NULL) { + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, calld->connected_subchannel, + "picked"); + } + for (size_t i = 0; i < GRPC_CONTEXT_COUNT; ++i) { + if (calld->subchannel_call_context[i].value != NULL) { + calld->subchannel_call_context[i].destroy( + calld->subchannel_call_context[i].value); + } + } + GRPC_CLOSURE_SCHED(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE); +} + +static void cc_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_polling_entity *pollent) { + call_data *calld = (call_data *)elem->call_data; + calld->pollent = pollent; +} + +/************************************************************************* + * EXPORTED SYMBOLS + */ + +const grpc_channel_filter grpc_client_channel_filter = { + cc_start_transport_stream_op_batch, + cc_start_transport_op, + sizeof(call_data), + cc_init_call_elem, + cc_set_pollset_or_pollset_set, + cc_destroy_call_elem, + sizeof(channel_data), + cc_init_channel_elem, + cc_destroy_channel_elem, + cc_get_channel_info, + "client-channel", +}; + +static void try_to_connect_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error_ignored) { + channel_data *chand = (channel_data *)arg; + if (chand->lb_policy != NULL) { + grpc_lb_policy_exit_idle_locked(exec_ctx, chand->lb_policy); + } else { + chand->exit_idle_when_lb_policy_arrives = true; + if (!chand->started_resolving && chand->resolver != NULL) { + start_resolving_locked(exec_ctx, chand); + } + } + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->owning_stack, "try_to_connect"); +} + +grpc_connectivity_state grpc_client_channel_check_connectivity_state( + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, int try_to_connect) { + channel_data *chand = (channel_data *)elem->channel_data; + grpc_connectivity_state out = + grpc_connectivity_state_check(&chand->state_tracker); + if (out == GRPC_CHANNEL_IDLE && try_to_connect) { + GRPC_CHANNEL_STACK_REF(chand->owning_stack, "try_to_connect"); + GRPC_CLOSURE_SCHED( + exec_ctx, GRPC_CLOSURE_CREATE(try_to_connect_locked, chand, + grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); + } + return out; +} + +typedef struct external_connectivity_watcher { + channel_data *chand; + grpc_polling_entity pollent; + grpc_closure *on_complete; + grpc_closure *watcher_timer_init; + grpc_connectivity_state *state; + grpc_closure my_closure; + struct external_connectivity_watcher *next; +} external_connectivity_watcher; + +static external_connectivity_watcher *lookup_external_connectivity_watcher( + channel_data *chand, grpc_closure *on_complete) { + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + external_connectivity_watcher *w = + chand->external_connectivity_watcher_list_head; + while (w != NULL && w->on_complete != on_complete) { + w = w->next; + } + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + return w; +} + +static void external_connectivity_watcher_list_append( + channel_data *chand, external_connectivity_watcher *w) { + GPR_ASSERT(!lookup_external_connectivity_watcher(chand, w->on_complete)); + + gpr_mu_lock(&w->chand->external_connectivity_watcher_list_mu); + GPR_ASSERT(!w->next); + w->next = chand->external_connectivity_watcher_list_head; + chand->external_connectivity_watcher_list_head = w; + gpr_mu_unlock(&w->chand->external_connectivity_watcher_list_mu); +} + +static void external_connectivity_watcher_list_remove( + channel_data *chand, external_connectivity_watcher *too_remove) { + GPR_ASSERT( + lookup_external_connectivity_watcher(chand, too_remove->on_complete)); + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + if (too_remove == chand->external_connectivity_watcher_list_head) { + chand->external_connectivity_watcher_list_head = too_remove->next; + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + return; + } + external_connectivity_watcher *w = + chand->external_connectivity_watcher_list_head; + while (w != NULL) { + if (w->next == too_remove) { + w->next = w->next->next; + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + return; + } + w = w->next; + } + GPR_UNREACHABLE_CODE(return ); +} + +int grpc_client_channel_num_external_connectivity_watchers( + grpc_channel_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + int count = 0; + + gpr_mu_lock(&chand->external_connectivity_watcher_list_mu); + external_connectivity_watcher *w = + chand->external_connectivity_watcher_list_head; + while (w != NULL) { + count++; + w = w->next; + } + gpr_mu_unlock(&chand->external_connectivity_watcher_list_mu); + + return count; +} + +static void on_external_watch_complete(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + external_connectivity_watcher *w = (external_connectivity_watcher *)arg; + grpc_closure *follow_up = w->on_complete; + grpc_polling_entity_del_from_pollset_set(exec_ctx, &w->pollent, + w->chand->interested_parties); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, + "external_connectivity_watcher"); + external_connectivity_watcher_list_remove(w->chand, w); + gpr_free(w); + GRPC_CLOSURE_RUN(exec_ctx, follow_up, GRPC_ERROR_REF(error)); +} + +static void watch_connectivity_state_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error_ignored) { + external_connectivity_watcher *w = (external_connectivity_watcher *)arg; + external_connectivity_watcher *found = NULL; + if (w->state != NULL) { + external_connectivity_watcher_list_append(w->chand, w); + GRPC_CLOSURE_RUN(exec_ctx, w->watcher_timer_init, GRPC_ERROR_NONE); + GRPC_CLOSURE_INIT(&w->my_closure, on_external_watch_complete, w, + grpc_schedule_on_exec_ctx); + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &w->chand->state_tracker, w->state, &w->my_closure); + } else { + GPR_ASSERT(w->watcher_timer_init == NULL); + found = lookup_external_connectivity_watcher(w->chand, w->on_complete); + if (found) { + GPR_ASSERT(found->on_complete == w->on_complete); + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &found->chand->state_tracker, NULL, &found->my_closure); + } + grpc_polling_entity_del_from_pollset_set(exec_ctx, &w->pollent, + w->chand->interested_parties); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, w->chand->owning_stack, + "external_connectivity_watcher"); + gpr_free(w); + } +} + +void grpc_client_channel_watch_connectivity_state( + grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_polling_entity pollent, grpc_connectivity_state *state, + grpc_closure *closure, grpc_closure *watcher_timer_init) { + channel_data *chand = (channel_data *)elem->channel_data; + external_connectivity_watcher *w = + (external_connectivity_watcher *)gpr_zalloc(sizeof(*w)); + w->chand = chand; + w->pollent = pollent; + w->on_complete = closure; + w->state = state; + w->watcher_timer_init = watcher_timer_init; + grpc_polling_entity_add_to_pollset_set(exec_ctx, &w->pollent, + chand->interested_parties); + GRPC_CHANNEL_STACK_REF(w->chand->owning_stack, + "external_connectivity_watcher"); + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&w->my_closure, watch_connectivity_state_locked, w, + grpc_combiner_scheduler(chand->combiner)), + GRPC_ERROR_NONE); +} diff --git a/src/core/ext/filters/client_channel/client_channel_factory.c b/src/core/ext/filters/client_channel/client_channel_factory.c deleted file mode 100644 index 57eac8f875..0000000000 --- a/src/core/ext/filters/client_channel/client_channel_factory.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/client_channel_factory.h" -#include "src/core/lib/channel/channel_args.h" - -void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory) { - factory->vtable->ref(factory); -} - -void grpc_client_channel_factory_unref(grpc_exec_ctx* exec_ctx, - grpc_client_channel_factory* factory) { - factory->vtable->unref(exec_ctx, factory); -} - -grpc_subchannel* grpc_client_channel_factory_create_subchannel( - grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, - const grpc_subchannel_args* args) { - return factory->vtable->create_subchannel(exec_ctx, factory, args); -} - -grpc_channel* grpc_client_channel_factory_create_channel( - grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, - const char* target, grpc_client_channel_type type, - const grpc_channel_args* args) { - return factory->vtable->create_client_channel(exec_ctx, factory, target, type, - args); -} - -static void* factory_arg_copy(void* factory) { - grpc_client_channel_factory_ref((grpc_client_channel_factory*)factory); - return factory; -} - -static void factory_arg_destroy(grpc_exec_ctx* exec_ctx, void* factory) { - grpc_client_channel_factory_unref(exec_ctx, - (grpc_client_channel_factory*)factory); -} - -static int factory_arg_cmp(void* factory1, void* factory2) { - if (factory1 < factory2) return -1; - if (factory1 > factory2) return 1; - return 0; -} - -static const grpc_arg_pointer_vtable factory_arg_vtable = { - factory_arg_copy, factory_arg_destroy, factory_arg_cmp}; - -grpc_arg grpc_client_channel_factory_create_channel_arg( - grpc_client_channel_factory* factory) { - return grpc_channel_arg_pointer_create((char*)GRPC_ARG_CLIENT_CHANNEL_FACTORY, - factory, &factory_arg_vtable); -} diff --git a/src/core/ext/filters/client_channel/client_channel_factory.cc b/src/core/ext/filters/client_channel/client_channel_factory.cc new file mode 100644 index 0000000000..57eac8f875 --- /dev/null +++ b/src/core/ext/filters/client_channel/client_channel_factory.cc @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/lib/channel/channel_args.h" + +void grpc_client_channel_factory_ref(grpc_client_channel_factory* factory) { + factory->vtable->ref(factory); +} + +void grpc_client_channel_factory_unref(grpc_exec_ctx* exec_ctx, + grpc_client_channel_factory* factory) { + factory->vtable->unref(exec_ctx, factory); +} + +grpc_subchannel* grpc_client_channel_factory_create_subchannel( + grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, + const grpc_subchannel_args* args) { + return factory->vtable->create_subchannel(exec_ctx, factory, args); +} + +grpc_channel* grpc_client_channel_factory_create_channel( + grpc_exec_ctx* exec_ctx, grpc_client_channel_factory* factory, + const char* target, grpc_client_channel_type type, + const grpc_channel_args* args) { + return factory->vtable->create_client_channel(exec_ctx, factory, target, type, + args); +} + +static void* factory_arg_copy(void* factory) { + grpc_client_channel_factory_ref((grpc_client_channel_factory*)factory); + return factory; +} + +static void factory_arg_destroy(grpc_exec_ctx* exec_ctx, void* factory) { + grpc_client_channel_factory_unref(exec_ctx, + (grpc_client_channel_factory*)factory); +} + +static int factory_arg_cmp(void* factory1, void* factory2) { + if (factory1 < factory2) return -1; + if (factory1 > factory2) return 1; + return 0; +} + +static const grpc_arg_pointer_vtable factory_arg_vtable = { + factory_arg_copy, factory_arg_destroy, factory_arg_cmp}; + +grpc_arg grpc_client_channel_factory_create_channel_arg( + grpc_client_channel_factory* factory) { + return grpc_channel_arg_pointer_create((char*)GRPC_ARG_CLIENT_CHANNEL_FACTORY, + factory, &factory_arg_vtable); +} diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.c b/src/core/ext/filters/client_channel/client_channel_plugin.c deleted file mode 100644 index 1f71c5a7f9..0000000000 --- a/src/core/ext/filters/client_channel/client_channel_plugin.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" -#include "src/core/ext/filters/client_channel/http_proxy.h" -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/ext/filters/client_channel/retry_throttle.h" -#include "src/core/ext/filters/client_channel/subchannel_index.h" -#include "src/core/lib/surface/channel_init.h" - -static bool append_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, void *arg) { - return grpc_channel_stack_builder_append_filter( - builder, (const grpc_channel_filter *)arg, NULL, NULL); -} - -static bool set_default_host_if_unset(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, - void *unused) { - const grpc_channel_args *args = - grpc_channel_stack_builder_get_channel_arguments(builder); - for (size_t i = 0; i < args->num_args; i++) { - if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY) || - 0 == strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { - return true; - } - } - char *default_authority = grpc_get_default_authority( - exec_ctx, grpc_channel_stack_builder_get_target(builder)); - if (default_authority != NULL) { - grpc_arg arg = grpc_channel_arg_string_create( - (char *)GRPC_ARG_DEFAULT_AUTHORITY, default_authority); - grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); - grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder, - new_args); - gpr_free(default_authority); - grpc_channel_args_destroy(exec_ctx, new_args); - } - return true; -} - -void grpc_client_channel_init(void) { - grpc_lb_policy_registry_init(); - grpc_resolver_registry_init(); - grpc_retry_throttle_map_init(); - grpc_proxy_mapper_registry_init(); - grpc_register_http_proxy_mapper(); - grpc_subchannel_index_init(); - grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MIN, - set_default_host_if_unset, NULL); - grpc_channel_init_register_stage( - GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter, - (void *)&grpc_client_channel_filter); - grpc_http_connect_register_handshaker_factory(); - grpc_register_tracer(&grpc_client_channel_trace); -#ifndef NDEBUG - grpc_register_tracer(&grpc_trace_resolver_refcount); -#endif -} - -void grpc_client_channel_shutdown(void) { - grpc_subchannel_index_shutdown(); - grpc_channel_init_shutdown(); - grpc_proxy_mapper_registry_shutdown(); - grpc_retry_throttle_map_shutdown(); - grpc_resolver_registry_shutdown(); - grpc_lb_policy_registry_shutdown(); -} diff --git a/src/core/ext/filters/client_channel/client_channel_plugin.cc b/src/core/ext/filters/client_channel/client_channel_plugin.cc new file mode 100644 index 0000000000..4431d11519 --- /dev/null +++ b/src/core/ext/filters/client_channel/client_channel_plugin.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/http_proxy.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/retry_throttle.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/surface/channel_init.h" + +static bool append_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, void *arg) { + return grpc_channel_stack_builder_append_filter( + builder, (const grpc_channel_filter *)arg, NULL, NULL); +} + +static bool set_default_host_if_unset(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, + void *unused) { + const grpc_channel_args *args = + grpc_channel_stack_builder_get_channel_arguments(builder); + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY) || + 0 == strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { + return true; + } + } + char *default_authority = grpc_get_default_authority( + exec_ctx, grpc_channel_stack_builder_get_target(builder)); + if (default_authority != NULL) { + grpc_arg arg = grpc_channel_arg_string_create( + (char *)GRPC_ARG_DEFAULT_AUTHORITY, default_authority); + grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); + grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder, + new_args); + gpr_free(default_authority); + grpc_channel_args_destroy(exec_ctx, new_args); + } + return true; +} + +extern "C" void grpc_client_channel_init(void) { + grpc_lb_policy_registry_init(); + grpc_resolver_registry_init(); + grpc_retry_throttle_map_init(); + grpc_proxy_mapper_registry_init(); + grpc_register_http_proxy_mapper(); + grpc_subchannel_index_init(); + grpc_channel_init_register_stage(GRPC_CLIENT_CHANNEL, INT_MIN, + set_default_host_if_unset, NULL); + grpc_channel_init_register_stage( + GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter, + (void *)&grpc_client_channel_filter); + grpc_http_connect_register_handshaker_factory(); + grpc_register_tracer(&grpc_client_channel_trace); +#ifndef NDEBUG + grpc_register_tracer(&grpc_trace_resolver_refcount); +#endif +} + +extern "C" void grpc_client_channel_shutdown(void) { + grpc_subchannel_index_shutdown(); + grpc_channel_init_shutdown(); + grpc_proxy_mapper_registry_shutdown(); + grpc_retry_throttle_map_shutdown(); + grpc_resolver_registry_shutdown(); + grpc_lb_policy_registry_shutdown(); +} diff --git a/src/core/ext/filters/client_channel/connector.c b/src/core/ext/filters/client_channel/connector.c deleted file mode 100644 index c258468e58..0000000000 --- a/src/core/ext/filters/client_channel/connector.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/connector.h" - -grpc_connector* grpc_connector_ref(grpc_connector* connector) { - connector->vtable->ref(connector); - return connector; -} - -void grpc_connector_unref(grpc_exec_ctx* exec_ctx, grpc_connector* connector) { - connector->vtable->unref(exec_ctx, connector); -} - -void grpc_connector_connect(grpc_exec_ctx* exec_ctx, grpc_connector* connector, - const grpc_connect_in_args* in_args, - grpc_connect_out_args* out_args, - grpc_closure* notify) { - connector->vtable->connect(exec_ctx, connector, in_args, out_args, notify); -} - -void grpc_connector_shutdown(grpc_exec_ctx* exec_ctx, grpc_connector* connector, - grpc_error* why) { - connector->vtable->shutdown(exec_ctx, connector, why); -} diff --git a/src/core/ext/filters/client_channel/connector.cc b/src/core/ext/filters/client_channel/connector.cc new file mode 100644 index 0000000000..c258468e58 --- /dev/null +++ b/src/core/ext/filters/client_channel/connector.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/connector.h" + +grpc_connector* grpc_connector_ref(grpc_connector* connector) { + connector->vtable->ref(connector); + return connector; +} + +void grpc_connector_unref(grpc_exec_ctx* exec_ctx, grpc_connector* connector) { + connector->vtable->unref(exec_ctx, connector); +} + +void grpc_connector_connect(grpc_exec_ctx* exec_ctx, grpc_connector* connector, + const grpc_connect_in_args* in_args, + grpc_connect_out_args* out_args, + grpc_closure* notify) { + connector->vtable->connect(exec_ctx, connector, in_args, out_args, notify); +} + +void grpc_connector_shutdown(grpc_exec_ctx* exec_ctx, grpc_connector* connector, + grpc_error* why) { + connector->vtable->shutdown(exec_ctx, connector, why); +} diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.c b/src/core/ext/filters/client_channel/http_connect_handshaker.c deleted file mode 100644 index 418bb41ef6..0000000000 --- a/src/core/ext/filters/client_channel/http_connect_handshaker.c +++ /dev/null @@ -1,376 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" - -#include - -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/ext/filters/client_channel/uri_parser.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker_registry.h" -#include "src/core/lib/http/format_request.h" -#include "src/core/lib/http/parser.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" - -typedef struct http_connect_handshaker { - // Base class. Must be first. - grpc_handshaker base; - - gpr_refcount refcount; - gpr_mu mu; - - bool shutdown; - // Endpoint and read buffer to destroy after a shutdown. - grpc_endpoint* endpoint_to_destroy; - grpc_slice_buffer* read_buffer_to_destroy; - - // State saved while performing the handshake. - grpc_handshaker_args* args; - grpc_closure* on_handshake_done; - - // Objects for processing the HTTP CONNECT request and response. - grpc_slice_buffer write_buffer; - grpc_closure request_done_closure; - grpc_closure response_read_closure; - grpc_http_parser http_parser; - grpc_http_response http_response; -} http_connect_handshaker; - -// Unref and clean up handshaker. -static void http_connect_handshaker_unref(grpc_exec_ctx* exec_ctx, - http_connect_handshaker* handshaker) { - if (gpr_unref(&handshaker->refcount)) { - gpr_mu_destroy(&handshaker->mu); - if (handshaker->endpoint_to_destroy != NULL) { - grpc_endpoint_destroy(exec_ctx, handshaker->endpoint_to_destroy); - } - if (handshaker->read_buffer_to_destroy != NULL) { - grpc_slice_buffer_destroy_internal(exec_ctx, - handshaker->read_buffer_to_destroy); - gpr_free(handshaker->read_buffer_to_destroy); - } - grpc_slice_buffer_destroy_internal(exec_ctx, &handshaker->write_buffer); - grpc_http_parser_destroy(&handshaker->http_parser); - grpc_http_response_destroy(&handshaker->http_response); - gpr_free(handshaker); - } -} - -// Set args fields to NULL, saving the endpoint and read buffer for -// later destruction. -static void cleanup_args_for_failure_locked( - grpc_exec_ctx* exec_ctx, http_connect_handshaker* handshaker) { - handshaker->endpoint_to_destroy = handshaker->args->endpoint; - handshaker->args->endpoint = NULL; - handshaker->read_buffer_to_destroy = handshaker->args->read_buffer; - handshaker->args->read_buffer = NULL; - grpc_channel_args_destroy(exec_ctx, handshaker->args->args); - handshaker->args->args = NULL; -} - -// If the handshake failed or we're shutting down, clean up and invoke the -// callback with the error. -static void handshake_failed_locked(grpc_exec_ctx* exec_ctx, - http_connect_handshaker* handshaker, - grpc_error* error) { - if (error == GRPC_ERROR_NONE) { - // If we were shut down after an endpoint operation succeeded but - // before the endpoint callback was invoked, we need to generate our - // own error. - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); - } - if (!handshaker->shutdown) { - // TODO(ctiller): It is currently necessary to shutdown endpoints - // before destroying them, even if we know that there are no - // pending read/write callbacks. This should be fixed, at which - // point this can be removed. - grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint, - GRPC_ERROR_REF(error)); - // Not shutting down, so the handshake failed. Clean up before - // invoking the callback. - cleanup_args_for_failure_locked(exec_ctx, handshaker); - // Set shutdown to true so that subsequent calls to - // http_connect_handshaker_shutdown() do nothing. - handshaker->shutdown = true; - } - // Invoke callback. - GRPC_CLOSURE_SCHED(exec_ctx, handshaker->on_handshake_done, error); -} - -// Callback invoked when finished writing HTTP CONNECT request. -static void on_write_done(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - http_connect_handshaker* handshaker = (http_connect_handshaker*)arg; - gpr_mu_lock(&handshaker->mu); - if (error != GRPC_ERROR_NONE || handshaker->shutdown) { - // If the write failed or we're shutting down, clean up and invoke the - // callback with the error. - handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error)); - gpr_mu_unlock(&handshaker->mu); - http_connect_handshaker_unref(exec_ctx, handshaker); - } else { - // Otherwise, read the response. - // The read callback inherits our ref to the handshaker. - grpc_endpoint_read(exec_ctx, handshaker->args->endpoint, - handshaker->args->read_buffer, - &handshaker->response_read_closure); - gpr_mu_unlock(&handshaker->mu); - } -} - -// Callback invoked for reading HTTP CONNECT response. -static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - http_connect_handshaker* handshaker = (http_connect_handshaker*)arg; - gpr_mu_lock(&handshaker->mu); - if (error != GRPC_ERROR_NONE || handshaker->shutdown) { - // If the read failed or we're shutting down, clean up and invoke the - // callback with the error. - handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error)); - goto done; - } - // Add buffer to parser. - for (size_t i = 0; i < handshaker->args->read_buffer->count; ++i) { - if (GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i]) > 0) { - size_t body_start_offset = 0; - error = grpc_http_parser_parse(&handshaker->http_parser, - handshaker->args->read_buffer->slices[i], - &body_start_offset); - if (error != GRPC_ERROR_NONE) { - handshake_failed_locked(exec_ctx, handshaker, error); - goto done; - } - if (handshaker->http_parser.state == GRPC_HTTP_BODY) { - // Remove the data we've already read from the read buffer, - // leaving only the leftover bytes (if any). - grpc_slice_buffer tmp_buffer; - grpc_slice_buffer_init(&tmp_buffer); - if (body_start_offset < - GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i])) { - grpc_slice_buffer_add( - &tmp_buffer, - grpc_slice_split_tail(&handshaker->args->read_buffer->slices[i], - body_start_offset)); - } - grpc_slice_buffer_addn(&tmp_buffer, - &handshaker->args->read_buffer->slices[i + 1], - handshaker->args->read_buffer->count - i - 1); - grpc_slice_buffer_swap(handshaker->args->read_buffer, &tmp_buffer); - grpc_slice_buffer_destroy_internal(exec_ctx, &tmp_buffer); - break; - } - } - } - // If we're not done reading the response, read more data. - // TODO(roth): In practice, I suspect that the response to a CONNECT - // request will never include a body, in which case this check is - // sufficient. However, the language of RFC-2817 doesn't explicitly - // forbid the response from including a body. If there is a body, - // it's possible that we might have parsed part but not all of the - // body, in which case this check will cause us to fail to parse the - // remainder of the body. If that ever becomes an issue, we may - // need to fix the HTTP parser to understand when the body is - // complete (e.g., handling chunked transfer encoding or looking - // at the Content-Length: header). - if (handshaker->http_parser.state != GRPC_HTTP_BODY) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, - handshaker->args->read_buffer); - grpc_endpoint_read(exec_ctx, handshaker->args->endpoint, - handshaker->args->read_buffer, - &handshaker->response_read_closure); - gpr_mu_unlock(&handshaker->mu); - return; - } - // Make sure we got a 2xx response. - if (handshaker->http_response.status < 200 || - handshaker->http_response.status >= 300) { - char* msg; - gpr_asprintf(&msg, "HTTP proxy returned response code %d", - handshaker->http_response.status); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - handshake_failed_locked(exec_ctx, handshaker, error); - goto done; - } - // Success. Invoke handshake-done callback. - GRPC_CLOSURE_SCHED(exec_ctx, handshaker->on_handshake_done, error); -done: - // Set shutdown to true so that subsequent calls to - // http_connect_handshaker_shutdown() do nothing. - handshaker->shutdown = true; - gpr_mu_unlock(&handshaker->mu); - http_connect_handshaker_unref(exec_ctx, handshaker); -} - -// -// Public handshaker methods -// - -static void http_connect_handshaker_destroy(grpc_exec_ctx* exec_ctx, - grpc_handshaker* handshaker_in) { - http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in; - http_connect_handshaker_unref(exec_ctx, handshaker); -} - -static void http_connect_handshaker_shutdown(grpc_exec_ctx* exec_ctx, - grpc_handshaker* handshaker_in, - grpc_error* why) { - http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in; - gpr_mu_lock(&handshaker->mu); - if (!handshaker->shutdown) { - handshaker->shutdown = true; - grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint, - GRPC_ERROR_REF(why)); - cleanup_args_for_failure_locked(exec_ctx, handshaker); - } - gpr_mu_unlock(&handshaker->mu); - GRPC_ERROR_UNREF(why); -} - -static void http_connect_handshaker_do_handshake( - grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker_in, - grpc_tcp_server_acceptor* acceptor, grpc_closure* on_handshake_done, - grpc_handshaker_args* args) { - http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in; - // Check for HTTP CONNECT channel arg. - // If not found, invoke on_handshake_done without doing anything. - const grpc_arg* arg = - grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_SERVER); - if (arg == NULL) { - // Set shutdown to true so that subsequent calls to - // http_connect_handshaker_shutdown() do nothing. - gpr_mu_lock(&handshaker->mu); - handshaker->shutdown = true; - gpr_mu_unlock(&handshaker->mu); - GRPC_CLOSURE_SCHED(exec_ctx, on_handshake_done, GRPC_ERROR_NONE); - return; - } - GPR_ASSERT(arg->type == GRPC_ARG_STRING); - char* server_name = arg->value.string; - // Get headers from channel args. - arg = grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_HEADERS); - grpc_http_header* headers = NULL; - size_t num_headers = 0; - char** header_strings = NULL; - size_t num_header_strings = 0; - if (arg != NULL) { - GPR_ASSERT(arg->type == GRPC_ARG_STRING); - gpr_string_split(arg->value.string, "\n", &header_strings, - &num_header_strings); - headers = (grpc_http_header*)gpr_malloc(sizeof(grpc_http_header) * - num_header_strings); - for (size_t i = 0; i < num_header_strings; ++i) { - char* sep = strchr(header_strings[i], ':'); - if (sep == NULL) { - gpr_log(GPR_ERROR, "skipping unparseable HTTP CONNECT header: %s", - header_strings[i]); - continue; - } - *sep = '\0'; - headers[num_headers].key = header_strings[i]; - headers[num_headers].value = sep + 1; - ++num_headers; - } - } - // Save state in the handshaker object. - gpr_mu_lock(&handshaker->mu); - handshaker->args = args; - handshaker->on_handshake_done = on_handshake_done; - // Log connection via proxy. - char* proxy_name = grpc_endpoint_get_peer(args->endpoint); - gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name, - proxy_name); - gpr_free(proxy_name); - // Construct HTTP CONNECT request. - grpc_httpcli_request request; - memset(&request, 0, sizeof(request)); - request.host = server_name; - request.http.method = (char*)"CONNECT"; - request.http.path = server_name; - request.http.hdrs = headers; - request.http.hdr_count = num_headers; - request.handshaker = &grpc_httpcli_plaintext; - grpc_slice request_slice = grpc_httpcli_format_connect_request(&request); - grpc_slice_buffer_add(&handshaker->write_buffer, request_slice); - // Clean up. - gpr_free(headers); - for (size_t i = 0; i < num_header_strings; ++i) { - gpr_free(header_strings[i]); - } - gpr_free(header_strings); - // Take a new ref to be held by the write callback. - gpr_ref(&handshaker->refcount); - grpc_endpoint_write(exec_ctx, args->endpoint, &handshaker->write_buffer, - &handshaker->request_done_closure); - gpr_mu_unlock(&handshaker->mu); -} - -static const grpc_handshaker_vtable http_connect_handshaker_vtable = { - http_connect_handshaker_destroy, http_connect_handshaker_shutdown, - http_connect_handshaker_do_handshake}; - -static grpc_handshaker* grpc_http_connect_handshaker_create() { - http_connect_handshaker* handshaker = - (http_connect_handshaker*)gpr_malloc(sizeof(*handshaker)); - memset(handshaker, 0, sizeof(*handshaker)); - grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base); - gpr_mu_init(&handshaker->mu); - gpr_ref_init(&handshaker->refcount, 1); - grpc_slice_buffer_init(&handshaker->write_buffer); - GRPC_CLOSURE_INIT(&handshaker->request_done_closure, on_write_done, - handshaker, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&handshaker->response_read_closure, on_read_done, - handshaker, grpc_schedule_on_exec_ctx); - grpc_http_parser_init(&handshaker->http_parser, GRPC_HTTP_RESPONSE, - &handshaker->http_response); - return &handshaker->base; -} - -// -// handshaker factory -// - -static void handshaker_factory_add_handshakers( - grpc_exec_ctx* exec_ctx, grpc_handshaker_factory* factory, - const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) { - grpc_handshake_manager_add(handshake_mgr, - grpc_http_connect_handshaker_create()); -} - -static void handshaker_factory_destroy(grpc_exec_ctx* exec_ctx, - grpc_handshaker_factory* factory) {} - -static const grpc_handshaker_factory_vtable handshaker_factory_vtable = { - handshaker_factory_add_handshakers, handshaker_factory_destroy}; - -static grpc_handshaker_factory handshaker_factory = { - &handshaker_factory_vtable}; - -void grpc_http_connect_register_handshaker_factory() { - grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_CLIENT, - &handshaker_factory); -} diff --git a/src/core/ext/filters/client_channel/http_connect_handshaker.cc b/src/core/ext/filters/client_channel/http_connect_handshaker.cc new file mode 100644 index 0000000000..418bb41ef6 --- /dev/null +++ b/src/core/ext/filters/client_channel/http_connect_handshaker.cc @@ -0,0 +1,376 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" + +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/uri_parser.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/http/format_request.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +typedef struct http_connect_handshaker { + // Base class. Must be first. + grpc_handshaker base; + + gpr_refcount refcount; + gpr_mu mu; + + bool shutdown; + // Endpoint and read buffer to destroy after a shutdown. + grpc_endpoint* endpoint_to_destroy; + grpc_slice_buffer* read_buffer_to_destroy; + + // State saved while performing the handshake. + grpc_handshaker_args* args; + grpc_closure* on_handshake_done; + + // Objects for processing the HTTP CONNECT request and response. + grpc_slice_buffer write_buffer; + grpc_closure request_done_closure; + grpc_closure response_read_closure; + grpc_http_parser http_parser; + grpc_http_response http_response; +} http_connect_handshaker; + +// Unref and clean up handshaker. +static void http_connect_handshaker_unref(grpc_exec_ctx* exec_ctx, + http_connect_handshaker* handshaker) { + if (gpr_unref(&handshaker->refcount)) { + gpr_mu_destroy(&handshaker->mu); + if (handshaker->endpoint_to_destroy != NULL) { + grpc_endpoint_destroy(exec_ctx, handshaker->endpoint_to_destroy); + } + if (handshaker->read_buffer_to_destroy != NULL) { + grpc_slice_buffer_destroy_internal(exec_ctx, + handshaker->read_buffer_to_destroy); + gpr_free(handshaker->read_buffer_to_destroy); + } + grpc_slice_buffer_destroy_internal(exec_ctx, &handshaker->write_buffer); + grpc_http_parser_destroy(&handshaker->http_parser); + grpc_http_response_destroy(&handshaker->http_response); + gpr_free(handshaker); + } +} + +// Set args fields to NULL, saving the endpoint and read buffer for +// later destruction. +static void cleanup_args_for_failure_locked( + grpc_exec_ctx* exec_ctx, http_connect_handshaker* handshaker) { + handshaker->endpoint_to_destroy = handshaker->args->endpoint; + handshaker->args->endpoint = NULL; + handshaker->read_buffer_to_destroy = handshaker->args->read_buffer; + handshaker->args->read_buffer = NULL; + grpc_channel_args_destroy(exec_ctx, handshaker->args->args); + handshaker->args->args = NULL; +} + +// If the handshake failed or we're shutting down, clean up and invoke the +// callback with the error. +static void handshake_failed_locked(grpc_exec_ctx* exec_ctx, + http_connect_handshaker* handshaker, + grpc_error* error) { + if (error == GRPC_ERROR_NONE) { + // If we were shut down after an endpoint operation succeeded but + // before the endpoint callback was invoked, we need to generate our + // own error. + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); + } + if (!handshaker->shutdown) { + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint, + GRPC_ERROR_REF(error)); + // Not shutting down, so the handshake failed. Clean up before + // invoking the callback. + cleanup_args_for_failure_locked(exec_ctx, handshaker); + // Set shutdown to true so that subsequent calls to + // http_connect_handshaker_shutdown() do nothing. + handshaker->shutdown = true; + } + // Invoke callback. + GRPC_CLOSURE_SCHED(exec_ctx, handshaker->on_handshake_done, error); +} + +// Callback invoked when finished writing HTTP CONNECT request. +static void on_write_done(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + http_connect_handshaker* handshaker = (http_connect_handshaker*)arg; + gpr_mu_lock(&handshaker->mu); + if (error != GRPC_ERROR_NONE || handshaker->shutdown) { + // If the write failed or we're shutting down, clean up and invoke the + // callback with the error. + handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&handshaker->mu); + http_connect_handshaker_unref(exec_ctx, handshaker); + } else { + // Otherwise, read the response. + // The read callback inherits our ref to the handshaker. + grpc_endpoint_read(exec_ctx, handshaker->args->endpoint, + handshaker->args->read_buffer, + &handshaker->response_read_closure); + gpr_mu_unlock(&handshaker->mu); + } +} + +// Callback invoked for reading HTTP CONNECT response. +static void on_read_done(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + http_connect_handshaker* handshaker = (http_connect_handshaker*)arg; + gpr_mu_lock(&handshaker->mu); + if (error != GRPC_ERROR_NONE || handshaker->shutdown) { + // If the read failed or we're shutting down, clean up and invoke the + // callback with the error. + handshake_failed_locked(exec_ctx, handshaker, GRPC_ERROR_REF(error)); + goto done; + } + // Add buffer to parser. + for (size_t i = 0; i < handshaker->args->read_buffer->count; ++i) { + if (GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i]) > 0) { + size_t body_start_offset = 0; + error = grpc_http_parser_parse(&handshaker->http_parser, + handshaker->args->read_buffer->slices[i], + &body_start_offset); + if (error != GRPC_ERROR_NONE) { + handshake_failed_locked(exec_ctx, handshaker, error); + goto done; + } + if (handshaker->http_parser.state == GRPC_HTTP_BODY) { + // Remove the data we've already read from the read buffer, + // leaving only the leftover bytes (if any). + grpc_slice_buffer tmp_buffer; + grpc_slice_buffer_init(&tmp_buffer); + if (body_start_offset < + GRPC_SLICE_LENGTH(handshaker->args->read_buffer->slices[i])) { + grpc_slice_buffer_add( + &tmp_buffer, + grpc_slice_split_tail(&handshaker->args->read_buffer->slices[i], + body_start_offset)); + } + grpc_slice_buffer_addn(&tmp_buffer, + &handshaker->args->read_buffer->slices[i + 1], + handshaker->args->read_buffer->count - i - 1); + grpc_slice_buffer_swap(handshaker->args->read_buffer, &tmp_buffer); + grpc_slice_buffer_destroy_internal(exec_ctx, &tmp_buffer); + break; + } + } + } + // If we're not done reading the response, read more data. + // TODO(roth): In practice, I suspect that the response to a CONNECT + // request will never include a body, in which case this check is + // sufficient. However, the language of RFC-2817 doesn't explicitly + // forbid the response from including a body. If there is a body, + // it's possible that we might have parsed part but not all of the + // body, in which case this check will cause us to fail to parse the + // remainder of the body. If that ever becomes an issue, we may + // need to fix the HTTP parser to understand when the body is + // complete (e.g., handling chunked transfer encoding or looking + // at the Content-Length: header). + if (handshaker->http_parser.state != GRPC_HTTP_BODY) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + handshaker->args->read_buffer); + grpc_endpoint_read(exec_ctx, handshaker->args->endpoint, + handshaker->args->read_buffer, + &handshaker->response_read_closure); + gpr_mu_unlock(&handshaker->mu); + return; + } + // Make sure we got a 2xx response. + if (handshaker->http_response.status < 200 || + handshaker->http_response.status >= 300) { + char* msg; + gpr_asprintf(&msg, "HTTP proxy returned response code %d", + handshaker->http_response.status); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + handshake_failed_locked(exec_ctx, handshaker, error); + goto done; + } + // Success. Invoke handshake-done callback. + GRPC_CLOSURE_SCHED(exec_ctx, handshaker->on_handshake_done, error); +done: + // Set shutdown to true so that subsequent calls to + // http_connect_handshaker_shutdown() do nothing. + handshaker->shutdown = true; + gpr_mu_unlock(&handshaker->mu); + http_connect_handshaker_unref(exec_ctx, handshaker); +} + +// +// Public handshaker methods +// + +static void http_connect_handshaker_destroy(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker_in) { + http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in; + http_connect_handshaker_unref(exec_ctx, handshaker); +} + +static void http_connect_handshaker_shutdown(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker_in, + grpc_error* why) { + http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in; + gpr_mu_lock(&handshaker->mu); + if (!handshaker->shutdown) { + handshaker->shutdown = true; + grpc_endpoint_shutdown(exec_ctx, handshaker->args->endpoint, + GRPC_ERROR_REF(why)); + cleanup_args_for_failure_locked(exec_ctx, handshaker); + } + gpr_mu_unlock(&handshaker->mu); + GRPC_ERROR_UNREF(why); +} + +static void http_connect_handshaker_do_handshake( + grpc_exec_ctx* exec_ctx, grpc_handshaker* handshaker_in, + grpc_tcp_server_acceptor* acceptor, grpc_closure* on_handshake_done, + grpc_handshaker_args* args) { + http_connect_handshaker* handshaker = (http_connect_handshaker*)handshaker_in; + // Check for HTTP CONNECT channel arg. + // If not found, invoke on_handshake_done without doing anything. + const grpc_arg* arg = + grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_SERVER); + if (arg == NULL) { + // Set shutdown to true so that subsequent calls to + // http_connect_handshaker_shutdown() do nothing. + gpr_mu_lock(&handshaker->mu); + handshaker->shutdown = true; + gpr_mu_unlock(&handshaker->mu); + GRPC_CLOSURE_SCHED(exec_ctx, on_handshake_done, GRPC_ERROR_NONE); + return; + } + GPR_ASSERT(arg->type == GRPC_ARG_STRING); + char* server_name = arg->value.string; + // Get headers from channel args. + arg = grpc_channel_args_find(args->args, GRPC_ARG_HTTP_CONNECT_HEADERS); + grpc_http_header* headers = NULL; + size_t num_headers = 0; + char** header_strings = NULL; + size_t num_header_strings = 0; + if (arg != NULL) { + GPR_ASSERT(arg->type == GRPC_ARG_STRING); + gpr_string_split(arg->value.string, "\n", &header_strings, + &num_header_strings); + headers = (grpc_http_header*)gpr_malloc(sizeof(grpc_http_header) * + num_header_strings); + for (size_t i = 0; i < num_header_strings; ++i) { + char* sep = strchr(header_strings[i], ':'); + if (sep == NULL) { + gpr_log(GPR_ERROR, "skipping unparseable HTTP CONNECT header: %s", + header_strings[i]); + continue; + } + *sep = '\0'; + headers[num_headers].key = header_strings[i]; + headers[num_headers].value = sep + 1; + ++num_headers; + } + } + // Save state in the handshaker object. + gpr_mu_lock(&handshaker->mu); + handshaker->args = args; + handshaker->on_handshake_done = on_handshake_done; + // Log connection via proxy. + char* proxy_name = grpc_endpoint_get_peer(args->endpoint); + gpr_log(GPR_INFO, "Connecting to server %s via HTTP proxy %s", server_name, + proxy_name); + gpr_free(proxy_name); + // Construct HTTP CONNECT request. + grpc_httpcli_request request; + memset(&request, 0, sizeof(request)); + request.host = server_name; + request.http.method = (char*)"CONNECT"; + request.http.path = server_name; + request.http.hdrs = headers; + request.http.hdr_count = num_headers; + request.handshaker = &grpc_httpcli_plaintext; + grpc_slice request_slice = grpc_httpcli_format_connect_request(&request); + grpc_slice_buffer_add(&handshaker->write_buffer, request_slice); + // Clean up. + gpr_free(headers); + for (size_t i = 0; i < num_header_strings; ++i) { + gpr_free(header_strings[i]); + } + gpr_free(header_strings); + // Take a new ref to be held by the write callback. + gpr_ref(&handshaker->refcount); + grpc_endpoint_write(exec_ctx, args->endpoint, &handshaker->write_buffer, + &handshaker->request_done_closure); + gpr_mu_unlock(&handshaker->mu); +} + +static const grpc_handshaker_vtable http_connect_handshaker_vtable = { + http_connect_handshaker_destroy, http_connect_handshaker_shutdown, + http_connect_handshaker_do_handshake}; + +static grpc_handshaker* grpc_http_connect_handshaker_create() { + http_connect_handshaker* handshaker = + (http_connect_handshaker*)gpr_malloc(sizeof(*handshaker)); + memset(handshaker, 0, sizeof(*handshaker)); + grpc_handshaker_init(&http_connect_handshaker_vtable, &handshaker->base); + gpr_mu_init(&handshaker->mu); + gpr_ref_init(&handshaker->refcount, 1); + grpc_slice_buffer_init(&handshaker->write_buffer); + GRPC_CLOSURE_INIT(&handshaker->request_done_closure, on_write_done, + handshaker, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&handshaker->response_read_closure, on_read_done, + handshaker, grpc_schedule_on_exec_ctx); + grpc_http_parser_init(&handshaker->http_parser, GRPC_HTTP_RESPONSE, + &handshaker->http_response); + return &handshaker->base; +} + +// +// handshaker factory +// + +static void handshaker_factory_add_handshakers( + grpc_exec_ctx* exec_ctx, grpc_handshaker_factory* factory, + const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) { + grpc_handshake_manager_add(handshake_mgr, + grpc_http_connect_handshaker_create()); +} + +static void handshaker_factory_destroy(grpc_exec_ctx* exec_ctx, + grpc_handshaker_factory* factory) {} + +static const grpc_handshaker_factory_vtable handshaker_factory_vtable = { + handshaker_factory_add_handshakers, handshaker_factory_destroy}; + +static grpc_handshaker_factory handshaker_factory = { + &handshaker_factory_vtable}; + +void grpc_http_connect_register_handshaker_factory() { + grpc_handshaker_factory_register(true /* at_start */, HANDSHAKER_CLIENT, + &handshaker_factory); +} diff --git a/src/core/ext/filters/client_channel/http_proxy.c b/src/core/ext/filters/client_channel/http_proxy.c deleted file mode 100644 index a16b44d3dc..0000000000 --- a/src/core/ext/filters/client_channel/http_proxy.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/http_proxy.h" - -#include -#include - -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" -#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" -#include "src/core/ext/filters/client_channel/uri_parser.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/slice/b64.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" - -/** - * Parses the 'http_proxy' env var and returns the proxy hostname to resolve or - * NULL on error. Also sets 'user_cred' to user credentials if present in the - * 'http_proxy' env var, otherwise leaves it unchanged. It is caller's - * responsibility to gpr_free user_cred. - */ -static char* get_http_proxy_server(grpc_exec_ctx* exec_ctx, char** user_cred) { - GPR_ASSERT(user_cred != NULL); - char* proxy_name = NULL; - char* uri_str = gpr_getenv("http_proxy"); - char** authority_strs = NULL; - size_t authority_nstrs; - if (uri_str == NULL) return NULL; - grpc_uri* uri = - grpc_uri_parse(exec_ctx, uri_str, false /* suppress_errors */); - if (uri == NULL || uri->authority == NULL) { - gpr_log(GPR_ERROR, "cannot parse value of 'http_proxy' env var"); - goto done; - } - if (strcmp(uri->scheme, "http") != 0) { - gpr_log(GPR_ERROR, "'%s' scheme not supported in proxy URI", uri->scheme); - goto done; - } - /* Split on '@' to separate user credentials from host */ - gpr_string_split(uri->authority, "@", &authority_strs, &authority_nstrs); - GPR_ASSERT(authority_nstrs != 0); /* should have at least 1 string */ - if (authority_nstrs == 1) { - /* User cred not present in authority */ - proxy_name = authority_strs[0]; - } else if (authority_nstrs == 2) { - /* User cred found */ - *user_cred = authority_strs[0]; - proxy_name = authority_strs[1]; - gpr_log(GPR_DEBUG, "userinfo found in proxy URI"); - } else { - /* Bad authority */ - for (size_t i = 0; i < authority_nstrs; i++) { - gpr_free(authority_strs[i]); - } - proxy_name = NULL; - } - gpr_free(authority_strs); -done: - gpr_free(uri_str); - grpc_uri_destroy(uri); - return proxy_name; -} - -static bool proxy_mapper_map_name(grpc_exec_ctx* exec_ctx, - grpc_proxy_mapper* mapper, - const char* server_uri, - const grpc_channel_args* args, - char** name_to_resolve, - grpc_channel_args** new_args) { - char* user_cred = NULL; - *name_to_resolve = get_http_proxy_server(exec_ctx, &user_cred); - if (*name_to_resolve == NULL) return false; - char* no_proxy_str = NULL; - grpc_uri* uri = - grpc_uri_parse(exec_ctx, server_uri, false /* suppress_errors */); - if (uri == NULL || uri->path[0] == '\0') { - gpr_log(GPR_ERROR, - "'http_proxy' environment variable set, but cannot " - "parse server URI '%s' -- not using proxy", - server_uri); - goto no_use_proxy; - } - if (strcmp(uri->scheme, "unix") == 0) { - gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'", - server_uri); - goto no_use_proxy; - } - no_proxy_str = gpr_getenv("no_proxy"); - if (no_proxy_str != NULL) { - static const char* NO_PROXY_SEPARATOR = ","; - bool use_proxy = true; - char* server_host; - char* server_port; - if (!gpr_split_host_port(uri->path[0] == '/' ? uri->path + 1 : uri->path, - &server_host, &server_port)) { - gpr_log(GPR_INFO, - "unable to split host and port, not checking no_proxy list for " - "host '%s'", - server_uri); - } else { - size_t uri_len = strlen(server_host); - char** no_proxy_hosts; - size_t num_no_proxy_hosts; - gpr_string_split(no_proxy_str, NO_PROXY_SEPARATOR, &no_proxy_hosts, - &num_no_proxy_hosts); - for (size_t i = 0; i < num_no_proxy_hosts; i++) { - char* no_proxy_entry = no_proxy_hosts[i]; - size_t no_proxy_len = strlen(no_proxy_entry); - if (no_proxy_len <= uri_len && - gpr_stricmp(no_proxy_entry, &server_host[uri_len - no_proxy_len]) == - 0) { - gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'", - server_uri); - use_proxy = false; - break; - } - } - for (size_t i = 0; i < num_no_proxy_hosts; i++) { - gpr_free(no_proxy_hosts[i]); - } - gpr_free(no_proxy_hosts); - gpr_free(server_host); - gpr_free(server_port); - if (!use_proxy) goto no_use_proxy; - } - } - grpc_arg args_to_add[2]; - args_to_add[0] = grpc_channel_arg_string_create( - (char*)GRPC_ARG_HTTP_CONNECT_SERVER, - uri->path[0] == '/' ? uri->path + 1 : uri->path); - if (user_cred != NULL) { - /* Use base64 encoding for user credentials as stated in RFC 7617 */ - char* encoded_user_cred = - grpc_base64_encode(user_cred, strlen(user_cred), 0, 0); - char* header; - gpr_asprintf(&header, "Proxy-Authorization:Basic %s", encoded_user_cred); - gpr_free(encoded_user_cred); - args_to_add[1] = grpc_channel_arg_string_create( - (char*)GRPC_ARG_HTTP_CONNECT_HEADERS, header); - *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 2); - gpr_free(header); - } else { - *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1); - } - grpc_uri_destroy(uri); - gpr_free(user_cred); - return true; -no_use_proxy: - if (uri != NULL) grpc_uri_destroy(uri); - gpr_free(*name_to_resolve); - *name_to_resolve = NULL; - gpr_free(user_cred); - return false; -} - -static bool proxy_mapper_map_address(grpc_exec_ctx* exec_ctx, - grpc_proxy_mapper* mapper, - const grpc_resolved_address* address, - const grpc_channel_args* args, - grpc_resolved_address** new_address, - grpc_channel_args** new_args) { - return false; -} - -static void proxy_mapper_destroy(grpc_proxy_mapper* mapper) {} - -static const grpc_proxy_mapper_vtable proxy_mapper_vtable = { - proxy_mapper_map_name, proxy_mapper_map_address, proxy_mapper_destroy}; - -static grpc_proxy_mapper proxy_mapper = {&proxy_mapper_vtable}; - -void grpc_register_http_proxy_mapper() { - grpc_proxy_mapper_register(true /* at_start */, &proxy_mapper); -} diff --git a/src/core/ext/filters/client_channel/http_proxy.cc b/src/core/ext/filters/client_channel/http_proxy.cc new file mode 100644 index 0000000000..a16b44d3dc --- /dev/null +++ b/src/core/ext/filters/client_channel/http_proxy.cc @@ -0,0 +1,195 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/http_proxy.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/uri_parser.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +/** + * Parses the 'http_proxy' env var and returns the proxy hostname to resolve or + * NULL on error. Also sets 'user_cred' to user credentials if present in the + * 'http_proxy' env var, otherwise leaves it unchanged. It is caller's + * responsibility to gpr_free user_cred. + */ +static char* get_http_proxy_server(grpc_exec_ctx* exec_ctx, char** user_cred) { + GPR_ASSERT(user_cred != NULL); + char* proxy_name = NULL; + char* uri_str = gpr_getenv("http_proxy"); + char** authority_strs = NULL; + size_t authority_nstrs; + if (uri_str == NULL) return NULL; + grpc_uri* uri = + grpc_uri_parse(exec_ctx, uri_str, false /* suppress_errors */); + if (uri == NULL || uri->authority == NULL) { + gpr_log(GPR_ERROR, "cannot parse value of 'http_proxy' env var"); + goto done; + } + if (strcmp(uri->scheme, "http") != 0) { + gpr_log(GPR_ERROR, "'%s' scheme not supported in proxy URI", uri->scheme); + goto done; + } + /* Split on '@' to separate user credentials from host */ + gpr_string_split(uri->authority, "@", &authority_strs, &authority_nstrs); + GPR_ASSERT(authority_nstrs != 0); /* should have at least 1 string */ + if (authority_nstrs == 1) { + /* User cred not present in authority */ + proxy_name = authority_strs[0]; + } else if (authority_nstrs == 2) { + /* User cred found */ + *user_cred = authority_strs[0]; + proxy_name = authority_strs[1]; + gpr_log(GPR_DEBUG, "userinfo found in proxy URI"); + } else { + /* Bad authority */ + for (size_t i = 0; i < authority_nstrs; i++) { + gpr_free(authority_strs[i]); + } + proxy_name = NULL; + } + gpr_free(authority_strs); +done: + gpr_free(uri_str); + grpc_uri_destroy(uri); + return proxy_name; +} + +static bool proxy_mapper_map_name(grpc_exec_ctx* exec_ctx, + grpc_proxy_mapper* mapper, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + char* user_cred = NULL; + *name_to_resolve = get_http_proxy_server(exec_ctx, &user_cred); + if (*name_to_resolve == NULL) return false; + char* no_proxy_str = NULL; + grpc_uri* uri = + grpc_uri_parse(exec_ctx, server_uri, false /* suppress_errors */); + if (uri == NULL || uri->path[0] == '\0') { + gpr_log(GPR_ERROR, + "'http_proxy' environment variable set, but cannot " + "parse server URI '%s' -- not using proxy", + server_uri); + goto no_use_proxy; + } + if (strcmp(uri->scheme, "unix") == 0) { + gpr_log(GPR_INFO, "not using proxy for Unix domain socket '%s'", + server_uri); + goto no_use_proxy; + } + no_proxy_str = gpr_getenv("no_proxy"); + if (no_proxy_str != NULL) { + static const char* NO_PROXY_SEPARATOR = ","; + bool use_proxy = true; + char* server_host; + char* server_port; + if (!gpr_split_host_port(uri->path[0] == '/' ? uri->path + 1 : uri->path, + &server_host, &server_port)) { + gpr_log(GPR_INFO, + "unable to split host and port, not checking no_proxy list for " + "host '%s'", + server_uri); + } else { + size_t uri_len = strlen(server_host); + char** no_proxy_hosts; + size_t num_no_proxy_hosts; + gpr_string_split(no_proxy_str, NO_PROXY_SEPARATOR, &no_proxy_hosts, + &num_no_proxy_hosts); + for (size_t i = 0; i < num_no_proxy_hosts; i++) { + char* no_proxy_entry = no_proxy_hosts[i]; + size_t no_proxy_len = strlen(no_proxy_entry); + if (no_proxy_len <= uri_len && + gpr_stricmp(no_proxy_entry, &server_host[uri_len - no_proxy_len]) == + 0) { + gpr_log(GPR_INFO, "not using proxy for host in no_proxy list '%s'", + server_uri); + use_proxy = false; + break; + } + } + for (size_t i = 0; i < num_no_proxy_hosts; i++) { + gpr_free(no_proxy_hosts[i]); + } + gpr_free(no_proxy_hosts); + gpr_free(server_host); + gpr_free(server_port); + if (!use_proxy) goto no_use_proxy; + } + } + grpc_arg args_to_add[2]; + args_to_add[0] = grpc_channel_arg_string_create( + (char*)GRPC_ARG_HTTP_CONNECT_SERVER, + uri->path[0] == '/' ? uri->path + 1 : uri->path); + if (user_cred != NULL) { + /* Use base64 encoding for user credentials as stated in RFC 7617 */ + char* encoded_user_cred = + grpc_base64_encode(user_cred, strlen(user_cred), 0, 0); + char* header; + gpr_asprintf(&header, "Proxy-Authorization:Basic %s", encoded_user_cred); + gpr_free(encoded_user_cred); + args_to_add[1] = grpc_channel_arg_string_create( + (char*)GRPC_ARG_HTTP_CONNECT_HEADERS, header); + *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 2); + gpr_free(header); + } else { + *new_args = grpc_channel_args_copy_and_add(args, args_to_add, 1); + } + grpc_uri_destroy(uri); + gpr_free(user_cred); + return true; +no_use_proxy: + if (uri != NULL) grpc_uri_destroy(uri); + gpr_free(*name_to_resolve); + *name_to_resolve = NULL; + gpr_free(user_cred); + return false; +} + +static bool proxy_mapper_map_address(grpc_exec_ctx* exec_ctx, + grpc_proxy_mapper* mapper, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + return false; +} + +static void proxy_mapper_destroy(grpc_proxy_mapper* mapper) {} + +static const grpc_proxy_mapper_vtable proxy_mapper_vtable = { + proxy_mapper_map_name, proxy_mapper_map_address, proxy_mapper_destroy}; + +static grpc_proxy_mapper proxy_mapper = {&proxy_mapper_vtable}; + +void grpc_register_http_proxy_mapper() { + grpc_proxy_mapper_register(true /* at_start */, &proxy_mapper); +} diff --git a/src/core/ext/filters/client_channel/lb_policy.c b/src/core/ext/filters/client_channel/lb_policy.c deleted file mode 100644 index 8e6673d737..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/lb_policy.h" -#include "src/core/lib/iomgr/combiner.h" - -#define WEAK_REF_BITS 16 - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_lb_policy_refcount = - GRPC_TRACER_INITIALIZER(false, "lb_policy_refcount"); -#endif - -void grpc_lb_policy_init(grpc_lb_policy *policy, - const grpc_lb_policy_vtable *vtable, - grpc_combiner *combiner) { - policy->vtable = vtable; - gpr_atm_no_barrier_store(&policy->ref_pair, 1 << WEAK_REF_BITS); - policy->interested_parties = grpc_pollset_set_create(); - policy->combiner = GRPC_COMBINER_REF(combiner, "lb_policy"); -} - -#ifndef NDEBUG -#define REF_FUNC_EXTRA_ARGS , const char *file, int line, const char *reason -#define REF_MUTATE_EXTRA_ARGS REF_FUNC_EXTRA_ARGS, const char *purpose -#define REF_FUNC_PASS_ARGS(new_reason) , file, line, new_reason -#define REF_MUTATE_PASS_ARGS(purpose) , file, line, reason, purpose -#else -#define REF_FUNC_EXTRA_ARGS -#define REF_MUTATE_EXTRA_ARGS -#define REF_FUNC_PASS_ARGS(new_reason) -#define REF_MUTATE_PASS_ARGS(x) -#endif - -static gpr_atm ref_mutate(grpc_lb_policy *c, gpr_atm delta, - int barrier REF_MUTATE_EXTRA_ARGS) { - gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) - : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_lb_policy_refcount)) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "LB_POLICY: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c, - purpose, old_val, old_val + delta, reason); - } -#endif - return old_val; -} - -void grpc_lb_policy_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - ref_mutate(policy, 1 << WEAK_REF_BITS, 0 REF_MUTATE_PASS_ARGS("STRONG_REF")); -} - -static void shutdown_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_lb_policy *policy = (grpc_lb_policy *)arg; - policy->vtable->shutdown_locked(exec_ctx, policy); - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, policy, "strong-unref"); -} - -void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - gpr_atm old_val = - ref_mutate(policy, (gpr_atm)1 - (gpr_atm)(1 << WEAK_REF_BITS), - 1 REF_MUTATE_PASS_ARGS("STRONG_UNREF")); - gpr_atm mask = ~(gpr_atm)((1 << WEAK_REF_BITS) - 1); - gpr_atm check = 1 << WEAK_REF_BITS; - if ((old_val & mask) == check) { - GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE( - shutdown_locked, policy, - grpc_combiner_scheduler(policy->combiner)), - GRPC_ERROR_NONE); - } else { - grpc_lb_policy_weak_unref(exec_ctx, - policy REF_FUNC_PASS_ARGS("strong-unref")); - } -} - -void grpc_lb_policy_weak_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - ref_mutate(policy, 1, 0 REF_MUTATE_PASS_ARGS("WEAK_REF")); -} - -void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { - gpr_atm old_val = - ref_mutate(policy, -(gpr_atm)1, 1 REF_MUTATE_PASS_ARGS("WEAK_UNREF")); - if (old_val == 1) { - grpc_pollset_set_destroy(exec_ctx, policy->interested_parties); - grpc_combiner *combiner = policy->combiner; - policy->vtable->destroy(exec_ctx, policy); - GRPC_COMBINER_UNREF(exec_ctx, combiner, "lb_policy"); - } -} - -int grpc_lb_policy_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - const grpc_lb_policy_pick_args *pick_args, - grpc_connected_subchannel **target, - grpc_call_context_element *context, - void **user_data, grpc_closure *on_complete) { - return policy->vtable->pick_locked(exec_ctx, policy, pick_args, target, - context, user_data, on_complete); -} - -void grpc_lb_policy_cancel_pick_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - grpc_connected_subchannel **target, - grpc_error *error) { - policy->vtable->cancel_pick_locked(exec_ctx, policy, target, error); -} - -void grpc_lb_policy_cancel_picks_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - uint32_t initial_metadata_flags_mask, - uint32_t initial_metadata_flags_eq, - grpc_error *error) { - policy->vtable->cancel_picks_locked(exec_ctx, policy, - initial_metadata_flags_mask, - initial_metadata_flags_eq, error); -} - -void grpc_lb_policy_exit_idle_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy) { - policy->vtable->exit_idle_locked(exec_ctx, policy); -} - -void grpc_lb_policy_ping_one_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - grpc_closure *closure) { - policy->vtable->ping_one_locked(exec_ctx, policy, closure); -} - -void grpc_lb_policy_notify_on_state_change_locked( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_connectivity_state *state, grpc_closure *closure) { - policy->vtable->notify_on_state_change_locked(exec_ctx, policy, state, - closure); -} - -grpc_connectivity_state grpc_lb_policy_check_connectivity_locked( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - grpc_error **connectivity_error) { - return policy->vtable->check_connectivity_locked(exec_ctx, policy, - connectivity_error); -} - -void grpc_lb_policy_update_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *policy, - const grpc_lb_policy_args *lb_policy_args) { - policy->vtable->update_locked(exec_ctx, policy, lb_policy_args); -} diff --git a/src/core/ext/filters/client_channel/lb_policy.cc b/src/core/ext/filters/client_channel/lb_policy.cc new file mode 100644 index 0000000000..8e6673d737 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy.cc @@ -0,0 +1,164 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/lb_policy.h" +#include "src/core/lib/iomgr/combiner.h" + +#define WEAK_REF_BITS 16 + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_lb_policy_refcount = + GRPC_TRACER_INITIALIZER(false, "lb_policy_refcount"); +#endif + +void grpc_lb_policy_init(grpc_lb_policy *policy, + const grpc_lb_policy_vtable *vtable, + grpc_combiner *combiner) { + policy->vtable = vtable; + gpr_atm_no_barrier_store(&policy->ref_pair, 1 << WEAK_REF_BITS); + policy->interested_parties = grpc_pollset_set_create(); + policy->combiner = GRPC_COMBINER_REF(combiner, "lb_policy"); +} + +#ifndef NDEBUG +#define REF_FUNC_EXTRA_ARGS , const char *file, int line, const char *reason +#define REF_MUTATE_EXTRA_ARGS REF_FUNC_EXTRA_ARGS, const char *purpose +#define REF_FUNC_PASS_ARGS(new_reason) , file, line, new_reason +#define REF_MUTATE_PASS_ARGS(purpose) , file, line, reason, purpose +#else +#define REF_FUNC_EXTRA_ARGS +#define REF_MUTATE_EXTRA_ARGS +#define REF_FUNC_PASS_ARGS(new_reason) +#define REF_MUTATE_PASS_ARGS(x) +#endif + +static gpr_atm ref_mutate(grpc_lb_policy *c, gpr_atm delta, + int barrier REF_MUTATE_EXTRA_ARGS) { + gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) + : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_lb_policy_refcount)) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "LB_POLICY: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c, + purpose, old_val, old_val + delta, reason); + } +#endif + return old_val; +} + +void grpc_lb_policy_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + ref_mutate(policy, 1 << WEAK_REF_BITS, 0 REF_MUTATE_PASS_ARGS("STRONG_REF")); +} + +static void shutdown_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_lb_policy *policy = (grpc_lb_policy *)arg; + policy->vtable->shutdown_locked(exec_ctx, policy); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, policy, "strong-unref"); +} + +void grpc_lb_policy_unref(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + gpr_atm old_val = + ref_mutate(policy, (gpr_atm)1 - (gpr_atm)(1 << WEAK_REF_BITS), + 1 REF_MUTATE_PASS_ARGS("STRONG_UNREF")); + gpr_atm mask = ~(gpr_atm)((1 << WEAK_REF_BITS) - 1); + gpr_atm check = 1 << WEAK_REF_BITS; + if ((old_val & mask) == check) { + GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE( + shutdown_locked, policy, + grpc_combiner_scheduler(policy->combiner)), + GRPC_ERROR_NONE); + } else { + grpc_lb_policy_weak_unref(exec_ctx, + policy REF_FUNC_PASS_ARGS("strong-unref")); + } +} + +void grpc_lb_policy_weak_ref(grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + ref_mutate(policy, 1, 0 REF_MUTATE_PASS_ARGS("WEAK_REF")); +} + +void grpc_lb_policy_weak_unref(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy REF_FUNC_EXTRA_ARGS) { + gpr_atm old_val = + ref_mutate(policy, -(gpr_atm)1, 1 REF_MUTATE_PASS_ARGS("WEAK_UNREF")); + if (old_val == 1) { + grpc_pollset_set_destroy(exec_ctx, policy->interested_parties); + grpc_combiner *combiner = policy->combiner; + policy->vtable->destroy(exec_ctx, policy); + GRPC_COMBINER_UNREF(exec_ctx, combiner, "lb_policy"); + } +} + +int grpc_lb_policy_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + const grpc_lb_policy_pick_args *pick_args, + grpc_connected_subchannel **target, + grpc_call_context_element *context, + void **user_data, grpc_closure *on_complete) { + return policy->vtable->pick_locked(exec_ctx, policy, pick_args, target, + context, user_data, on_complete); +} + +void grpc_lb_policy_cancel_pick_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, + grpc_connected_subchannel **target, + grpc_error *error) { + policy->vtable->cancel_pick_locked(exec_ctx, policy, target, error); +} + +void grpc_lb_policy_cancel_picks_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, + uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error *error) { + policy->vtable->cancel_picks_locked(exec_ctx, policy, + initial_metadata_flags_mask, + initial_metadata_flags_eq, error); +} + +void grpc_lb_policy_exit_idle_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy) { + policy->vtable->exit_idle_locked(exec_ctx, policy); +} + +void grpc_lb_policy_ping_one_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, + grpc_closure *closure) { + policy->vtable->ping_one_locked(exec_ctx, policy, closure); +} + +void grpc_lb_policy_notify_on_state_change_locked( + grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_connectivity_state *state, grpc_closure *closure) { + policy->vtable->notify_on_state_change_locked(exec_ctx, policy, state, + closure); +} + +grpc_connectivity_state grpc_lb_policy_check_connectivity_locked( + grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + grpc_error **connectivity_error) { + return policy->vtable->check_connectivity_locked(exec_ctx, policy, + connectivity_error); +} + +void grpc_lb_policy_update_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *policy, + const grpc_lb_policy_args *lb_policy_args) { + policy->vtable->update_locked(exec_ctx, policy, lb_policy_args); +} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c deleted file mode 100644 index 7ad322902b..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" - -#include -#include - -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/profiling/timers.h" - -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - return GRPC_ERROR_NONE; -} - -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) {} - -typedef struct { - // Stats object to update. - grpc_grpclb_client_stats *client_stats; - // State for intercepting send_initial_metadata. - grpc_closure on_complete_for_send; - grpc_closure *original_on_complete_for_send; - bool send_initial_metadata_succeeded; - // State for intercepting recv_initial_metadata. - grpc_closure recv_initial_metadata_ready; - grpc_closure *original_recv_initial_metadata_ready; - bool recv_initial_metadata_succeeded; -} call_data; - -static void on_complete_for_send(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - call_data *calld = (call_data *)arg; - if (error == GRPC_ERROR_NONE) { - calld->send_initial_metadata_succeeded = true; - } - GRPC_CLOSURE_RUN(exec_ctx, calld->original_on_complete_for_send, - GRPC_ERROR_REF(error)); -} - -static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - call_data *calld = (call_data *)arg; - if (error == GRPC_ERROR_NONE) { - calld->recv_initial_metadata_succeeded = true; - } - GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_initial_metadata_ready, - GRPC_ERROR_REF(error)); -} - -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - // Get stats object from context and take a ref. - GPR_ASSERT(args->context != NULL); - GPR_ASSERT(args->context[GRPC_GRPCLB_CLIENT_STATS].value != NULL); - calld->client_stats = grpc_grpclb_client_stats_ref( - (grpc_grpclb_client_stats *)args->context[GRPC_GRPCLB_CLIENT_STATS] - .value); - // Record call started. - grpc_grpclb_client_stats_add_call_started(calld->client_stats); - return GRPC_ERROR_NONE; -} - -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *calld = (call_data *)elem->call_data; - // Record call finished, optionally setting client_failed_to_send and - // received. - grpc_grpclb_client_stats_add_call_finished( - !calld->send_initial_metadata_succeeded /* client_failed_to_send */, - calld->recv_initial_metadata_succeeded /* known_received */, - calld->client_stats); - // All done, so unref the stats object. - grpc_grpclb_client_stats_unref(calld->client_stats); -} - -static void start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - GPR_TIMER_BEGIN("clr_start_transport_stream_op_batch", 0); - // Intercept send_initial_metadata. - if (batch->send_initial_metadata) { - calld->original_on_complete_for_send = batch->on_complete; - GRPC_CLOSURE_INIT(&calld->on_complete_for_send, on_complete_for_send, calld, - grpc_schedule_on_exec_ctx); - batch->on_complete = &calld->on_complete_for_send; - } - // Intercept recv_initial_metadata. - if (batch->recv_initial_metadata) { - calld->original_recv_initial_metadata_ready = - batch->payload->recv_initial_metadata.recv_initial_metadata_ready; - GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, - recv_initial_metadata_ready, calld, - grpc_schedule_on_exec_ctx); - batch->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->recv_initial_metadata_ready; - } - // Chain to next filter. - grpc_call_next_op(exec_ctx, elem, batch); - GPR_TIMER_END("clr_start_transport_stream_op_batch", 0); -} - -const grpc_channel_filter grpc_client_load_reporting_filter = { - start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - 0, // sizeof(channel_data) - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "client_load_reporting"}; diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc new file mode 100644 index 0000000000..7ad322902b --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc @@ -0,0 +1,137 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" + +#include +#include + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/profiling/timers.h" + +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + return GRPC_ERROR_NONE; +} + +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) {} + +typedef struct { + // Stats object to update. + grpc_grpclb_client_stats *client_stats; + // State for intercepting send_initial_metadata. + grpc_closure on_complete_for_send; + grpc_closure *original_on_complete_for_send; + bool send_initial_metadata_succeeded; + // State for intercepting recv_initial_metadata. + grpc_closure recv_initial_metadata_ready; + grpc_closure *original_recv_initial_metadata_ready; + bool recv_initial_metadata_succeeded; +} call_data; + +static void on_complete_for_send(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + call_data *calld = (call_data *)arg; + if (error == GRPC_ERROR_NONE) { + calld->send_initial_metadata_succeeded = true; + } + GRPC_CLOSURE_RUN(exec_ctx, calld->original_on_complete_for_send, + GRPC_ERROR_REF(error)); +} + +static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + call_data *calld = (call_data *)arg; + if (error == GRPC_ERROR_NONE) { + calld->recv_initial_metadata_succeeded = true; + } + GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + // Get stats object from context and take a ref. + GPR_ASSERT(args->context != NULL); + GPR_ASSERT(args->context[GRPC_GRPCLB_CLIENT_STATS].value != NULL); + calld->client_stats = grpc_grpclb_client_stats_ref( + (grpc_grpclb_client_stats *)args->context[GRPC_GRPCLB_CLIENT_STATS] + .value); + // Record call started. + grpc_grpclb_client_stats_add_call_started(calld->client_stats); + return GRPC_ERROR_NONE; +} + +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *calld = (call_data *)elem->call_data; + // Record call finished, optionally setting client_failed_to_send and + // received. + grpc_grpclb_client_stats_add_call_finished( + !calld->send_initial_metadata_succeeded /* client_failed_to_send */, + calld->recv_initial_metadata_succeeded /* known_received */, + calld->client_stats); + // All done, so unref the stats object. + grpc_grpclb_client_stats_unref(calld->client_stats); +} + +static void start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + GPR_TIMER_BEGIN("clr_start_transport_stream_op_batch", 0); + // Intercept send_initial_metadata. + if (batch->send_initial_metadata) { + calld->original_on_complete_for_send = batch->on_complete; + GRPC_CLOSURE_INIT(&calld->on_complete_for_send, on_complete_for_send, calld, + grpc_schedule_on_exec_ctx); + batch->on_complete = &calld->on_complete_for_send; + } + // Intercept recv_initial_metadata. + if (batch->recv_initial_metadata) { + calld->original_recv_initial_metadata_ready = + batch->payload->recv_initial_metadata.recv_initial_metadata_ready; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, calld, + grpc_schedule_on_exec_ctx); + batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + // Chain to next filter. + grpc_call_next_op(exec_ctx, elem, batch); + GPR_TIMER_END("clr_start_transport_stream_op_batch", 0); +} + +const grpc_channel_filter grpc_client_load_reporting_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, // sizeof(channel_data) + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "client_load_reporting"}; diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c deleted file mode 100644 index 8dc81b46d1..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c +++ /dev/null @@ -1,2021 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** Implementation of the gRPC LB policy. - * - * This policy takes as input a set of resolved addresses {a1..an} for which the - * LB set was set (it's the resolver's responsibility to ensure this). That is - * to say, {a1..an} represent a collection of LB servers. - * - * An internal channel (\a glb_lb_policy.lb_channel) is created over {a1..an}. - * This channel behaves just like a regular channel. In particular, the - * constructed URI over the addresses a1..an will use the default pick first - * policy to select from this list of LB server backends. - * - * The first time the policy gets a request for a pick, a ping, or to exit the - * idle state, \a query_for_backends_locked() is called. This function sets up - * and initiates the internal communication with the LB server. In particular, - * it's responsible for instantiating the internal *streaming* call to the LB - * server (whichever address from {a1..an} pick-first chose). This call is - * serviced by two callbacks, \a lb_on_server_status_received and \a - * lb_on_response_received. The former will be called when the call to the LB - * server completes. This can happen if the LB server closes the connection or - * if this policy itself cancels the call (for example because it's shutting - * down). If the internal call times out, the usual behavior of pick-first - * applies, continuing to pick from the list {a1..an}. - * - * Upon sucesss, the incoming \a LoadBalancingResponse is processed by \a - * res_recv. An invalid one results in the termination of the streaming call. A - * new streaming call should be created if possible, failing the original call - * otherwise. For a valid \a LoadBalancingResponse, the server list of actual - * backends is extracted. A Round Robin policy will be created from this list. - * There are two possible scenarios: - * - * 1. This is the first server list received. There was no previous instance of - * the Round Robin policy. \a rr_handover_locked() will instantiate the RR - * policy and perform all the pending operations over it. - * 2. There's already a RR policy instance active. We need to introduce the new - * one build from the new serverlist, but taking care not to disrupt the - * operations in progress over the old RR instance. This is done by - * decreasing the reference count on the old policy. The moment no more - * references are held on the old RR policy, it'll be destroyed and \a - * glb_rr_connectivity_changed notified with a \a GRPC_CHANNEL_SHUTDOWN - * state. At this point we can transition to a new RR instance safely, which - * is done once again via \a rr_handover_locked(). - * - * - * Once a RR policy instance is in place (and getting updated as described), - * calls to for a pick, a ping or a cancellation will be serviced right away by - * forwarding them to the RR instance. Any time there's no RR policy available - * (ie, right after the creation of the gRPCLB policy, if an empty serverlist is - * received, etc), pick/ping requests are added to a list of pending picks/pings - * to be flushed and serviced as part of \a rr_handover_locked() the moment the - * RR policy instance becomes available. - * - * \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the - * high level design and details. */ - -/* TODO(dgq): - * - Implement LB service forwarding (point 2c. in the doc's diagram). - */ - -/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when - using that endpoint. Because of various transitive includes in uv.h, - including windows.h on Windows, uv.h must be included before other system - headers. Therefore, sockaddr.h must always be included first */ -#include "src/core/lib/iomgr/sockaddr.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/client_channel_factory.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" -#include "src/core/ext/filters/client_channel/lb_policy_factory.h" -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/parse_address.h" -#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" -#include "src/core/ext/filters/client_channel/subchannel_index.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/slice/slice_hash_table.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/backoff.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/static_metadata.h" - -#define GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS 20 -#define GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS 1 -#define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6 -#define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120 -#define GRPC_GRPCLB_RECONNECT_JITTER 0.2 -#define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000 - -grpc_tracer_flag grpc_lb_glb_trace = GRPC_TRACER_INITIALIZER(false, "glb"); - -/* add lb_token of selected subchannel (address) to the call's initial - * metadata */ -static grpc_error *initial_metadata_add_lb_token( - grpc_exec_ctx *exec_ctx, grpc_metadata_batch *initial_metadata, - grpc_linked_mdelem *lb_token_mdelem_storage, grpc_mdelem lb_token) { - GPR_ASSERT(lb_token_mdelem_storage != NULL); - GPR_ASSERT(!GRPC_MDISNULL(lb_token)); - return grpc_metadata_batch_add_tail(exec_ctx, initial_metadata, - lb_token_mdelem_storage, lb_token); -} - -static void destroy_client_stats(void *arg) { - grpc_grpclb_client_stats_unref((grpc_grpclb_client_stats *)arg); -} - -typedef struct wrapped_rr_closure_arg { - /* the closure instance using this struct as argument */ - grpc_closure wrapper_closure; - - /* the original closure. Usually a on_complete/notify cb for pick() and ping() - * calls against the internal RR instance, respectively. */ - grpc_closure *wrapped_closure; - - /* the pick's initial metadata, kept in order to append the LB token for the - * pick */ - grpc_metadata_batch *initial_metadata; - - /* the picked target, used to determine which LB token to add to the pick's - * initial metadata */ - grpc_connected_subchannel **target; - - /* the context to be populated for the subchannel call */ - grpc_call_context_element *context; - - /* Stats for client-side load reporting. Note that this holds a - * reference, which must be either passed on via context or unreffed. */ - grpc_grpclb_client_stats *client_stats; - - /* the LB token associated with the pick */ - grpc_mdelem lb_token; - - /* storage for the lb token initial metadata mdelem */ - grpc_linked_mdelem *lb_token_mdelem_storage; - - /* The RR instance related to the closure */ - grpc_lb_policy *rr_policy; - - /* heap memory to be freed upon closure execution. */ - void *free_when_done; -} wrapped_rr_closure_arg; - -/* The \a on_complete closure passed as part of the pick requires keeping a - * reference to its associated round robin instance. We wrap this closure in - * order to unref the round robin instance upon its invocation */ -static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - wrapped_rr_closure_arg *wc_arg = (wrapped_rr_closure_arg *)arg; - - GPR_ASSERT(wc_arg->wrapped_closure != NULL); - GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_REF(error)); - - if (wc_arg->rr_policy != NULL) { - /* if *target is NULL, no pick has been made by the RR policy (eg, all - * addresses failed to connect). There won't be any user_data/token - * available */ - if (*wc_arg->target != NULL) { - if (!GRPC_MDISNULL(wc_arg->lb_token)) { - initial_metadata_add_lb_token(exec_ctx, wc_arg->initial_metadata, - wc_arg->lb_token_mdelem_storage, - GRPC_MDELEM_REF(wc_arg->lb_token)); - } else { - gpr_log(GPR_ERROR, - "No LB token for connected subchannel pick %p (from RR " - "instance %p).", - (void *)*wc_arg->target, (void *)wc_arg->rr_policy); - abort(); - } - // Pass on client stats via context. Passes ownership of the reference. - GPR_ASSERT(wc_arg->client_stats != NULL); - wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats; - wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats; - } else { - grpc_grpclb_client_stats_unref(wc_arg->client_stats); - } - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Unreffing RR %p", (void *)wc_arg->rr_policy); - } - GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "wrapped_rr_closure"); - } - GPR_ASSERT(wc_arg->free_when_done != NULL); - gpr_free(wc_arg->free_when_done); -} - -/* Linked list of pending pick requests. It stores all information needed to - * eventually call (Round Robin's) pick() on them. They mainly stay pending - * waiting for the RR policy to be created/updated. - * - * One particularity is the wrapping of the user-provided \a on_complete closure - * (in \a wrapped_on_complete and \a wrapped_on_complete_arg). This is needed in - * order to correctly unref the RR policy instance upon completion of the pick. - * See \a wrapped_rr_closure for details. */ -typedef struct pending_pick { - struct pending_pick *next; - - /* original pick()'s arguments */ - grpc_lb_policy_pick_args pick_args; - - /* output argument where to store the pick()ed connected subchannel, or NULL - * upon error. */ - grpc_connected_subchannel **target; - - /* args for wrapped_on_complete */ - wrapped_rr_closure_arg wrapped_on_complete_arg; -} pending_pick; - -static void add_pending_pick(pending_pick **root, - const grpc_lb_policy_pick_args *pick_args, - grpc_connected_subchannel **target, - grpc_call_context_element *context, - grpc_closure *on_complete) { - pending_pick *pp = (pending_pick *)gpr_zalloc(sizeof(*pp)); - pp->next = *root; - pp->pick_args = *pick_args; - pp->target = target; - pp->wrapped_on_complete_arg.wrapped_closure = on_complete; - pp->wrapped_on_complete_arg.target = target; - pp->wrapped_on_complete_arg.context = context; - pp->wrapped_on_complete_arg.initial_metadata = pick_args->initial_metadata; - pp->wrapped_on_complete_arg.lb_token_mdelem_storage = - pick_args->lb_token_mdelem_storage; - pp->wrapped_on_complete_arg.free_when_done = pp; - GRPC_CLOSURE_INIT(&pp->wrapped_on_complete_arg.wrapper_closure, - wrapped_rr_closure, &pp->wrapped_on_complete_arg, - grpc_schedule_on_exec_ctx); - *root = pp; -} - -/* Same as the \a pending_pick struct but for ping operations */ -typedef struct pending_ping { - struct pending_ping *next; - - /* args for wrapped_notify */ - wrapped_rr_closure_arg wrapped_notify_arg; -} pending_ping; - -static void add_pending_ping(pending_ping **root, grpc_closure *notify) { - pending_ping *pping = (pending_ping *)gpr_zalloc(sizeof(*pping)); - pping->wrapped_notify_arg.wrapped_closure = notify; - pping->wrapped_notify_arg.free_when_done = pping; - pping->next = *root; - GRPC_CLOSURE_INIT(&pping->wrapped_notify_arg.wrapper_closure, - wrapped_rr_closure, &pping->wrapped_notify_arg, - grpc_schedule_on_exec_ctx); - *root = pping; -} - -/* - * glb_lb_policy - */ -typedef struct rr_connectivity_data rr_connectivity_data; - -typedef struct glb_lb_policy { - /** base policy: must be first */ - grpc_lb_policy base; - - /** who the client is trying to communicate with */ - const char *server_name; - grpc_client_channel_factory *cc_factory; - grpc_channel_args *args; - - /** timeout in milliseconds for the LB call. 0 means no deadline. */ - int lb_call_timeout_ms; - - /** timeout in milliseconds for before using fallback backend addresses. - * 0 means not using fallback. */ - int lb_fallback_timeout_ms; - - /** for communicating with the LB server */ - grpc_channel *lb_channel; - - /** response generator to inject address updates into \a lb_channel */ - grpc_fake_resolver_response_generator *response_generator; - - /** the RR policy to use of the backend servers returned by the LB server */ - grpc_lb_policy *rr_policy; - - bool started_picking; - - /** our connectivity state tracker */ - grpc_connectivity_state_tracker state_tracker; - - /** connectivity state of the LB channel */ - grpc_connectivity_state lb_channel_connectivity; - - /** stores the deserialized response from the LB. May be NULL until one such - * response has arrived. */ - grpc_grpclb_serverlist *serverlist; - - /** Index into serverlist for next pick. - * If the server at this index is a drop, we return a drop. - * Otherwise, we delegate to the RR policy. */ - size_t serverlist_index; - - /** stores the backend addresses from the resolver */ - grpc_lb_addresses *fallback_backend_addresses; - - /** list of picks that are waiting on RR's policy connectivity */ - pending_pick *pending_picks; - - /** list of pings that are waiting on RR's policy connectivity */ - pending_ping *pending_pings; - - bool shutting_down; - - /** are we currently updating lb_call? */ - bool updating_lb_call; - - /** are we currently updating lb_channel? */ - bool updating_lb_channel; - - /** are we already watching the LB channel's connectivity? */ - bool watching_lb_channel; - - /** is \a lb_call_retry_timer active? */ - bool retry_timer_active; - - /** is \a lb_fallback_timer active? */ - bool fallback_timer_active; - - /** called upon changes to the LB channel's connectivity. */ - grpc_closure lb_channel_on_connectivity_changed; - - /** args from the latest update received while already updating, or NULL */ - grpc_lb_policy_args *pending_update_args; - - /************************************************************/ - /* client data associated with the LB server communication */ - /************************************************************/ - /* Status from the LB server has been received. This signals the end of the LB - * call. */ - grpc_closure lb_on_server_status_received; - - /* A response from the LB server has been received. Process it */ - grpc_closure lb_on_response_received; - - /* LB call retry timer callback. */ - grpc_closure lb_on_call_retry; - - /* LB fallback timer callback. */ - grpc_closure lb_on_fallback; - - grpc_call *lb_call; /* streaming call to the LB server, */ - - grpc_metadata_array lb_initial_metadata_recv; /* initial MD from LB server */ - grpc_metadata_array - lb_trailing_metadata_recv; /* trailing MD from LB server */ - - /* what's being sent to the LB server. Note that its value may vary if the LB - * server indicates a redirect. */ - grpc_byte_buffer *lb_request_payload; - - /* response the LB server, if any. Processed in lb_on_response_received() */ - grpc_byte_buffer *lb_response_payload; - - /* call status code and details, set in lb_on_server_status_received() */ - grpc_status_code lb_call_status; - grpc_slice lb_call_status_details; - - /** LB call retry backoff state */ - gpr_backoff lb_call_backoff_state; - - /** LB call retry timer */ - grpc_timer lb_call_retry_timer; - - /** LB fallback timer */ - grpc_timer lb_fallback_timer; - - bool seen_initial_response; - - /* Stats for client-side load reporting. Should be unreffed and - * recreated whenever lb_call is replaced. */ - grpc_grpclb_client_stats *client_stats; - /* Interval and timer for next client load report. */ - gpr_timespec client_stats_report_interval; - grpc_timer client_load_report_timer; - bool client_load_report_timer_pending; - bool last_client_load_report_counters_were_zero; - /* Closure used for either the load report timer or the callback for - * completion of sending the load report. */ - grpc_closure client_load_report_closure; - /* Client load report message payload. */ - grpc_byte_buffer *client_load_report_payload; -} glb_lb_policy; - -/* Keeps track and reacts to changes in connectivity of the RR instance */ -struct rr_connectivity_data { - grpc_closure on_change; - grpc_connectivity_state state; - glb_lb_policy *glb_policy; -}; - -static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, - bool log) { - if (server->drop) return false; - const grpc_grpclb_ip_address *ip = &server->ip_address; - if (server->port >> 16 != 0) { - if (log) { - gpr_log(GPR_ERROR, - "Invalid port '%d' at index %lu of serverlist. Ignoring.", - server->port, (unsigned long)idx); - } - return false; - } - if (ip->size != 4 && ip->size != 16) { - if (log) { - gpr_log(GPR_ERROR, - "Expected IP to be 4 or 16 bytes, got %d at index %lu of " - "serverlist. Ignoring", - ip->size, (unsigned long)idx); - } - return false; - } - return true; -} - -/* vtable for LB tokens in grpc_lb_addresses. */ -static void *lb_token_copy(void *token) { - return token == NULL - ? NULL - : (void *)GRPC_MDELEM_REF((grpc_mdelem){(uintptr_t)token}).payload; -} -static void lb_token_destroy(grpc_exec_ctx *exec_ctx, void *token) { - if (token != NULL) { - GRPC_MDELEM_UNREF(exec_ctx, (grpc_mdelem){(uintptr_t)token}); - } -} -static int lb_token_cmp(void *token1, void *token2) { - if (token1 > token2) return 1; - if (token1 < token2) return -1; - return 0; -} -static const grpc_lb_user_data_vtable lb_token_vtable = { - lb_token_copy, lb_token_destroy, lb_token_cmp}; - -static void parse_server(const grpc_grpclb_server *server, - grpc_resolved_address *addr) { - memset(addr, 0, sizeof(*addr)); - if (server->drop) return; - const uint16_t netorder_port = htons((uint16_t)server->port); - /* the addresses are given in binary format (a in(6)_addr struct) in - * server->ip_address.bytes. */ - const grpc_grpclb_ip_address *ip = &server->ip_address; - if (ip->size == 4) { - addr->len = sizeof(struct sockaddr_in); - struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr->addr; - addr4->sin_family = AF_INET; - memcpy(&addr4->sin_addr, ip->bytes, ip->size); - addr4->sin_port = netorder_port; - } else if (ip->size == 16) { - addr->len = sizeof(struct sockaddr_in6); - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr->addr; - addr6->sin6_family = AF_INET6; - memcpy(&addr6->sin6_addr, ip->bytes, ip->size); - addr6->sin6_port = netorder_port; - } -} - -/* Returns addresses extracted from \a serverlist. */ -static grpc_lb_addresses *process_serverlist_locked( - grpc_exec_ctx *exec_ctx, const grpc_grpclb_serverlist *serverlist) { - size_t num_valid = 0; - /* first pass: count how many are valid in order to allocate the necessary - * memory in a single block */ - for (size_t i = 0; i < serverlist->num_servers; ++i) { - if (is_server_valid(serverlist->servers[i], i, true)) ++num_valid; - } - grpc_lb_addresses *lb_addresses = - grpc_lb_addresses_create(num_valid, &lb_token_vtable); - /* second pass: actually populate the addresses and LB tokens (aka user data - * to the outside world) to be read by the RR policy during its creation. - * Given that the validity tests are very cheap, they are performed again - * instead of marking the valid ones during the first pass, as this would - * incurr in an allocation due to the arbitrary number of server */ - size_t addr_idx = 0; - for (size_t sl_idx = 0; sl_idx < serverlist->num_servers; ++sl_idx) { - const grpc_grpclb_server *server = serverlist->servers[sl_idx]; - if (!is_server_valid(serverlist->servers[sl_idx], sl_idx, false)) continue; - GPR_ASSERT(addr_idx < num_valid); - /* address processing */ - grpc_resolved_address addr; - parse_server(server, &addr); - /* lb token processing */ - void *user_data; - if (server->has_load_balance_token) { - const size_t lb_token_max_length = - GPR_ARRAY_SIZE(server->load_balance_token); - const size_t lb_token_length = - strnlen(server->load_balance_token, lb_token_max_length); - grpc_slice lb_token_mdstr = grpc_slice_from_copied_buffer( - server->load_balance_token, lb_token_length); - user_data = (void *)grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_LB_TOKEN, - lb_token_mdstr) - .payload; - } else { - char *uri = grpc_sockaddr_to_uri(&addr); - gpr_log(GPR_INFO, - "Missing LB token for backend address '%s'. The empty token will " - "be used instead", - uri); - gpr_free(uri); - user_data = (void *)GRPC_MDELEM_LB_TOKEN_EMPTY.payload; - } - - grpc_lb_addresses_set_address(lb_addresses, addr_idx, &addr.addr, addr.len, - false /* is_balancer */, - NULL /* balancer_name */, user_data); - ++addr_idx; - } - GPR_ASSERT(addr_idx == num_valid); - return lb_addresses; -} - -/* Returns the backend addresses extracted from the given addresses */ -static grpc_lb_addresses *extract_backend_addresses_locked( - grpc_exec_ctx *exec_ctx, const grpc_lb_addresses *addresses) { - /* first pass: count the number of backend addresses */ - size_t num_backends = 0; - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (!addresses->addresses[i].is_balancer) { - ++num_backends; - } - } - /* second pass: actually populate the addresses and (empty) LB tokens */ - grpc_lb_addresses *backend_addresses = - grpc_lb_addresses_create(num_backends, &lb_token_vtable); - size_t num_copied = 0; - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (addresses->addresses[i].is_balancer) continue; - const grpc_resolved_address *addr = &addresses->addresses[i].address; - grpc_lb_addresses_set_address(backend_addresses, num_copied, &addr->addr, - addr->len, false /* is_balancer */, - NULL /* balancer_name */, - (void *)GRPC_MDELEM_LB_TOKEN_EMPTY.payload); - ++num_copied; - } - return backend_addresses; -} - -static void update_lb_connectivity_status_locked( - grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, - grpc_connectivity_state rr_state, grpc_error *rr_state_error) { - const grpc_connectivity_state curr_glb_state = - grpc_connectivity_state_check(&glb_policy->state_tracker); - - /* The new connectivity status is a function of the previous one and the new - * input coming from the status of the RR policy. - * - * current state (grpclb's) - * | - * v || I | C | R | TF | SD | <- new state (RR's) - * ===++====+=====+=====+======+======+ - * I || I | C | R | [I] | [I] | - * ---++----+-----+-----+------+------+ - * C || I | C | R | [C] | [C] | - * ---++----+-----+-----+------+------+ - * R || I | C | R | [R] | [R] | - * ---++----+-----+-----+------+------+ - * TF || I | C | R | [TF] | [TF] | - * ---++----+-----+-----+------+------+ - * SD || NA | NA | NA | NA | NA | (*) - * ---++----+-----+-----+------+------+ - * - * A [STATE] indicates that the old RR policy is kept. In those cases, STATE - * is the current state of grpclb, which is left untouched. - * - * In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to - * the previous RR instance. - * - * Note that the status is never updated to SHUTDOWN as a result of calling - * this function. Only glb_shutdown() has the power to set that state. - * - * (*) This function mustn't be called during shutting down. */ - GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN); - - switch (rr_state) { - case GRPC_CHANNEL_TRANSIENT_FAILURE: - case GRPC_CHANNEL_SHUTDOWN: - GPR_ASSERT(rr_state_error != GRPC_ERROR_NONE); - break; - case GRPC_CHANNEL_INIT: - case GRPC_CHANNEL_IDLE: - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_READY: - GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE); - } - - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log( - GPR_INFO, "Setting grpclb's state to %s from new RR policy %p state.", - grpc_connectivity_state_name(rr_state), (void *)glb_policy->rr_policy); - } - grpc_connectivity_state_set(exec_ctx, &glb_policy->state_tracker, rr_state, - rr_state_error, - "update_lb_connectivity_status_locked"); -} - -/* Perform a pick over \a glb_policy->rr_policy. Given that a pick can return - * immediately (ignoring its completion callback), we need to perform the - * cleanups this callback would otherwise be resposible for. - * If \a force_async is true, then we will manually schedule the - * completion callback even if the pick is available immediately. */ -static bool pick_from_internal_rr_locked( - grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, - const grpc_lb_policy_pick_args *pick_args, bool force_async, - grpc_connected_subchannel **target, wrapped_rr_closure_arg *wc_arg) { - // Check for drops if we are not using fallback backend addresses. - if (glb_policy->serverlist != NULL) { - // Look at the index into the serverlist to see if we should drop this call. - grpc_grpclb_server *server = - glb_policy->serverlist->servers[glb_policy->serverlist_index++]; - if (glb_policy->serverlist_index == glb_policy->serverlist->num_servers) { - glb_policy->serverlist_index = 0; // Wrap-around. - } - if (server->drop) { - // Not using the RR policy, so unref it. - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Unreffing RR for drop (0x%" PRIxPTR ")", - (intptr_t)wc_arg->rr_policy); - } - GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync"); - // Update client load reporting stats to indicate the number of - // dropped calls. Note that we have to do this here instead of in - // the client_load_reporting filter, because we do not create a - // subchannel call (and therefore no client_load_reporting filter) - // for dropped calls. - grpc_grpclb_client_stats_add_call_dropped_locked( - server->load_balance_token, wc_arg->client_stats); - grpc_grpclb_client_stats_unref(wc_arg->client_stats); - if (force_async) { - GPR_ASSERT(wc_arg->wrapped_closure != NULL); - GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE); - gpr_free(wc_arg->free_when_done); - return false; - } - gpr_free(wc_arg->free_when_done); - return true; - } - } - // Pick via the RR policy. - const bool pick_done = grpc_lb_policy_pick_locked( - exec_ctx, wc_arg->rr_policy, pick_args, target, wc_arg->context, - (void **)&wc_arg->lb_token, &wc_arg->wrapper_closure); - if (pick_done) { - /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */ - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Unreffing RR (0x%" PRIxPTR ")", - (intptr_t)wc_arg->rr_policy); - } - GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync"); - /* add the load reporting initial metadata */ - initial_metadata_add_lb_token(exec_ctx, pick_args->initial_metadata, - pick_args->lb_token_mdelem_storage, - GRPC_MDELEM_REF(wc_arg->lb_token)); - // Pass on client stats via context. Passes ownership of the reference. - GPR_ASSERT(wc_arg->client_stats != NULL); - wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats; - wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats; - if (force_async) { - GPR_ASSERT(wc_arg->wrapped_closure != NULL); - GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE); - gpr_free(wc_arg->free_when_done); - return false; - } - gpr_free(wc_arg->free_when_done); - } - /* else, the pending pick will be registered and taken care of by the - * pending pick list inside the RR policy (glb_policy->rr_policy). - * Eventually, wrapped_on_complete will be called, which will -among other - * things- add the LB token to the call's initial metadata */ - return pick_done; -} - -static grpc_lb_policy_args *lb_policy_args_create(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - grpc_lb_addresses *addresses; - if (glb_policy->serverlist != NULL) { - GPR_ASSERT(glb_policy->serverlist->num_servers > 0); - addresses = process_serverlist_locked(exec_ctx, glb_policy->serverlist); - } else { - // If rr_handover_locked() is invoked when we haven't received any - // serverlist from the balancer, we use the fallback backends returned by - // the resolver. Note that the fallback backend list may be empty, in which - // case the new round_robin policy will keep the requested picks pending. - GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL); - addresses = grpc_lb_addresses_copy(glb_policy->fallback_backend_addresses); - } - GPR_ASSERT(addresses != NULL); - grpc_lb_policy_args *args = (grpc_lb_policy_args *)gpr_zalloc(sizeof(*args)); - args->client_channel_factory = glb_policy->cc_factory; - args->combiner = glb_policy->base.combiner; - // Replace the LB addresses in the channel args that we pass down to - // the subchannel. - static const char *keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; - const grpc_arg arg = grpc_lb_addresses_create_channel_arg(addresses); - args->args = grpc_channel_args_copy_and_add_and_remove( - glb_policy->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg, - 1); - grpc_lb_addresses_destroy(exec_ctx, addresses); - return args; -} - -static void lb_policy_args_destroy(grpc_exec_ctx *exec_ctx, - grpc_lb_policy_args *args) { - grpc_channel_args_destroy(exec_ctx, args->args); - gpr_free(args); -} - -static void glb_rr_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error); -static void create_rr_locked(grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, - grpc_lb_policy_args *args) { - GPR_ASSERT(glb_policy->rr_policy == NULL); - - grpc_lb_policy *new_rr_policy = - grpc_lb_policy_create(exec_ctx, "round_robin", args); - if (new_rr_policy == NULL) { - gpr_log(GPR_ERROR, - "Failure creating a RoundRobin policy for serverlist update with " - "%lu entries. The previous RR instance (%p), if any, will continue " - "to be used. Future updates from the LB will attempt to create new " - "instances.", - (unsigned long)glb_policy->serverlist->num_servers, - (void *)glb_policy->rr_policy); - return; - } - glb_policy->rr_policy = new_rr_policy; - grpc_error *rr_state_error = NULL; - const grpc_connectivity_state rr_state = - grpc_lb_policy_check_connectivity_locked(exec_ctx, glb_policy->rr_policy, - &rr_state_error); - /* Connectivity state is a function of the RR policy updated/created */ - update_lb_connectivity_status_locked(exec_ctx, glb_policy, rr_state, - rr_state_error); - /* Add the gRPC LB's interested_parties pollset_set to that of the newly - * created RR policy. This will make the RR policy progress upon activity on - * gRPC LB, which in turn is tied to the application's call */ - grpc_pollset_set_add_pollset_set(exec_ctx, - glb_policy->rr_policy->interested_parties, - glb_policy->base.interested_parties); - - /* Allocate the data for the tracking of the new RR policy's connectivity. - * It'll be deallocated in glb_rr_connectivity_changed() */ - rr_connectivity_data *rr_connectivity = - (rr_connectivity_data *)gpr_zalloc(sizeof(rr_connectivity_data)); - GRPC_CLOSURE_INIT(&rr_connectivity->on_change, - glb_rr_connectivity_changed_locked, rr_connectivity, - grpc_combiner_scheduler(glb_policy->base.combiner)); - rr_connectivity->glb_policy = glb_policy; - rr_connectivity->state = rr_state; - - /* Subscribe to changes to the connectivity of the new RR */ - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "glb_rr_connectivity_cb"); - grpc_lb_policy_notify_on_state_change_locked(exec_ctx, glb_policy->rr_policy, - &rr_connectivity->state, - &rr_connectivity->on_change); - grpc_lb_policy_exit_idle_locked(exec_ctx, glb_policy->rr_policy); - - /* Update picks and pings in wait */ - pending_pick *pp; - while ((pp = glb_policy->pending_picks)) { - glb_policy->pending_picks = pp->next; - GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_pick"); - pp->wrapped_on_complete_arg.rr_policy = glb_policy->rr_policy; - pp->wrapped_on_complete_arg.client_stats = - grpc_grpclb_client_stats_ref(glb_policy->client_stats); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Pending pick about to (async) PICK from %p", - (void *)glb_policy->rr_policy); - } - pick_from_internal_rr_locked(exec_ctx, glb_policy, &pp->pick_args, - true /* force_async */, pp->target, - &pp->wrapped_on_complete_arg); - } - - pending_ping *pping; - while ((pping = glb_policy->pending_pings)) { - glb_policy->pending_pings = pping->next; - GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_ping"); - pping->wrapped_notify_arg.rr_policy = glb_policy->rr_policy; - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Pending ping about to PING from 0x%" PRIxPTR "", - (intptr_t)glb_policy->rr_policy); - } - grpc_lb_policy_ping_one_locked(exec_ctx, glb_policy->rr_policy, - &pping->wrapped_notify_arg.wrapper_closure); - } -} - -/* glb_policy->rr_policy may be NULL (initial handover) */ -static void rr_handover_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - if (glb_policy->shutting_down) return; - grpc_lb_policy_args *args = lb_policy_args_create(exec_ctx, glb_policy); - GPR_ASSERT(args != NULL); - if (glb_policy->rr_policy != NULL) { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_DEBUG, "Updating Round Robin policy (%p)", - (void *)glb_policy->rr_policy); - } - grpc_lb_policy_update_locked(exec_ctx, glb_policy->rr_policy, args); - } else { - create_rr_locked(exec_ctx, glb_policy, args); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_DEBUG, "Created new Round Robin policy (%p)", - (void *)glb_policy->rr_policy); - } - } - lb_policy_args_destroy(exec_ctx, args); -} - -static void glb_rr_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error) { - rr_connectivity_data *rr_connectivity = (rr_connectivity_data *)arg; - glb_lb_policy *glb_policy = rr_connectivity->glb_policy; - if (glb_policy->shutting_down) { - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "glb_rr_connectivity_cb"); - gpr_free(rr_connectivity); - return; - } - if (rr_connectivity->state == GRPC_CHANNEL_SHUTDOWN) { - /* An RR policy that has transitioned into the SHUTDOWN connectivity state - * should not be considered for picks or updates: the SHUTDOWN state is a - * sink, policies can't transition back from it. .*/ - GRPC_LB_POLICY_UNREF(exec_ctx, glb_policy->rr_policy, - "rr_connectivity_shutdown"); - glb_policy->rr_policy = NULL; - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "glb_rr_connectivity_cb"); - gpr_free(rr_connectivity); - return; - } - /* rr state != SHUTDOWN && !glb_policy->shutting down: biz as usual */ - update_lb_connectivity_status_locked( - exec_ctx, glb_policy, rr_connectivity->state, GRPC_ERROR_REF(error)); - /* Resubscribe. Reuse the "glb_rr_connectivity_cb" weak ref. */ - grpc_lb_policy_notify_on_state_change_locked(exec_ctx, glb_policy->rr_policy, - &rr_connectivity->state, - &rr_connectivity->on_change); -} - -static void destroy_balancer_name(grpc_exec_ctx *exec_ctx, - void *balancer_name) { - gpr_free(balancer_name); -} - -static grpc_slice_hash_table_entry targets_info_entry_create( - const char *address, const char *balancer_name) { - grpc_slice_hash_table_entry entry; - entry.key = grpc_slice_from_copied_string(address); - entry.value = gpr_strdup(balancer_name); - return entry; -} - -static int balancer_name_cmp_fn(void *a, void *b) { - const char *a_str = (const char *)a; - const char *b_str = (const char *)b; - return strcmp(a_str, b_str); -} - -/* Returns the channel args for the LB channel, used to create a bidirectional - * stream for the reception of load balancing updates. - * - * Inputs: - * - \a addresses: corresponding to the balancers. - * - \a response_generator: in order to propagate updates from the resolver - * above the grpclb policy. - * - \a args: other args inherited from the grpclb policy. */ -static grpc_channel_args *build_lb_channel_args( - grpc_exec_ctx *exec_ctx, const grpc_lb_addresses *addresses, - grpc_fake_resolver_response_generator *response_generator, - const grpc_channel_args *args) { - size_t num_grpclb_addrs = 0; - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; - } - /* All input addresses come from a resolver that claims they are LB services. - * It's the resolver's responsibility to make sure this policy is only - * instantiated and used in that case. Otherwise, something has gone wrong. */ - GPR_ASSERT(num_grpclb_addrs > 0); - grpc_lb_addresses *lb_addresses = - grpc_lb_addresses_create(num_grpclb_addrs, NULL); - grpc_slice_hash_table_entry *targets_info_entries = - (grpc_slice_hash_table_entry *)gpr_zalloc(sizeof(*targets_info_entries) * - num_grpclb_addrs); - - size_t lb_addresses_idx = 0; - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (!addresses->addresses[i].is_balancer) continue; - if (addresses->addresses[i].user_data != NULL) { - gpr_log(GPR_ERROR, - "This LB policy doesn't support user data. It will be ignored"); - } - char *addr_str; - GPR_ASSERT(grpc_sockaddr_to_string( - &addr_str, &addresses->addresses[i].address, true) > 0); - targets_info_entries[lb_addresses_idx] = targets_info_entry_create( - addr_str, addresses->addresses[i].balancer_name); - gpr_free(addr_str); - - grpc_lb_addresses_set_address( - lb_addresses, lb_addresses_idx++, addresses->addresses[i].address.addr, - addresses->addresses[i].address.len, false /* is balancer */, - addresses->addresses[i].balancer_name, NULL /* user data */); - } - GPR_ASSERT(num_grpclb_addrs == lb_addresses_idx); - grpc_slice_hash_table *targets_info = - grpc_slice_hash_table_create(num_grpclb_addrs, targets_info_entries, - destroy_balancer_name, balancer_name_cmp_fn); - gpr_free(targets_info_entries); - - grpc_channel_args *lb_channel_args = - grpc_lb_policy_grpclb_build_lb_channel_args(exec_ctx, targets_info, - response_generator, args); - - grpc_arg lb_channel_addresses_arg = - grpc_lb_addresses_create_channel_arg(lb_addresses); - - grpc_channel_args *result = grpc_channel_args_copy_and_add( - lb_channel_args, &lb_channel_addresses_arg, 1); - grpc_slice_hash_table_unref(exec_ctx, targets_info); - grpc_channel_args_destroy(exec_ctx, lb_channel_args); - grpc_lb_addresses_destroy(exec_ctx, lb_addresses); - return result; -} - -static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - GPR_ASSERT(glb_policy->pending_picks == NULL); - GPR_ASSERT(glb_policy->pending_pings == NULL); - gpr_free((void *)glb_policy->server_name); - grpc_channel_args_destroy(exec_ctx, glb_policy->args); - if (glb_policy->client_stats != NULL) { - grpc_grpclb_client_stats_unref(glb_policy->client_stats); - } - grpc_connectivity_state_destroy(exec_ctx, &glb_policy->state_tracker); - if (glb_policy->serverlist != NULL) { - grpc_grpclb_destroy_serverlist(glb_policy->serverlist); - } - if (glb_policy->fallback_backend_addresses != NULL) { - grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses); - } - grpc_fake_resolver_response_generator_unref(glb_policy->response_generator); - grpc_subchannel_index_unref(); - if (glb_policy->pending_update_args != NULL) { - grpc_channel_args_destroy(exec_ctx, glb_policy->pending_update_args->args); - gpr_free(glb_policy->pending_update_args); - } - gpr_free(glb_policy); -} - -static void glb_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - glb_policy->shutting_down = true; - - /* We need a copy of the lb_call pointer because we can't cancell the call - * while holding glb_policy->mu: lb_on_server_status_received, invoked due to - * the cancel, needs to acquire that same lock */ - grpc_call *lb_call = glb_policy->lb_call; - - /* glb_policy->lb_call and this local lb_call must be consistent at this point - * because glb_policy->lb_call is only assigned in lb_call_init_locked as part - * of query_for_backends_locked, which can only be invoked while - * glb_policy->shutting_down is false. */ - if (lb_call != NULL) { - grpc_call_cancel(lb_call, NULL); - /* lb_on_server_status_received will pick up the cancel and clean up */ - } - if (glb_policy->retry_timer_active) { - grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer); - glb_policy->retry_timer_active = false; - } - - pending_pick *pp = glb_policy->pending_picks; - glb_policy->pending_picks = NULL; - pending_ping *pping = glb_policy->pending_pings; - glb_policy->pending_pings = NULL; - if (glb_policy->rr_policy != NULL) { - GRPC_LB_POLICY_UNREF(exec_ctx, glb_policy->rr_policy, "glb_shutdown"); - } - // We destroy the LB channel here because - // glb_lb_channel_on_connectivity_changed_cb needs a valid glb_policy - // instance. Destroying the lb channel in glb_destroy would likely result in - // a callback invocation without a valid glb_policy arg. - if (glb_policy->lb_channel != NULL) { - grpc_channel_destroy(glb_policy->lb_channel); - glb_policy->lb_channel = NULL; - } - grpc_connectivity_state_set( - exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Shutdown"), "glb_shutdown"); - - while (pp != NULL) { - pending_pick *next = pp->next; - *pp->target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure, - GRPC_ERROR_NONE); - pp = next; - } - - while (pping != NULL) { - pending_ping *next = pping->next; - GRPC_CLOSURE_SCHED(exec_ctx, &pping->wrapped_notify_arg.wrapper_closure, - GRPC_ERROR_NONE); - pping = next; - } -} - -// Cancel a specific pending pick. -// -// A grpclb pick progresses as follows: -// - If there's a Round Robin policy (glb_policy->rr_policy) available, it'll be -// handed over to the RR policy (in create_rr_locked()). From that point -// onwards, it'll be RR's responsibility. For cancellations, that implies the -// pick needs also be cancelled by the RR instance. -// - Otherwise, without an RR instance, picks stay pending at this policy's -// level (grpclb), inside the glb_policy->pending_picks list. To cancel these, -// we invoke the completion closure and set *target to NULL right here. -static void glb_cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_connected_subchannel **target, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - pending_pick *pp = glb_policy->pending_picks; - glb_policy->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if (pp->target == target) { - *target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick Cancelled", &error, 1)); - } else { - pp->next = glb_policy->pending_picks; - glb_policy->pending_picks = pp; - } - pp = next; - } - if (glb_policy->rr_policy != NULL) { - grpc_lb_policy_cancel_pick_locked(exec_ctx, glb_policy->rr_policy, target, - GRPC_ERROR_REF(error)); - } - GRPC_ERROR_UNREF(error); -} - -// Cancel all pending picks. -// -// A grpclb pick progresses as follows: -// - If there's a Round Robin policy (glb_policy->rr_policy) available, it'll be -// handed over to the RR policy (in create_rr_locked()). From that point -// onwards, it'll be RR's responsibility. For cancellations, that implies the -// pick needs also be cancelled by the RR instance. -// - Otherwise, without an RR instance, picks stay pending at this policy's -// level (grpclb), inside the glb_policy->pending_picks list. To cancel these, -// we invoke the completion closure and set *target to NULL right here. -static void glb_cancel_picks_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *pol, - uint32_t initial_metadata_flags_mask, - uint32_t initial_metadata_flags_eq, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - pending_pick *pp = glb_policy->pending_picks; - glb_policy->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if ((pp->pick_args.initial_metadata_flags & initial_metadata_flags_mask) == - initial_metadata_flags_eq) { - GRPC_CLOSURE_SCHED(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick Cancelled", &error, 1)); - } else { - pp->next = glb_policy->pending_picks; - glb_policy->pending_picks = pp; - } - pp = next; - } - if (glb_policy->rr_policy != NULL) { - grpc_lb_policy_cancel_picks_locked( - exec_ctx, glb_policy->rr_policy, initial_metadata_flags_mask, - initial_metadata_flags_eq, GRPC_ERROR_REF(error)); - } - GRPC_ERROR_UNREF(error); -} - -static void lb_on_fallback_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); -static void query_for_backends_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy); -static void start_picking_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - /* start a timer to fall back */ - if (glb_policy->lb_fallback_timeout_ms > 0 && - glb_policy->serverlist == NULL && !glb_policy->fallback_timer_active) { - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec deadline = gpr_time_add( - now, - gpr_time_from_millis(glb_policy->lb_fallback_timeout_ms, GPR_TIMESPAN)); - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_fallback_timer"); - GRPC_CLOSURE_INIT(&glb_policy->lb_on_fallback, lb_on_fallback_timer_locked, - glb_policy, - grpc_combiner_scheduler(glb_policy->base.combiner)); - glb_policy->fallback_timer_active = true; - grpc_timer_init(exec_ctx, &glb_policy->lb_fallback_timer, deadline, - &glb_policy->lb_on_fallback, now); - } - - glb_policy->started_picking = true; - gpr_backoff_reset(&glb_policy->lb_call_backoff_state); - query_for_backends_locked(exec_ctx, glb_policy); -} - -static void glb_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - if (!glb_policy->started_picking) { - start_picking_locked(exec_ctx, glb_policy); - } -} - -static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - const grpc_lb_policy_pick_args *pick_args, - grpc_connected_subchannel **target, - grpc_call_context_element *context, void **user_data, - grpc_closure *on_complete) { - if (pick_args->lb_token_mdelem_storage == NULL) { - *target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, on_complete, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "No mdelem storage for the LB token. Load reporting " - "won't work without it. Failing")); - return 0; - } - - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - bool pick_done; - - if (glb_policy->rr_policy != NULL) { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "grpclb %p about to PICK from RR %p", - (void *)glb_policy, (void *)glb_policy->rr_policy); - } - GRPC_LB_POLICY_REF(glb_policy->rr_policy, "glb_pick"); - - wrapped_rr_closure_arg *wc_arg = - (wrapped_rr_closure_arg *)gpr_zalloc(sizeof(wrapped_rr_closure_arg)); - - GRPC_CLOSURE_INIT(&wc_arg->wrapper_closure, wrapped_rr_closure, wc_arg, - grpc_schedule_on_exec_ctx); - wc_arg->rr_policy = glb_policy->rr_policy; - wc_arg->target = target; - wc_arg->context = context; - GPR_ASSERT(glb_policy->client_stats != NULL); - wc_arg->client_stats = - grpc_grpclb_client_stats_ref(glb_policy->client_stats); - wc_arg->wrapped_closure = on_complete; - wc_arg->lb_token_mdelem_storage = pick_args->lb_token_mdelem_storage; - wc_arg->initial_metadata = pick_args->initial_metadata; - wc_arg->free_when_done = wc_arg; - pick_done = - pick_from_internal_rr_locked(exec_ctx, glb_policy, pick_args, - false /* force_async */, target, wc_arg); - } else { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_DEBUG, - "No RR policy in grpclb instance %p. Adding to grpclb's pending " - "picks", - (void *)(glb_policy)); - } - add_pending_pick(&glb_policy->pending_picks, pick_args, target, context, - on_complete); - - if (!glb_policy->started_picking) { - start_picking_locked(exec_ctx, glb_policy); - } - pick_done = false; - } - return pick_done; -} - -static grpc_connectivity_state glb_check_connectivity_locked( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_error **connectivity_error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - return grpc_connectivity_state_get(&glb_policy->state_tracker, - connectivity_error); -} - -static void glb_ping_one_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_closure *closure) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - if (glb_policy->rr_policy) { - grpc_lb_policy_ping_one_locked(exec_ctx, glb_policy->rr_policy, closure); - } else { - add_pending_ping(&glb_policy->pending_pings, closure); - if (!glb_policy->started_picking) { - start_picking_locked(exec_ctx, glb_policy); - } - } -} - -static void glb_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *pol, - grpc_connectivity_state *current, - grpc_closure *notify) { - glb_lb_policy *glb_policy = (glb_lb_policy *)pol; - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &glb_policy->state_tracker, current, notify); -} - -static void lb_call_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - glb_policy->retry_timer_active = false; - if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Restaring call to LB server (grpclb %p)", - (void *)glb_policy); - } - GPR_ASSERT(glb_policy->lb_call == NULL); - query_for_backends_locked(exec_ctx, glb_policy); - } - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, "grpclb_retry_timer"); -} - -static void maybe_restart_lb_call(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - if (glb_policy->started_picking && glb_policy->updating_lb_call) { - if (glb_policy->retry_timer_active) { - grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer); - } - if (!glb_policy->shutting_down) start_picking_locked(exec_ctx, glb_policy); - glb_policy->updating_lb_call = false; - } else if (!glb_policy->shutting_down) { - /* if we aren't shutting down, restart the LB client call after some time */ - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec next_try = - gpr_backoff_step(&glb_policy->lb_call_backoff_state, now); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_DEBUG, "Connection to LB server lost (grpclb: %p)...", - (void *)glb_policy); - gpr_timespec timeout = gpr_time_sub(next_try, now); - if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { - gpr_log(GPR_DEBUG, - "... retry_timer_active in %" PRId64 ".%09d seconds.", - timeout.tv_sec, timeout.tv_nsec); - } else { - gpr_log(GPR_DEBUG, "... retry_timer_active immediately."); - } - } - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_retry_timer"); - GRPC_CLOSURE_INIT(&glb_policy->lb_on_call_retry, - lb_call_on_retry_timer_locked, glb_policy, - grpc_combiner_scheduler(glb_policy->base.combiner)); - glb_policy->retry_timer_active = true; - grpc_timer_init(exec_ctx, &glb_policy->lb_call_retry_timer, next_try, - &glb_policy->lb_on_call_retry, now); - } - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "lb_on_server_status_received_locked"); -} - -static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); - -static void schedule_next_client_load_report(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - const gpr_timespec next_client_load_report_time = - gpr_time_add(now, glb_policy->client_stats_report_interval); - GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure, - send_client_load_report_locked, glb_policy, - grpc_combiner_scheduler(glb_policy->base.combiner)); - grpc_timer_init(exec_ctx, &glb_policy->client_load_report_timer, - next_client_load_report_time, - &glb_policy->client_load_report_closure, now); -} - -static void client_load_report_done_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - grpc_byte_buffer_destroy(glb_policy->client_load_report_payload); - glb_policy->client_load_report_payload = NULL; - if (error != GRPC_ERROR_NONE || glb_policy->lb_call == NULL) { - glb_policy->client_load_report_timer_pending = false; - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "client_load_report"); - return; - } - schedule_next_client_load_report(exec_ctx, glb_policy); -} - -static bool load_report_counters_are_zero(grpc_grpclb_request *request) { - grpc_grpclb_dropped_call_counts *drop_entries = - (grpc_grpclb_dropped_call_counts *) - request->client_stats.calls_finished_with_drop.arg; - return request->client_stats.num_calls_started == 0 && - request->client_stats.num_calls_finished == 0 && - request->client_stats.num_calls_finished_with_client_failed_to_send == - 0 && - request->client_stats.num_calls_finished_known_received == 0 && - (drop_entries == NULL || drop_entries->num_entries == 0); -} - -static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - if (error == GRPC_ERROR_CANCELLED || glb_policy->lb_call == NULL) { - glb_policy->client_load_report_timer_pending = false; - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "client_load_report"); - if (glb_policy->lb_call == NULL) { - maybe_restart_lb_call(exec_ctx, glb_policy); - } - return; - } - // Construct message payload. - GPR_ASSERT(glb_policy->client_load_report_payload == NULL); - grpc_grpclb_request *request = - grpc_grpclb_load_report_request_create_locked(glb_policy->client_stats); - // Skip client load report if the counters were all zero in the last - // report and they are still zero in this one. - if (load_report_counters_are_zero(request)) { - if (glb_policy->last_client_load_report_counters_were_zero) { - grpc_grpclb_request_destroy(request); - schedule_next_client_load_report(exec_ctx, glb_policy); - return; - } - glb_policy->last_client_load_report_counters_were_zero = true; - } else { - glb_policy->last_client_load_report_counters_were_zero = false; - } - grpc_slice request_payload_slice = grpc_grpclb_request_encode(request); - glb_policy->client_load_report_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_slice_unref_internal(exec_ctx, request_payload_slice); - grpc_grpclb_request_destroy(request); - // Send load report message. - grpc_op op; - memset(&op, 0, sizeof(op)); - op.op = GRPC_OP_SEND_MESSAGE; - op.data.send_message.send_message = glb_policy->client_load_report_payload; - GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure, - client_load_report_done_locked, glb_policy, - grpc_combiner_scheduler(glb_policy->base.combiner)); - grpc_call_error call_error = grpc_call_start_batch_and_execute( - exec_ctx, glb_policy->lb_call, &op, 1, - &glb_policy->client_load_report_closure); - if (call_error != GRPC_CALL_OK) { - gpr_log(GPR_ERROR, "call_error=%d", call_error); - GPR_ASSERT(GRPC_CALL_OK == call_error); - } -} - -static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error); -static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); -static void lb_call_init_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - GPR_ASSERT(glb_policy->server_name != NULL); - GPR_ASSERT(glb_policy->server_name[0] != '\0'); - GPR_ASSERT(glb_policy->lb_call == NULL); - GPR_ASSERT(!glb_policy->shutting_down); - - /* Note the following LB call progresses every time there's activity in \a - * glb_policy->base.interested_parties, which is comprised of the polling - * entities from \a client_channel. */ - grpc_slice host = grpc_slice_from_copied_string(glb_policy->server_name); - gpr_timespec deadline = - glb_policy->lb_call_timeout_ms == 0 - ? gpr_inf_future(GPR_CLOCK_MONOTONIC) - : gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), - gpr_time_from_millis(glb_policy->lb_call_timeout_ms, - GPR_TIMESPAN)); - glb_policy->lb_call = grpc_channel_create_pollset_set_call( - exec_ctx, glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS, - glb_policy->base.interested_parties, - GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD, - &host, deadline, NULL); - grpc_slice_unref_internal(exec_ctx, host); - - if (glb_policy->client_stats != NULL) { - grpc_grpclb_client_stats_unref(glb_policy->client_stats); - } - glb_policy->client_stats = grpc_grpclb_client_stats_create(); - - grpc_metadata_array_init(&glb_policy->lb_initial_metadata_recv); - grpc_metadata_array_init(&glb_policy->lb_trailing_metadata_recv); - - grpc_grpclb_request *request = - grpc_grpclb_request_create(glb_policy->server_name); - grpc_slice request_payload_slice = grpc_grpclb_request_encode(request); - glb_policy->lb_request_payload = - grpc_raw_byte_buffer_create(&request_payload_slice, 1); - grpc_slice_unref_internal(exec_ctx, request_payload_slice); - grpc_grpclb_request_destroy(request); - - GRPC_CLOSURE_INIT(&glb_policy->lb_on_server_status_received, - lb_on_server_status_received_locked, glb_policy, - grpc_combiner_scheduler(glb_policy->base.combiner)); - GRPC_CLOSURE_INIT(&glb_policy->lb_on_response_received, - lb_on_response_received_locked, glb_policy, - grpc_combiner_scheduler(glb_policy->base.combiner)); - - gpr_backoff_init(&glb_policy->lb_call_backoff_state, - GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS, - GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER, - GRPC_GRPCLB_RECONNECT_JITTER, - GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000, - GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000); - - glb_policy->seen_initial_response = false; - glb_policy->last_client_load_report_counters_were_zero = false; -} - -static void lb_call_destroy_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - GPR_ASSERT(glb_policy->lb_call != NULL); - grpc_call_unref(glb_policy->lb_call); - glb_policy->lb_call = NULL; - - grpc_metadata_array_destroy(&glb_policy->lb_initial_metadata_recv); - grpc_metadata_array_destroy(&glb_policy->lb_trailing_metadata_recv); - - grpc_byte_buffer_destroy(glb_policy->lb_request_payload); - grpc_slice_unref_internal(exec_ctx, glb_policy->lb_call_status_details); - - if (glb_policy->client_load_report_timer_pending) { - grpc_timer_cancel(exec_ctx, &glb_policy->client_load_report_timer); - } -} - -/* - * Auxiliary functions and LB client callbacks. - */ -static void query_for_backends_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy) { - GPR_ASSERT(glb_policy->lb_channel != NULL); - if (glb_policy->shutting_down) return; - - lb_call_init_locked(exec_ctx, glb_policy); - - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, - "Query for backends (grpclb: %p, lb_channel: %p, lb_call: %p)", - (void *)glb_policy, (void *)glb_policy->lb_channel, - (void *)glb_policy->lb_call); - } - GPR_ASSERT(glb_policy->lb_call != NULL); - - grpc_call_error call_error; - grpc_op ops[3]; - memset(ops, 0, sizeof(ops)); - - grpc_op *op = ops; - op->op = GRPC_OP_SEND_INITIAL_METADATA; - op->data.send_initial_metadata.count = 0; - op->flags = 0; - op->reserved = NULL; - op++; - op->op = GRPC_OP_RECV_INITIAL_METADATA; - op->data.recv_initial_metadata.recv_initial_metadata = - &glb_policy->lb_initial_metadata_recv; - op->flags = 0; - op->reserved = NULL; - op++; - GPR_ASSERT(glb_policy->lb_request_payload != NULL); - op->op = GRPC_OP_SEND_MESSAGE; - op->data.send_message.send_message = glb_policy->lb_request_payload; - op->flags = 0; - op->reserved = NULL; - op++; - call_error = grpc_call_start_batch_and_execute(exec_ctx, glb_policy->lb_call, - ops, (size_t)(op - ops), NULL); - GPR_ASSERT(GRPC_CALL_OK == call_error); - - op = ops; - op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; - op->data.recv_status_on_client.trailing_metadata = - &glb_policy->lb_trailing_metadata_recv; - op->data.recv_status_on_client.status = &glb_policy->lb_call_status; - op->data.recv_status_on_client.status_details = - &glb_policy->lb_call_status_details; - op->flags = 0; - op->reserved = NULL; - op++; - /* take a weak ref (won't prevent calling of \a glb_shutdown if the strong ref - * count goes to zero) to be unref'd in lb_on_server_status_received_locked */ - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, - "lb_on_server_status_received_locked"); - call_error = grpc_call_start_batch_and_execute( - exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops), - &glb_policy->lb_on_server_status_received); - GPR_ASSERT(GRPC_CALL_OK == call_error); - - op = ops; - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &glb_policy->lb_response_payload; - op->flags = 0; - op->reserved = NULL; - op++; - /* take another weak ref to be unref'd/reused in - * lb_on_response_received_locked */ - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "lb_on_response_received_locked"); - call_error = grpc_call_start_batch_and_execute( - exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops), - &glb_policy->lb_on_response_received); - GPR_ASSERT(GRPC_CALL_OK == call_error); -} - -static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - grpc_op ops[2]; - memset(ops, 0, sizeof(ops)); - grpc_op *op = ops; - if (glb_policy->lb_response_payload != NULL) { - gpr_backoff_reset(&glb_policy->lb_call_backoff_state); - /* Received data from the LB server. Look inside - * glb_policy->lb_response_payload, for a serverlist. */ - grpc_byte_buffer_reader bbr; - grpc_byte_buffer_reader_init(&bbr, glb_policy->lb_response_payload); - grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr); - grpc_byte_buffer_reader_destroy(&bbr); - grpc_byte_buffer_destroy(glb_policy->lb_response_payload); - - grpc_grpclb_initial_response *response = NULL; - if (!glb_policy->seen_initial_response && - (response = grpc_grpclb_initial_response_parse(response_slice)) != - NULL) { - if (response->has_client_stats_report_interval) { - glb_policy->client_stats_report_interval = - gpr_time_max(gpr_time_from_seconds(1, GPR_TIMESPAN), - grpc_grpclb_duration_to_timespec( - &response->client_stats_report_interval)); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, - "received initial LB response message; " - "client load reporting interval = %" PRId64 ".%09d sec", - glb_policy->client_stats_report_interval.tv_sec, - glb_policy->client_stats_report_interval.tv_nsec); - } - /* take a weak ref (won't prevent calling of \a glb_shutdown() if the - * strong ref count goes to zero) to be unref'd in - * send_client_load_report_locked() */ - glb_policy->client_load_report_timer_pending = true; - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "client_load_report"); - schedule_next_client_load_report(exec_ctx, glb_policy); - } else if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, - "received initial LB response message; " - "client load reporting NOT enabled"); - } - grpc_grpclb_initial_response_destroy(response); - glb_policy->seen_initial_response = true; - } else { - grpc_grpclb_serverlist *serverlist = - grpc_grpclb_response_parse_serverlist(response_slice); - if (serverlist != NULL) { - GPR_ASSERT(glb_policy->lb_call != NULL); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Serverlist with %lu servers received", - (unsigned long)serverlist->num_servers); - for (size_t i = 0; i < serverlist->num_servers; ++i) { - grpc_resolved_address addr; - parse_server(serverlist->servers[i], &addr); - char *ipport; - grpc_sockaddr_to_string(&ipport, &addr, false); - gpr_log(GPR_INFO, "Serverlist[%lu]: %s", (unsigned long)i, ipport); - gpr_free(ipport); - } - } - /* update serverlist */ - if (serverlist->num_servers > 0) { - if (grpc_grpclb_serverlist_equals(glb_policy->serverlist, - serverlist)) { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, - "Incoming server list identical to current, ignoring."); - } - grpc_grpclb_destroy_serverlist(serverlist); - } else { /* new serverlist */ - if (glb_policy->serverlist != NULL) { - /* dispose of the old serverlist */ - grpc_grpclb_destroy_serverlist(glb_policy->serverlist); - } else { - /* or dispose of the fallback */ - grpc_lb_addresses_destroy(exec_ctx, - glb_policy->fallback_backend_addresses); - glb_policy->fallback_backend_addresses = NULL; - if (glb_policy->fallback_timer_active) { - grpc_timer_cancel(exec_ctx, &glb_policy->lb_fallback_timer); - glb_policy->fallback_timer_active = false; - } - } - /* and update the copy in the glb_lb_policy instance. This - * serverlist instance will be destroyed either upon the next - * update or in glb_destroy() */ - glb_policy->serverlist = serverlist; - glb_policy->serverlist_index = 0; - rr_handover_locked(exec_ctx, glb_policy); - } - } else { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Received empty server list, ignoring."); - } - grpc_grpclb_destroy_serverlist(serverlist); - } - } else { /* serverlist == NULL */ - gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.", - grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX)); - } - } - grpc_slice_unref_internal(exec_ctx, response_slice); - if (!glb_policy->shutting_down) { - /* keep listening for serverlist updates */ - op->op = GRPC_OP_RECV_MESSAGE; - op->data.recv_message.recv_message = &glb_policy->lb_response_payload; - op->flags = 0; - op->reserved = NULL; - op++; - /* reuse the "lb_on_response_received_locked" weak ref taken in - * query_for_backends_locked() */ - const grpc_call_error call_error = grpc_call_start_batch_and_execute( - exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops), - &glb_policy->lb_on_response_received); /* loop */ - GPR_ASSERT(GRPC_CALL_OK == call_error); - } else { - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "lb_on_response_received_locked_shutdown"); - } - } else { /* empty payload: call cancelled. */ - /* dispose of the "lb_on_response_received_locked" weak ref taken in - * query_for_backends_locked() and reused in every reception loop */ - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "lb_on_response_received_locked_empty_payload"); - } -} - -static void lb_on_fallback_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - glb_policy->fallback_timer_active = false; - /* If we receive a serverlist after the timer fires but before this callback - * actually runs, don't fall back. */ - if (glb_policy->serverlist == NULL) { - if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) { - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, - "Falling back to use backends from resolver (grpclb %p)", - (void *)glb_policy); - } - GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL); - rr_handover_locked(exec_ctx, glb_policy); - } - } - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "grpclb_fallback_timer"); -} - -static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - GPR_ASSERT(glb_policy->lb_call != NULL); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - char *status_details = - grpc_slice_to_c_string(glb_policy->lb_call_status_details); - gpr_log(GPR_INFO, - "Status from LB server received. Status = %d, Details = '%s', " - "(call: %p), error %p", - glb_policy->lb_call_status, status_details, - (void *)glb_policy->lb_call, (void *)error); - gpr_free(status_details); - } - /* We need to perform cleanups no matter what. */ - lb_call_destroy_locked(exec_ctx, glb_policy); - // If the load report timer is still pending, we wait for it to be - // called before restarting the call. Otherwise, we restart the call - // here. - if (!glb_policy->client_load_report_timer_pending) { - maybe_restart_lb_call(exec_ctx, glb_policy); - } -} - -static void fallback_update_locked(grpc_exec_ctx *exec_ctx, - glb_lb_policy *glb_policy, - const grpc_lb_addresses *addresses) { - GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL); - grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses); - glb_policy->fallback_backend_addresses = - extract_backend_addresses_locked(exec_ctx, addresses); - if (glb_policy->lb_fallback_timeout_ms > 0 && - !glb_policy->fallback_timer_active) { - rr_handover_locked(exec_ctx, glb_policy); - } -} - -static void glb_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - const grpc_lb_policy_args *args) { - glb_lb_policy *glb_policy = (glb_lb_policy *)policy; - const grpc_arg *arg = - grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); - if (arg == NULL || arg->type != GRPC_ARG_POINTER) { - if (glb_policy->lb_channel == NULL) { - // If we don't have a current channel to the LB, go into TRANSIENT - // FAILURE. - grpc_connectivity_state_set( - exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), - "glb_update_missing"); - } else { - // otherwise, keep using the current LB channel (ignore this update). - gpr_log(GPR_ERROR, - "No valid LB addresses channel arg for grpclb %p update, " - "ignoring.", - (void *)glb_policy); - } - return; - } - const grpc_lb_addresses *addresses = - (const grpc_lb_addresses *)arg->value.pointer.p; - - if (glb_policy->serverlist == NULL) { - // If a non-empty serverlist hasn't been received from the balancer, - // propagate the update to fallback_backend_addresses. - fallback_update_locked(exec_ctx, glb_policy, addresses); - } else if (glb_policy->updating_lb_channel) { - // If we have recieved serverlist from the balancer, we need to defer update - // when there is an in-progress one. - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, - "Update already in progress for grpclb %p. Deferring update.", - (void *)glb_policy); - } - if (glb_policy->pending_update_args != NULL) { - grpc_channel_args_destroy(exec_ctx, - glb_policy->pending_update_args->args); - gpr_free(glb_policy->pending_update_args); - } - glb_policy->pending_update_args = (grpc_lb_policy_args *)gpr_zalloc( - sizeof(*glb_policy->pending_update_args)); - glb_policy->pending_update_args->client_channel_factory = - args->client_channel_factory; - glb_policy->pending_update_args->args = grpc_channel_args_copy(args->args); - glb_policy->pending_update_args->combiner = args->combiner; - return; - } - - glb_policy->updating_lb_channel = true; - GPR_ASSERT(glb_policy->lb_channel != NULL); - grpc_channel_args *lb_channel_args = build_lb_channel_args( - exec_ctx, addresses, glb_policy->response_generator, args->args); - /* Propagate updates to the LB channel (pick first) through the fake resolver - */ - grpc_fake_resolver_response_generator_set_response( - exec_ctx, glb_policy->response_generator, lb_channel_args); - grpc_channel_args_destroy(exec_ctx, lb_channel_args); - - if (!glb_policy->watching_lb_channel) { - // Watch the LB channel connectivity for connection. - glb_policy->lb_channel_connectivity = grpc_channel_check_connectivity_state( - glb_policy->lb_channel, true /* try to connect */); - grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(glb_policy->lb_channel)); - GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); - glb_policy->watching_lb_channel = true; - GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "watch_lb_channel_connectivity"); - grpc_client_channel_watch_connectivity_state( - exec_ctx, client_channel_elem, - grpc_polling_entity_create_from_pollset_set( - glb_policy->base.interested_parties), - &glb_policy->lb_channel_connectivity, - &glb_policy->lb_channel_on_connectivity_changed, NULL); - } -} - -// Invoked as part of the update process. It continues watching the LB channel -// until it shuts down or becomes READY. It's invoked even if the LB channel -// stayed READY throughout the update (for example if the update is identical). -static void glb_lb_channel_on_connectivity_changed_cb(grpc_exec_ctx *exec_ctx, - void *arg, - grpc_error *error) { - glb_lb_policy *glb_policy = (glb_lb_policy *)arg; - if (glb_policy->shutting_down) goto done; - // Re-initialize the lb_call. This should also take care of updating the - // embedded RR policy. Note that the current RR policy, if any, will stay in - // effect until an update from the new lb_call is received. - switch (glb_policy->lb_channel_connectivity) { - case GRPC_CHANNEL_INIT: - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_TRANSIENT_FAILURE: { - /* resub. */ - grpc_channel_element *client_channel_elem = - grpc_channel_stack_last_element( - grpc_channel_get_channel_stack(glb_policy->lb_channel)); - GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); - grpc_client_channel_watch_connectivity_state( - exec_ctx, client_channel_elem, - grpc_polling_entity_create_from_pollset_set( - glb_policy->base.interested_parties), - &glb_policy->lb_channel_connectivity, - &glb_policy->lb_channel_on_connectivity_changed, NULL); - break; - } - case GRPC_CHANNEL_IDLE: - // lb channel inactive (probably shutdown prior to update). Restart lb - // call to kick the lb channel into gear. - GPR_ASSERT(glb_policy->lb_call == NULL); - /* fallthrough */ - case GRPC_CHANNEL_READY: - if (glb_policy->lb_call != NULL) { - glb_policy->updating_lb_channel = false; - glb_policy->updating_lb_call = true; - grpc_call_cancel(glb_policy->lb_call, NULL); - // lb_on_server_status_received will pick up the cancel and reinit - // lb_call. - if (glb_policy->pending_update_args != NULL) { - grpc_lb_policy_args *args = glb_policy->pending_update_args; - glb_policy->pending_update_args = NULL; - glb_update_locked(exec_ctx, &glb_policy->base, args); - grpc_channel_args_destroy(exec_ctx, args->args); - gpr_free(args); - } - } else if (glb_policy->started_picking && !glb_policy->shutting_down) { - if (glb_policy->retry_timer_active) { - grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer); - glb_policy->retry_timer_active = false; - } - start_picking_locked(exec_ctx, glb_policy); - } - /* fallthrough */ - case GRPC_CHANNEL_SHUTDOWN: - done: - glb_policy->watching_lb_channel = false; - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, - "watch_lb_channel_connectivity_cb_shutdown"); - break; - } -} - -/* Code wiring the policy with the rest of the core */ -static const grpc_lb_policy_vtable glb_lb_policy_vtable = { - glb_destroy, - glb_shutdown_locked, - glb_pick_locked, - glb_cancel_pick_locked, - glb_cancel_picks_locked, - glb_ping_one_locked, - glb_exit_idle_locked, - glb_check_connectivity_locked, - glb_notify_on_state_change_locked, - glb_update_locked}; - -static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, - grpc_lb_policy_factory *factory, - grpc_lb_policy_args *args) { - /* Count the number of gRPC-LB addresses. There must be at least one. */ - const grpc_arg *arg = - grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); - if (arg == NULL || arg->type != GRPC_ARG_POINTER) { - return NULL; - } - grpc_lb_addresses *addresses = (grpc_lb_addresses *)arg->value.pointer.p; - size_t num_grpclb_addrs = 0; - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; - } - if (num_grpclb_addrs == 0) return NULL; - - glb_lb_policy *glb_policy = (glb_lb_policy *)gpr_zalloc(sizeof(*glb_policy)); - - /* Get server name. */ - arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI); - GPR_ASSERT(arg != NULL); - GPR_ASSERT(arg->type == GRPC_ARG_STRING); - grpc_uri *uri = grpc_uri_parse(exec_ctx, arg->value.string, true); - GPR_ASSERT(uri->path[0] != '\0'); - glb_policy->server_name = - gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path); - if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { - gpr_log(GPR_INFO, "Will use '%s' as the server name for LB request.", - glb_policy->server_name); - } - grpc_uri_destroy(uri); - - glb_policy->cc_factory = args->client_channel_factory; - GPR_ASSERT(glb_policy->cc_factory != NULL); - - arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS); - glb_policy->lb_call_timeout_ms = - grpc_channel_arg_get_integer(arg, (grpc_integer_options){0, 0, INT_MAX}); - - arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS); - glb_policy->lb_fallback_timeout_ms = grpc_channel_arg_get_integer( - arg, (grpc_integer_options){GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0, - INT_MAX}); - - // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args, - // since we use this to trigger the client_load_reporting filter. - grpc_arg new_arg = grpc_channel_arg_string_create( - (char *)GRPC_ARG_LB_POLICY_NAME, (char *)"grpclb"); - static const char *args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME}; - glb_policy->args = grpc_channel_args_copy_and_add_and_remove( - args->args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1); - - /* Extract the backend addresses (may be empty) from the resolver for - * fallback. */ - glb_policy->fallback_backend_addresses = - extract_backend_addresses_locked(exec_ctx, addresses); - - /* Create a client channel over them to communicate with a LB service */ - glb_policy->response_generator = - grpc_fake_resolver_response_generator_create(); - grpc_channel_args *lb_channel_args = build_lb_channel_args( - exec_ctx, addresses, glb_policy->response_generator, args->args); - char *uri_str; - gpr_asprintf(&uri_str, "fake:///%s", glb_policy->server_name); - glb_policy->lb_channel = grpc_lb_policy_grpclb_create_lb_channel( - exec_ctx, uri_str, args->client_channel_factory, lb_channel_args); - - /* Propagate initial resolution */ - grpc_fake_resolver_response_generator_set_response( - exec_ctx, glb_policy->response_generator, lb_channel_args); - grpc_channel_args_destroy(exec_ctx, lb_channel_args); - gpr_free(uri_str); - if (glb_policy->lb_channel == NULL) { - gpr_free((void *)glb_policy->server_name); - grpc_channel_args_destroy(exec_ctx, glb_policy->args); - gpr_free(glb_policy); - return NULL; - } - grpc_subchannel_index_ref(); - GRPC_CLOSURE_INIT(&glb_policy->lb_channel_on_connectivity_changed, - glb_lb_channel_on_connectivity_changed_cb, glb_policy, - grpc_combiner_scheduler(args->combiner)); - grpc_lb_policy_init(&glb_policy->base, &glb_lb_policy_vtable, args->combiner); - grpc_connectivity_state_init(&glb_policy->state_tracker, GRPC_CHANNEL_IDLE, - "grpclb"); - return &glb_policy->base; -} - -static void glb_factory_ref(grpc_lb_policy_factory *factory) {} - -static void glb_factory_unref(grpc_lb_policy_factory *factory) {} - -static const grpc_lb_policy_factory_vtable glb_factory_vtable = { - glb_factory_ref, glb_factory_unref, glb_create, "grpclb"}; - -static grpc_lb_policy_factory glb_lb_policy_factory = {&glb_factory_vtable}; - -grpc_lb_policy_factory *grpc_glb_lb_factory_create() { - return &glb_lb_policy_factory; -} - -/* Plugin registration */ - -// Only add client_load_reporting filter if the grpclb LB policy is used. -static bool maybe_add_client_load_reporting_filter( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { - const grpc_channel_args *args = - grpc_channel_stack_builder_get_channel_arguments(builder); - const grpc_arg *channel_arg = - grpc_channel_args_find(args, GRPC_ARG_LB_POLICY_NAME); - if (channel_arg != NULL && channel_arg->type == GRPC_ARG_STRING && - strcmp(channel_arg->value.string, "grpclb") == 0) { - return grpc_channel_stack_builder_append_filter( - builder, (const grpc_channel_filter *)arg, NULL, NULL); - } - return true; -} - -void grpc_lb_policy_grpclb_init() { - grpc_register_lb_policy(grpc_glb_lb_factory_create()); - grpc_register_tracer(&grpc_lb_glb_trace); -#ifndef NDEBUG - grpc_register_tracer(&grpc_trace_lb_policy_refcount); -#endif - grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_client_load_reporting_filter, - (void *)&grpc_client_load_reporting_filter); -} - -void grpc_lb_policy_grpclb_shutdown() {} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc new file mode 100644 index 0000000000..b9e72da6be --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -0,0 +1,2021 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** Implementation of the gRPC LB policy. + * + * This policy takes as input a set of resolved addresses {a1..an} for which the + * LB set was set (it's the resolver's responsibility to ensure this). That is + * to say, {a1..an} represent a collection of LB servers. + * + * An internal channel (\a glb_lb_policy.lb_channel) is created over {a1..an}. + * This channel behaves just like a regular channel. In particular, the + * constructed URI over the addresses a1..an will use the default pick first + * policy to select from this list of LB server backends. + * + * The first time the policy gets a request for a pick, a ping, or to exit the + * idle state, \a query_for_backends_locked() is called. This function sets up + * and initiates the internal communication with the LB server. In particular, + * it's responsible for instantiating the internal *streaming* call to the LB + * server (whichever address from {a1..an} pick-first chose). This call is + * serviced by two callbacks, \a lb_on_server_status_received and \a + * lb_on_response_received. The former will be called when the call to the LB + * server completes. This can happen if the LB server closes the connection or + * if this policy itself cancels the call (for example because it's shutting + * down). If the internal call times out, the usual behavior of pick-first + * applies, continuing to pick from the list {a1..an}. + * + * Upon sucesss, the incoming \a LoadBalancingResponse is processed by \a + * res_recv. An invalid one results in the termination of the streaming call. A + * new streaming call should be created if possible, failing the original call + * otherwise. For a valid \a LoadBalancingResponse, the server list of actual + * backends is extracted. A Round Robin policy will be created from this list. + * There are two possible scenarios: + * + * 1. This is the first server list received. There was no previous instance of + * the Round Robin policy. \a rr_handover_locked() will instantiate the RR + * policy and perform all the pending operations over it. + * 2. There's already a RR policy instance active. We need to introduce the new + * one build from the new serverlist, but taking care not to disrupt the + * operations in progress over the old RR instance. This is done by + * decreasing the reference count on the old policy. The moment no more + * references are held on the old RR policy, it'll be destroyed and \a + * glb_rr_connectivity_changed notified with a \a GRPC_CHANNEL_SHUTDOWN + * state. At this point we can transition to a new RR instance safely, which + * is done once again via \a rr_handover_locked(). + * + * + * Once a RR policy instance is in place (and getting updated as described), + * calls to for a pick, a ping or a cancellation will be serviced right away by + * forwarding them to the RR instance. Any time there's no RR policy available + * (ie, right after the creation of the gRPCLB policy, if an empty serverlist is + * received, etc), pick/ping requests are added to a list of pending picks/pings + * to be flushed and serviced as part of \a rr_handover_locked() the moment the + * RR policy instance becomes available. + * + * \see https://github.com/grpc/grpc/blob/master/doc/load-balancing.md for the + * high level design and details. */ + +/* TODO(dgq): + * - Implement LB service forwarding (point 2c. in the doc's diagram). + */ + +/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when + using that endpoint. Because of various transitive includes in uv.h, + including windows.h on Windows, uv.h must be included before other system + headers. Therefore, sockaddr.h must always be included first */ +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/client_channel_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/backoff.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/static_metadata.h" + +#define GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS 20 +#define GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_GRPCLB_RECONNECT_JITTER 0.2 +#define GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS 10000 + +grpc_tracer_flag grpc_lb_glb_trace = GRPC_TRACER_INITIALIZER(false, "glb"); + +/* add lb_token of selected subchannel (address) to the call's initial + * metadata */ +static grpc_error *initial_metadata_add_lb_token( + grpc_exec_ctx *exec_ctx, grpc_metadata_batch *initial_metadata, + grpc_linked_mdelem *lb_token_mdelem_storage, grpc_mdelem lb_token) { + GPR_ASSERT(lb_token_mdelem_storage != NULL); + GPR_ASSERT(!GRPC_MDISNULL(lb_token)); + return grpc_metadata_batch_add_tail(exec_ctx, initial_metadata, + lb_token_mdelem_storage, lb_token); +} + +static void destroy_client_stats(void *arg) { + grpc_grpclb_client_stats_unref((grpc_grpclb_client_stats *)arg); +} + +typedef struct wrapped_rr_closure_arg { + /* the closure instance using this struct as argument */ + grpc_closure wrapper_closure; + + /* the original closure. Usually a on_complete/notify cb for pick() and ping() + * calls against the internal RR instance, respectively. */ + grpc_closure *wrapped_closure; + + /* the pick's initial metadata, kept in order to append the LB token for the + * pick */ + grpc_metadata_batch *initial_metadata; + + /* the picked target, used to determine which LB token to add to the pick's + * initial metadata */ + grpc_connected_subchannel **target; + + /* the context to be populated for the subchannel call */ + grpc_call_context_element *context; + + /* Stats for client-side load reporting. Note that this holds a + * reference, which must be either passed on via context or unreffed. */ + grpc_grpclb_client_stats *client_stats; + + /* the LB token associated with the pick */ + grpc_mdelem lb_token; + + /* storage for the lb token initial metadata mdelem */ + grpc_linked_mdelem *lb_token_mdelem_storage; + + /* The RR instance related to the closure */ + grpc_lb_policy *rr_policy; + + /* heap memory to be freed upon closure execution. */ + void *free_when_done; +} wrapped_rr_closure_arg; + +/* The \a on_complete closure passed as part of the pick requires keeping a + * reference to its associated round robin instance. We wrap this closure in + * order to unref the round robin instance upon its invocation */ +static void wrapped_rr_closure(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + wrapped_rr_closure_arg *wc_arg = (wrapped_rr_closure_arg *)arg; + + GPR_ASSERT(wc_arg->wrapped_closure != NULL); + GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_REF(error)); + + if (wc_arg->rr_policy != NULL) { + /* if *target is NULL, no pick has been made by the RR policy (eg, all + * addresses failed to connect). There won't be any user_data/token + * available */ + if (*wc_arg->target != NULL) { + if (!GRPC_MDISNULL(wc_arg->lb_token)) { + initial_metadata_add_lb_token(exec_ctx, wc_arg->initial_metadata, + wc_arg->lb_token_mdelem_storage, + GRPC_MDELEM_REF(wc_arg->lb_token)); + } else { + gpr_log(GPR_ERROR, + "No LB token for connected subchannel pick %p (from RR " + "instance %p).", + (void *)*wc_arg->target, (void *)wc_arg->rr_policy); + abort(); + } + // Pass on client stats via context. Passes ownership of the reference. + GPR_ASSERT(wc_arg->client_stats != NULL); + wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats; + wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats; + } else { + grpc_grpclb_client_stats_unref(wc_arg->client_stats); + } + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Unreffing RR %p", (void *)wc_arg->rr_policy); + } + GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "wrapped_rr_closure"); + } + GPR_ASSERT(wc_arg->free_when_done != NULL); + gpr_free(wc_arg->free_when_done); +} + +/* Linked list of pending pick requests. It stores all information needed to + * eventually call (Round Robin's) pick() on them. They mainly stay pending + * waiting for the RR policy to be created/updated. + * + * One particularity is the wrapping of the user-provided \a on_complete closure + * (in \a wrapped_on_complete and \a wrapped_on_complete_arg). This is needed in + * order to correctly unref the RR policy instance upon completion of the pick. + * See \a wrapped_rr_closure for details. */ +typedef struct pending_pick { + struct pending_pick *next; + + /* original pick()'s arguments */ + grpc_lb_policy_pick_args pick_args; + + /* output argument where to store the pick()ed connected subchannel, or NULL + * upon error. */ + grpc_connected_subchannel **target; + + /* args for wrapped_on_complete */ + wrapped_rr_closure_arg wrapped_on_complete_arg; +} pending_pick; + +static void add_pending_pick(pending_pick **root, + const grpc_lb_policy_pick_args *pick_args, + grpc_connected_subchannel **target, + grpc_call_context_element *context, + grpc_closure *on_complete) { + pending_pick *pp = (pending_pick *)gpr_zalloc(sizeof(*pp)); + pp->next = *root; + pp->pick_args = *pick_args; + pp->target = target; + pp->wrapped_on_complete_arg.wrapped_closure = on_complete; + pp->wrapped_on_complete_arg.target = target; + pp->wrapped_on_complete_arg.context = context; + pp->wrapped_on_complete_arg.initial_metadata = pick_args->initial_metadata; + pp->wrapped_on_complete_arg.lb_token_mdelem_storage = + pick_args->lb_token_mdelem_storage; + pp->wrapped_on_complete_arg.free_when_done = pp; + GRPC_CLOSURE_INIT(&pp->wrapped_on_complete_arg.wrapper_closure, + wrapped_rr_closure, &pp->wrapped_on_complete_arg, + grpc_schedule_on_exec_ctx); + *root = pp; +} + +/* Same as the \a pending_pick struct but for ping operations */ +typedef struct pending_ping { + struct pending_ping *next; + + /* args for wrapped_notify */ + wrapped_rr_closure_arg wrapped_notify_arg; +} pending_ping; + +static void add_pending_ping(pending_ping **root, grpc_closure *notify) { + pending_ping *pping = (pending_ping *)gpr_zalloc(sizeof(*pping)); + pping->wrapped_notify_arg.wrapped_closure = notify; + pping->wrapped_notify_arg.free_when_done = pping; + pping->next = *root; + GRPC_CLOSURE_INIT(&pping->wrapped_notify_arg.wrapper_closure, + wrapped_rr_closure, &pping->wrapped_notify_arg, + grpc_schedule_on_exec_ctx); + *root = pping; +} + +/* + * glb_lb_policy + */ +typedef struct rr_connectivity_data rr_connectivity_data; + +typedef struct glb_lb_policy { + /** base policy: must be first */ + grpc_lb_policy base; + + /** who the client is trying to communicate with */ + const char *server_name; + grpc_client_channel_factory *cc_factory; + grpc_channel_args *args; + + /** timeout in milliseconds for the LB call. 0 means no deadline. */ + int lb_call_timeout_ms; + + /** timeout in milliseconds for before using fallback backend addresses. + * 0 means not using fallback. */ + int lb_fallback_timeout_ms; + + /** for communicating with the LB server */ + grpc_channel *lb_channel; + + /** response generator to inject address updates into \a lb_channel */ + grpc_fake_resolver_response_generator *response_generator; + + /** the RR policy to use of the backend servers returned by the LB server */ + grpc_lb_policy *rr_policy; + + bool started_picking; + + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker; + + /** connectivity state of the LB channel */ + grpc_connectivity_state lb_channel_connectivity; + + /** stores the deserialized response from the LB. May be NULL until one such + * response has arrived. */ + grpc_grpclb_serverlist *serverlist; + + /** Index into serverlist for next pick. + * If the server at this index is a drop, we return a drop. + * Otherwise, we delegate to the RR policy. */ + size_t serverlist_index; + + /** stores the backend addresses from the resolver */ + grpc_lb_addresses *fallback_backend_addresses; + + /** list of picks that are waiting on RR's policy connectivity */ + pending_pick *pending_picks; + + /** list of pings that are waiting on RR's policy connectivity */ + pending_ping *pending_pings; + + bool shutting_down; + + /** are we currently updating lb_call? */ + bool updating_lb_call; + + /** are we currently updating lb_channel? */ + bool updating_lb_channel; + + /** are we already watching the LB channel's connectivity? */ + bool watching_lb_channel; + + /** is \a lb_call_retry_timer active? */ + bool retry_timer_active; + + /** is \a lb_fallback_timer active? */ + bool fallback_timer_active; + + /** called upon changes to the LB channel's connectivity. */ + grpc_closure lb_channel_on_connectivity_changed; + + /** args from the latest update received while already updating, or NULL */ + grpc_lb_policy_args *pending_update_args; + + /************************************************************/ + /* client data associated with the LB server communication */ + /************************************************************/ + /* Status from the LB server has been received. This signals the end of the LB + * call. */ + grpc_closure lb_on_server_status_received; + + /* A response from the LB server has been received. Process it */ + grpc_closure lb_on_response_received; + + /* LB call retry timer callback. */ + grpc_closure lb_on_call_retry; + + /* LB fallback timer callback. */ + grpc_closure lb_on_fallback; + + grpc_call *lb_call; /* streaming call to the LB server, */ + + grpc_metadata_array lb_initial_metadata_recv; /* initial MD from LB server */ + grpc_metadata_array + lb_trailing_metadata_recv; /* trailing MD from LB server */ + + /* what's being sent to the LB server. Note that its value may vary if the LB + * server indicates a redirect. */ + grpc_byte_buffer *lb_request_payload; + + /* response the LB server, if any. Processed in lb_on_response_received() */ + grpc_byte_buffer *lb_response_payload; + + /* call status code and details, set in lb_on_server_status_received() */ + grpc_status_code lb_call_status; + grpc_slice lb_call_status_details; + + /** LB call retry backoff state */ + gpr_backoff lb_call_backoff_state; + + /** LB call retry timer */ + grpc_timer lb_call_retry_timer; + + /** LB fallback timer */ + grpc_timer lb_fallback_timer; + + bool seen_initial_response; + + /* Stats for client-side load reporting. Should be unreffed and + * recreated whenever lb_call is replaced. */ + grpc_grpclb_client_stats *client_stats; + /* Interval and timer for next client load report. */ + gpr_timespec client_stats_report_interval; + grpc_timer client_load_report_timer; + bool client_load_report_timer_pending; + bool last_client_load_report_counters_were_zero; + /* Closure used for either the load report timer or the callback for + * completion of sending the load report. */ + grpc_closure client_load_report_closure; + /* Client load report message payload. */ + grpc_byte_buffer *client_load_report_payload; +} glb_lb_policy; + +/* Keeps track and reacts to changes in connectivity of the RR instance */ +struct rr_connectivity_data { + grpc_closure on_change; + grpc_connectivity_state state; + glb_lb_policy *glb_policy; +}; + +static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, + bool log) { + if (server->drop) return false; + const grpc_grpclb_ip_address *ip = &server->ip_address; + if (server->port >> 16 != 0) { + if (log) { + gpr_log(GPR_ERROR, + "Invalid port '%d' at index %lu of serverlist. Ignoring.", + server->port, (unsigned long)idx); + } + return false; + } + if (ip->size != 4 && ip->size != 16) { + if (log) { + gpr_log(GPR_ERROR, + "Expected IP to be 4 or 16 bytes, got %d at index %lu of " + "serverlist. Ignoring", + ip->size, (unsigned long)idx); + } + return false; + } + return true; +} + +/* vtable for LB tokens in grpc_lb_addresses. */ +static void *lb_token_copy(void *token) { + return token == NULL + ? NULL + : (void *)GRPC_MDELEM_REF((grpc_mdelem){(uintptr_t)token}).payload; +} +static void lb_token_destroy(grpc_exec_ctx *exec_ctx, void *token) { + if (token != NULL) { + GRPC_MDELEM_UNREF(exec_ctx, (grpc_mdelem){(uintptr_t)token}); + } +} +static int lb_token_cmp(void *token1, void *token2) { + if (token1 > token2) return 1; + if (token1 < token2) return -1; + return 0; +} +static const grpc_lb_user_data_vtable lb_token_vtable = { + lb_token_copy, lb_token_destroy, lb_token_cmp}; + +static void parse_server(const grpc_grpclb_server *server, + grpc_resolved_address *addr) { + memset(addr, 0, sizeof(*addr)); + if (server->drop) return; + const uint16_t netorder_port = htons((uint16_t)server->port); + /* the addresses are given in binary format (a in(6)_addr struct) in + * server->ip_address.bytes. */ + const grpc_grpclb_ip_address *ip = &server->ip_address; + if (ip->size == 4) { + addr->len = sizeof(struct sockaddr_in); + struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr->addr; + addr4->sin_family = AF_INET; + memcpy(&addr4->sin_addr, ip->bytes, ip->size); + addr4->sin_port = netorder_port; + } else if (ip->size == 16) { + addr->len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr->addr; + addr6->sin6_family = AF_INET6; + memcpy(&addr6->sin6_addr, ip->bytes, ip->size); + addr6->sin6_port = netorder_port; + } +} + +/* Returns addresses extracted from \a serverlist. */ +static grpc_lb_addresses *process_serverlist_locked( + grpc_exec_ctx *exec_ctx, const grpc_grpclb_serverlist *serverlist) { + size_t num_valid = 0; + /* first pass: count how many are valid in order to allocate the necessary + * memory in a single block */ + for (size_t i = 0; i < serverlist->num_servers; ++i) { + if (is_server_valid(serverlist->servers[i], i, true)) ++num_valid; + } + grpc_lb_addresses *lb_addresses = + grpc_lb_addresses_create(num_valid, &lb_token_vtable); + /* second pass: actually populate the addresses and LB tokens (aka user data + * to the outside world) to be read by the RR policy during its creation. + * Given that the validity tests are very cheap, they are performed again + * instead of marking the valid ones during the first pass, as this would + * incurr in an allocation due to the arbitrary number of server */ + size_t addr_idx = 0; + for (size_t sl_idx = 0; sl_idx < serverlist->num_servers; ++sl_idx) { + const grpc_grpclb_server *server = serverlist->servers[sl_idx]; + if (!is_server_valid(serverlist->servers[sl_idx], sl_idx, false)) continue; + GPR_ASSERT(addr_idx < num_valid); + /* address processing */ + grpc_resolved_address addr; + parse_server(server, &addr); + /* lb token processing */ + void *user_data; + if (server->has_load_balance_token) { + const size_t lb_token_max_length = + GPR_ARRAY_SIZE(server->load_balance_token); + const size_t lb_token_length = + strnlen(server->load_balance_token, lb_token_max_length); + grpc_slice lb_token_mdstr = grpc_slice_from_copied_buffer( + server->load_balance_token, lb_token_length); + user_data = (void *)grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_LB_TOKEN, + lb_token_mdstr) + .payload; + } else { + char *uri = grpc_sockaddr_to_uri(&addr); + gpr_log(GPR_INFO, + "Missing LB token for backend address '%s'. The empty token will " + "be used instead", + uri); + gpr_free(uri); + user_data = (void *)GRPC_MDELEM_LB_TOKEN_EMPTY.payload; + } + + grpc_lb_addresses_set_address(lb_addresses, addr_idx, &addr.addr, addr.len, + false /* is_balancer */, + NULL /* balancer_name */, user_data); + ++addr_idx; + } + GPR_ASSERT(addr_idx == num_valid); + return lb_addresses; +} + +/* Returns the backend addresses extracted from the given addresses */ +static grpc_lb_addresses *extract_backend_addresses_locked( + grpc_exec_ctx *exec_ctx, const grpc_lb_addresses *addresses) { + /* first pass: count the number of backend addresses */ + size_t num_backends = 0; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (!addresses->addresses[i].is_balancer) { + ++num_backends; + } + } + /* second pass: actually populate the addresses and (empty) LB tokens */ + grpc_lb_addresses *backend_addresses = + grpc_lb_addresses_create(num_backends, &lb_token_vtable); + size_t num_copied = 0; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (addresses->addresses[i].is_balancer) continue; + const grpc_resolved_address *addr = &addresses->addresses[i].address; + grpc_lb_addresses_set_address(backend_addresses, num_copied, &addr->addr, + addr->len, false /* is_balancer */, + NULL /* balancer_name */, + (void *)GRPC_MDELEM_LB_TOKEN_EMPTY.payload); + ++num_copied; + } + return backend_addresses; +} + +static void update_lb_connectivity_status_locked( + grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, + grpc_connectivity_state rr_state, grpc_error *rr_state_error) { + const grpc_connectivity_state curr_glb_state = + grpc_connectivity_state_check(&glb_policy->state_tracker); + + /* The new connectivity status is a function of the previous one and the new + * input coming from the status of the RR policy. + * + * current state (grpclb's) + * | + * v || I | C | R | TF | SD | <- new state (RR's) + * ===++====+=====+=====+======+======+ + * I || I | C | R | [I] | [I] | + * ---++----+-----+-----+------+------+ + * C || I | C | R | [C] | [C] | + * ---++----+-----+-----+------+------+ + * R || I | C | R | [R] | [R] | + * ---++----+-----+-----+------+------+ + * TF || I | C | R | [TF] | [TF] | + * ---++----+-----+-----+------+------+ + * SD || NA | NA | NA | NA | NA | (*) + * ---++----+-----+-----+------+------+ + * + * A [STATE] indicates that the old RR policy is kept. In those cases, STATE + * is the current state of grpclb, which is left untouched. + * + * In summary, if the new state is TRANSIENT_FAILURE or SHUTDOWN, stick to + * the previous RR instance. + * + * Note that the status is never updated to SHUTDOWN as a result of calling + * this function. Only glb_shutdown() has the power to set that state. + * + * (*) This function mustn't be called during shutting down. */ + GPR_ASSERT(curr_glb_state != GRPC_CHANNEL_SHUTDOWN); + + switch (rr_state) { + case GRPC_CHANNEL_TRANSIENT_FAILURE: + case GRPC_CHANNEL_SHUTDOWN: + GPR_ASSERT(rr_state_error != GRPC_ERROR_NONE); + break; + case GRPC_CHANNEL_INIT: + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_READY: + GPR_ASSERT(rr_state_error == GRPC_ERROR_NONE); + } + + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log( + GPR_INFO, "Setting grpclb's state to %s from new RR policy %p state.", + grpc_connectivity_state_name(rr_state), (void *)glb_policy->rr_policy); + } + grpc_connectivity_state_set(exec_ctx, &glb_policy->state_tracker, rr_state, + rr_state_error, + "update_lb_connectivity_status_locked"); +} + +/* Perform a pick over \a glb_policy->rr_policy. Given that a pick can return + * immediately (ignoring its completion callback), we need to perform the + * cleanups this callback would otherwise be resposible for. + * If \a force_async is true, then we will manually schedule the + * completion callback even if the pick is available immediately. */ +static bool pick_from_internal_rr_locked( + grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, + const grpc_lb_policy_pick_args *pick_args, bool force_async, + grpc_connected_subchannel **target, wrapped_rr_closure_arg *wc_arg) { + // Check for drops if we are not using fallback backend addresses. + if (glb_policy->serverlist != NULL) { + // Look at the index into the serverlist to see if we should drop this call. + grpc_grpclb_server *server = + glb_policy->serverlist->servers[glb_policy->serverlist_index++]; + if (glb_policy->serverlist_index == glb_policy->serverlist->num_servers) { + glb_policy->serverlist_index = 0; // Wrap-around. + } + if (server->drop) { + // Not using the RR policy, so unref it. + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Unreffing RR for drop (0x%" PRIxPTR ")", + (intptr_t)wc_arg->rr_policy); + } + GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync"); + // Update client load reporting stats to indicate the number of + // dropped calls. Note that we have to do this here instead of in + // the client_load_reporting filter, because we do not create a + // subchannel call (and therefore no client_load_reporting filter) + // for dropped calls. + grpc_grpclb_client_stats_add_call_dropped_locked( + server->load_balance_token, wc_arg->client_stats); + grpc_grpclb_client_stats_unref(wc_arg->client_stats); + if (force_async) { + GPR_ASSERT(wc_arg->wrapped_closure != NULL); + GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE); + gpr_free(wc_arg->free_when_done); + return false; + } + gpr_free(wc_arg->free_when_done); + return true; + } + } + // Pick via the RR policy. + const bool pick_done = grpc_lb_policy_pick_locked( + exec_ctx, wc_arg->rr_policy, pick_args, target, wc_arg->context, + (void **)&wc_arg->lb_token, &wc_arg->wrapper_closure); + if (pick_done) { + /* synchronous grpc_lb_policy_pick call. Unref the RR policy. */ + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Unreffing RR (0x%" PRIxPTR ")", + (intptr_t)wc_arg->rr_policy); + } + GRPC_LB_POLICY_UNREF(exec_ctx, wc_arg->rr_policy, "glb_pick_sync"); + /* add the load reporting initial metadata */ + initial_metadata_add_lb_token(exec_ctx, pick_args->initial_metadata, + pick_args->lb_token_mdelem_storage, + GRPC_MDELEM_REF(wc_arg->lb_token)); + // Pass on client stats via context. Passes ownership of the reference. + GPR_ASSERT(wc_arg->client_stats != NULL); + wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].value = wc_arg->client_stats; + wc_arg->context[GRPC_GRPCLB_CLIENT_STATS].destroy = destroy_client_stats; + if (force_async) { + GPR_ASSERT(wc_arg->wrapped_closure != NULL); + GRPC_CLOSURE_SCHED(exec_ctx, wc_arg->wrapped_closure, GRPC_ERROR_NONE); + gpr_free(wc_arg->free_when_done); + return false; + } + gpr_free(wc_arg->free_when_done); + } + /* else, the pending pick will be registered and taken care of by the + * pending pick list inside the RR policy (glb_policy->rr_policy). + * Eventually, wrapped_on_complete will be called, which will -among other + * things- add the LB token to the call's initial metadata */ + return pick_done; +} + +static grpc_lb_policy_args *lb_policy_args_create(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + grpc_lb_addresses *addresses; + if (glb_policy->serverlist != NULL) { + GPR_ASSERT(glb_policy->serverlist->num_servers > 0); + addresses = process_serverlist_locked(exec_ctx, glb_policy->serverlist); + } else { + // If rr_handover_locked() is invoked when we haven't received any + // serverlist from the balancer, we use the fallback backends returned by + // the resolver. Note that the fallback backend list may be empty, in which + // case the new round_robin policy will keep the requested picks pending. + GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL); + addresses = grpc_lb_addresses_copy(glb_policy->fallback_backend_addresses); + } + GPR_ASSERT(addresses != NULL); + grpc_lb_policy_args *args = (grpc_lb_policy_args *)gpr_zalloc(sizeof(*args)); + args->client_channel_factory = glb_policy->cc_factory; + args->combiner = glb_policy->base.combiner; + // Replace the LB addresses in the channel args that we pass down to + // the subchannel. + static const char *keys_to_remove[] = {GRPC_ARG_LB_ADDRESSES}; + const grpc_arg arg = grpc_lb_addresses_create_channel_arg(addresses); + args->args = grpc_channel_args_copy_and_add_and_remove( + glb_policy->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &arg, + 1); + grpc_lb_addresses_destroy(exec_ctx, addresses); + return args; +} + +static void lb_policy_args_destroy(grpc_exec_ctx *exec_ctx, + grpc_lb_policy_args *args) { + grpc_channel_args_destroy(exec_ctx, args->args); + gpr_free(args); +} + +static void glb_rr_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error); +static void create_rr_locked(grpc_exec_ctx *exec_ctx, glb_lb_policy *glb_policy, + grpc_lb_policy_args *args) { + GPR_ASSERT(glb_policy->rr_policy == NULL); + + grpc_lb_policy *new_rr_policy = + grpc_lb_policy_create(exec_ctx, "round_robin", args); + if (new_rr_policy == NULL) { + gpr_log(GPR_ERROR, + "Failure creating a RoundRobin policy for serverlist update with " + "%lu entries. The previous RR instance (%p), if any, will continue " + "to be used. Future updates from the LB will attempt to create new " + "instances.", + (unsigned long)glb_policy->serverlist->num_servers, + (void *)glb_policy->rr_policy); + return; + } + glb_policy->rr_policy = new_rr_policy; + grpc_error *rr_state_error = NULL; + const grpc_connectivity_state rr_state = + grpc_lb_policy_check_connectivity_locked(exec_ctx, glb_policy->rr_policy, + &rr_state_error); + /* Connectivity state is a function of the RR policy updated/created */ + update_lb_connectivity_status_locked(exec_ctx, glb_policy, rr_state, + rr_state_error); + /* Add the gRPC LB's interested_parties pollset_set to that of the newly + * created RR policy. This will make the RR policy progress upon activity on + * gRPC LB, which in turn is tied to the application's call */ + grpc_pollset_set_add_pollset_set(exec_ctx, + glb_policy->rr_policy->interested_parties, + glb_policy->base.interested_parties); + + /* Allocate the data for the tracking of the new RR policy's connectivity. + * It'll be deallocated in glb_rr_connectivity_changed() */ + rr_connectivity_data *rr_connectivity = + (rr_connectivity_data *)gpr_zalloc(sizeof(rr_connectivity_data)); + GRPC_CLOSURE_INIT(&rr_connectivity->on_change, + glb_rr_connectivity_changed_locked, rr_connectivity, + grpc_combiner_scheduler(glb_policy->base.combiner)); + rr_connectivity->glb_policy = glb_policy; + rr_connectivity->state = rr_state; + + /* Subscribe to changes to the connectivity of the new RR */ + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "glb_rr_connectivity_cb"); + grpc_lb_policy_notify_on_state_change_locked(exec_ctx, glb_policy->rr_policy, + &rr_connectivity->state, + &rr_connectivity->on_change); + grpc_lb_policy_exit_idle_locked(exec_ctx, glb_policy->rr_policy); + + /* Update picks and pings in wait */ + pending_pick *pp; + while ((pp = glb_policy->pending_picks)) { + glb_policy->pending_picks = pp->next; + GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_pick"); + pp->wrapped_on_complete_arg.rr_policy = glb_policy->rr_policy; + pp->wrapped_on_complete_arg.client_stats = + grpc_grpclb_client_stats_ref(glb_policy->client_stats); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Pending pick about to (async) PICK from %p", + (void *)glb_policy->rr_policy); + } + pick_from_internal_rr_locked(exec_ctx, glb_policy, &pp->pick_args, + true /* force_async */, pp->target, + &pp->wrapped_on_complete_arg); + } + + pending_ping *pping; + while ((pping = glb_policy->pending_pings)) { + glb_policy->pending_pings = pping->next; + GRPC_LB_POLICY_REF(glb_policy->rr_policy, "rr_handover_pending_ping"); + pping->wrapped_notify_arg.rr_policy = glb_policy->rr_policy; + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Pending ping about to PING from 0x%" PRIxPTR "", + (intptr_t)glb_policy->rr_policy); + } + grpc_lb_policy_ping_one_locked(exec_ctx, glb_policy->rr_policy, + &pping->wrapped_notify_arg.wrapper_closure); + } +} + +/* glb_policy->rr_policy may be NULL (initial handover) */ +static void rr_handover_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + if (glb_policy->shutting_down) return; + grpc_lb_policy_args *args = lb_policy_args_create(exec_ctx, glb_policy); + GPR_ASSERT(args != NULL); + if (glb_policy->rr_policy != NULL) { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_DEBUG, "Updating Round Robin policy (%p)", + (void *)glb_policy->rr_policy); + } + grpc_lb_policy_update_locked(exec_ctx, glb_policy->rr_policy, args); + } else { + create_rr_locked(exec_ctx, glb_policy, args); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_DEBUG, "Created new Round Robin policy (%p)", + (void *)glb_policy->rr_policy); + } + } + lb_policy_args_destroy(exec_ctx, args); +} + +static void glb_rr_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error) { + rr_connectivity_data *rr_connectivity = (rr_connectivity_data *)arg; + glb_lb_policy *glb_policy = rr_connectivity->glb_policy; + if (glb_policy->shutting_down) { + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "glb_rr_connectivity_cb"); + gpr_free(rr_connectivity); + return; + } + if (rr_connectivity->state == GRPC_CHANNEL_SHUTDOWN) { + /* An RR policy that has transitioned into the SHUTDOWN connectivity state + * should not be considered for picks or updates: the SHUTDOWN state is a + * sink, policies can't transition back from it. .*/ + GRPC_LB_POLICY_UNREF(exec_ctx, glb_policy->rr_policy, + "rr_connectivity_shutdown"); + glb_policy->rr_policy = NULL; + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "glb_rr_connectivity_cb"); + gpr_free(rr_connectivity); + return; + } + /* rr state != SHUTDOWN && !glb_policy->shutting down: biz as usual */ + update_lb_connectivity_status_locked( + exec_ctx, glb_policy, rr_connectivity->state, GRPC_ERROR_REF(error)); + /* Resubscribe. Reuse the "glb_rr_connectivity_cb" weak ref. */ + grpc_lb_policy_notify_on_state_change_locked(exec_ctx, glb_policy->rr_policy, + &rr_connectivity->state, + &rr_connectivity->on_change); +} + +static void destroy_balancer_name(grpc_exec_ctx *exec_ctx, + void *balancer_name) { + gpr_free(balancer_name); +} + +static grpc_slice_hash_table_entry targets_info_entry_create( + const char *address, const char *balancer_name) { + grpc_slice_hash_table_entry entry; + entry.key = grpc_slice_from_copied_string(address); + entry.value = gpr_strdup(balancer_name); + return entry; +} + +static int balancer_name_cmp_fn(void *a, void *b) { + const char *a_str = (const char *)a; + const char *b_str = (const char *)b; + return strcmp(a_str, b_str); +} + +/* Returns the channel args for the LB channel, used to create a bidirectional + * stream for the reception of load balancing updates. + * + * Inputs: + * - \a addresses: corresponding to the balancers. + * - \a response_generator: in order to propagate updates from the resolver + * above the grpclb policy. + * - \a args: other args inherited from the grpclb policy. */ +static grpc_channel_args *build_lb_channel_args( + grpc_exec_ctx *exec_ctx, const grpc_lb_addresses *addresses, + grpc_fake_resolver_response_generator *response_generator, + const grpc_channel_args *args) { + size_t num_grpclb_addrs = 0; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; + } + /* All input addresses come from a resolver that claims they are LB services. + * It's the resolver's responsibility to make sure this policy is only + * instantiated and used in that case. Otherwise, something has gone wrong. */ + GPR_ASSERT(num_grpclb_addrs > 0); + grpc_lb_addresses *lb_addresses = + grpc_lb_addresses_create(num_grpclb_addrs, NULL); + grpc_slice_hash_table_entry *targets_info_entries = + (grpc_slice_hash_table_entry *)gpr_zalloc(sizeof(*targets_info_entries) * + num_grpclb_addrs); + + size_t lb_addresses_idx = 0; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (!addresses->addresses[i].is_balancer) continue; + if (addresses->addresses[i].user_data != NULL) { + gpr_log(GPR_ERROR, + "This LB policy doesn't support user data. It will be ignored"); + } + char *addr_str; + GPR_ASSERT(grpc_sockaddr_to_string( + &addr_str, &addresses->addresses[i].address, true) > 0); + targets_info_entries[lb_addresses_idx] = targets_info_entry_create( + addr_str, addresses->addresses[i].balancer_name); + gpr_free(addr_str); + + grpc_lb_addresses_set_address( + lb_addresses, lb_addresses_idx++, addresses->addresses[i].address.addr, + addresses->addresses[i].address.len, false /* is balancer */, + addresses->addresses[i].balancer_name, NULL /* user data */); + } + GPR_ASSERT(num_grpclb_addrs == lb_addresses_idx); + grpc_slice_hash_table *targets_info = + grpc_slice_hash_table_create(num_grpclb_addrs, targets_info_entries, + destroy_balancer_name, balancer_name_cmp_fn); + gpr_free(targets_info_entries); + + grpc_channel_args *lb_channel_args = + grpc_lb_policy_grpclb_build_lb_channel_args(exec_ctx, targets_info, + response_generator, args); + + grpc_arg lb_channel_addresses_arg = + grpc_lb_addresses_create_channel_arg(lb_addresses); + + grpc_channel_args *result = grpc_channel_args_copy_and_add( + lb_channel_args, &lb_channel_addresses_arg, 1); + grpc_slice_hash_table_unref(exec_ctx, targets_info); + grpc_channel_args_destroy(exec_ctx, lb_channel_args); + grpc_lb_addresses_destroy(exec_ctx, lb_addresses); + return result; +} + +static void glb_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + GPR_ASSERT(glb_policy->pending_picks == NULL); + GPR_ASSERT(glb_policy->pending_pings == NULL); + gpr_free((void *)glb_policy->server_name); + grpc_channel_args_destroy(exec_ctx, glb_policy->args); + if (glb_policy->client_stats != NULL) { + grpc_grpclb_client_stats_unref(glb_policy->client_stats); + } + grpc_connectivity_state_destroy(exec_ctx, &glb_policy->state_tracker); + if (glb_policy->serverlist != NULL) { + grpc_grpclb_destroy_serverlist(glb_policy->serverlist); + } + if (glb_policy->fallback_backend_addresses != NULL) { + grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses); + } + grpc_fake_resolver_response_generator_unref(glb_policy->response_generator); + grpc_subchannel_index_unref(); + if (glb_policy->pending_update_args != NULL) { + grpc_channel_args_destroy(exec_ctx, glb_policy->pending_update_args->args); + gpr_free(glb_policy->pending_update_args); + } + gpr_free(glb_policy); +} + +static void glb_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + glb_policy->shutting_down = true; + + /* We need a copy of the lb_call pointer because we can't cancell the call + * while holding glb_policy->mu: lb_on_server_status_received, invoked due to + * the cancel, needs to acquire that same lock */ + grpc_call *lb_call = glb_policy->lb_call; + + /* glb_policy->lb_call and this local lb_call must be consistent at this point + * because glb_policy->lb_call is only assigned in lb_call_init_locked as part + * of query_for_backends_locked, which can only be invoked while + * glb_policy->shutting_down is false. */ + if (lb_call != NULL) { + grpc_call_cancel(lb_call, NULL); + /* lb_on_server_status_received will pick up the cancel and clean up */ + } + if (glb_policy->retry_timer_active) { + grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer); + glb_policy->retry_timer_active = false; + } + + pending_pick *pp = glb_policy->pending_picks; + glb_policy->pending_picks = NULL; + pending_ping *pping = glb_policy->pending_pings; + glb_policy->pending_pings = NULL; + if (glb_policy->rr_policy != NULL) { + GRPC_LB_POLICY_UNREF(exec_ctx, glb_policy->rr_policy, "glb_shutdown"); + } + // We destroy the LB channel here because + // glb_lb_channel_on_connectivity_changed_cb needs a valid glb_policy + // instance. Destroying the lb channel in glb_destroy would likely result in + // a callback invocation without a valid glb_policy arg. + if (glb_policy->lb_channel != NULL) { + grpc_channel_destroy(glb_policy->lb_channel); + glb_policy->lb_channel = NULL; + } + grpc_connectivity_state_set( + exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Shutdown"), "glb_shutdown"); + + while (pp != NULL) { + pending_pick *next = pp->next; + *pp->target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure, + GRPC_ERROR_NONE); + pp = next; + } + + while (pping != NULL) { + pending_ping *next = pping->next; + GRPC_CLOSURE_SCHED(exec_ctx, &pping->wrapped_notify_arg.wrapper_closure, + GRPC_ERROR_NONE); + pping = next; + } +} + +// Cancel a specific pending pick. +// +// A grpclb pick progresses as follows: +// - If there's a Round Robin policy (glb_policy->rr_policy) available, it'll be +// handed over to the RR policy (in create_rr_locked()). From that point +// onwards, it'll be RR's responsibility. For cancellations, that implies the +// pick needs also be cancelled by the RR instance. +// - Otherwise, without an RR instance, picks stay pending at this policy's +// level (grpclb), inside the glb_policy->pending_picks list. To cancel these, +// we invoke the completion closure and set *target to NULL right here. +static void glb_cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_connected_subchannel **target, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + pending_pick *pp = glb_policy->pending_picks; + glb_policy->pending_picks = NULL; + while (pp != NULL) { + pending_pick *next = pp->next; + if (pp->target == target) { + *target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = glb_policy->pending_picks; + glb_policy->pending_picks = pp; + } + pp = next; + } + if (glb_policy->rr_policy != NULL) { + grpc_lb_policy_cancel_pick_locked(exec_ctx, glb_policy->rr_policy, target, + GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +// Cancel all pending picks. +// +// A grpclb pick progresses as follows: +// - If there's a Round Robin policy (glb_policy->rr_policy) available, it'll be +// handed over to the RR policy (in create_rr_locked()). From that point +// onwards, it'll be RR's responsibility. For cancellations, that implies the +// pick needs also be cancelled by the RR instance. +// - Otherwise, without an RR instance, picks stay pending at this policy's +// level (grpclb), inside the glb_policy->pending_picks list. To cancel these, +// we invoke the completion closure and set *target to NULL right here. +static void glb_cancel_picks_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol, + uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + pending_pick *pp = glb_policy->pending_picks; + glb_policy->pending_picks = NULL; + while (pp != NULL) { + pending_pick *next = pp->next; + if ((pp->pick_args.initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + GRPC_CLOSURE_SCHED(exec_ctx, &pp->wrapped_on_complete_arg.wrapper_closure, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + } else { + pp->next = glb_policy->pending_picks; + glb_policy->pending_picks = pp; + } + pp = next; + } + if (glb_policy->rr_policy != NULL) { + grpc_lb_policy_cancel_picks_locked( + exec_ctx, glb_policy->rr_policy, initial_metadata_flags_mask, + initial_metadata_flags_eq, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +static void lb_on_fallback_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); +static void query_for_backends_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy); +static void start_picking_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + /* start a timer to fall back */ + if (glb_policy->lb_fallback_timeout_ms > 0 && + glb_policy->serverlist == NULL && !glb_policy->fallback_timer_active) { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec deadline = gpr_time_add( + now, + gpr_time_from_millis(glb_policy->lb_fallback_timeout_ms, GPR_TIMESPAN)); + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_fallback_timer"); + GRPC_CLOSURE_INIT(&glb_policy->lb_on_fallback, lb_on_fallback_timer_locked, + glb_policy, + grpc_combiner_scheduler(glb_policy->base.combiner)); + glb_policy->fallback_timer_active = true; + grpc_timer_init(exec_ctx, &glb_policy->lb_fallback_timer, deadline, + &glb_policy->lb_on_fallback, now); + } + + glb_policy->started_picking = true; + gpr_backoff_reset(&glb_policy->lb_call_backoff_state); + query_for_backends_locked(exec_ctx, glb_policy); +} + +static void glb_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + if (!glb_policy->started_picking) { + start_picking_locked(exec_ctx, glb_policy); + } +} + +static int glb_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + const grpc_lb_policy_pick_args *pick_args, + grpc_connected_subchannel **target, + grpc_call_context_element *context, void **user_data, + grpc_closure *on_complete) { + if (pick_args->lb_token_mdelem_storage == NULL) { + *target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, on_complete, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No mdelem storage for the LB token. Load reporting " + "won't work without it. Failing")); + return 0; + } + + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + bool pick_done; + + if (glb_policy->rr_policy != NULL) { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "grpclb %p about to PICK from RR %p", + (void *)glb_policy, (void *)glb_policy->rr_policy); + } + GRPC_LB_POLICY_REF(glb_policy->rr_policy, "glb_pick"); + + wrapped_rr_closure_arg *wc_arg = + (wrapped_rr_closure_arg *)gpr_zalloc(sizeof(wrapped_rr_closure_arg)); + + GRPC_CLOSURE_INIT(&wc_arg->wrapper_closure, wrapped_rr_closure, wc_arg, + grpc_schedule_on_exec_ctx); + wc_arg->rr_policy = glb_policy->rr_policy; + wc_arg->target = target; + wc_arg->context = context; + GPR_ASSERT(glb_policy->client_stats != NULL); + wc_arg->client_stats = + grpc_grpclb_client_stats_ref(glb_policy->client_stats); + wc_arg->wrapped_closure = on_complete; + wc_arg->lb_token_mdelem_storage = pick_args->lb_token_mdelem_storage; + wc_arg->initial_metadata = pick_args->initial_metadata; + wc_arg->free_when_done = wc_arg; + pick_done = + pick_from_internal_rr_locked(exec_ctx, glb_policy, pick_args, + false /* force_async */, target, wc_arg); + } else { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_DEBUG, + "No RR policy in grpclb instance %p. Adding to grpclb's pending " + "picks", + (void *)(glb_policy)); + } + add_pending_pick(&glb_policy->pending_picks, pick_args, target, context, + on_complete); + + if (!glb_policy->started_picking) { + start_picking_locked(exec_ctx, glb_policy); + } + pick_done = false; + } + return pick_done; +} + +static grpc_connectivity_state glb_check_connectivity_locked( + grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_error **connectivity_error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + return grpc_connectivity_state_get(&glb_policy->state_tracker, + connectivity_error); +} + +static void glb_ping_one_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_closure *closure) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + if (glb_policy->rr_policy) { + grpc_lb_policy_ping_one_locked(exec_ctx, glb_policy->rr_policy, closure); + } else { + add_pending_ping(&glb_policy->pending_pings, closure); + if (!glb_policy->started_picking) { + start_picking_locked(exec_ctx, glb_policy); + } + } +} + +static void glb_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_closure *notify) { + glb_lb_policy *glb_policy = (glb_lb_policy *)pol; + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &glb_policy->state_tracker, current, notify); +} + +static void lb_call_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + glb_policy->retry_timer_active = false; + if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Restaring call to LB server (grpclb %p)", + (void *)glb_policy); + } + GPR_ASSERT(glb_policy->lb_call == NULL); + query_for_backends_locked(exec_ctx, glb_policy); + } + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, "grpclb_retry_timer"); +} + +static void maybe_restart_lb_call(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + if (glb_policy->started_picking && glb_policy->updating_lb_call) { + if (glb_policy->retry_timer_active) { + grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer); + } + if (!glb_policy->shutting_down) start_picking_locked(exec_ctx, glb_policy); + glb_policy->updating_lb_call = false; + } else if (!glb_policy->shutting_down) { + /* if we aren't shutting down, restart the LB client call after some time */ + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec next_try = + gpr_backoff_step(&glb_policy->lb_call_backoff_state, now); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_DEBUG, "Connection to LB server lost (grpclb: %p)...", + (void *)glb_policy); + gpr_timespec timeout = gpr_time_sub(next_try, now); + if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { + gpr_log(GPR_DEBUG, + "... retry_timer_active in %" PRId64 ".%09d seconds.", + timeout.tv_sec, timeout.tv_nsec); + } else { + gpr_log(GPR_DEBUG, "... retry_timer_active immediately."); + } + } + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "grpclb_retry_timer"); + GRPC_CLOSURE_INIT(&glb_policy->lb_on_call_retry, + lb_call_on_retry_timer_locked, glb_policy, + grpc_combiner_scheduler(glb_policy->base.combiner)); + glb_policy->retry_timer_active = true; + grpc_timer_init(exec_ctx, &glb_policy->lb_call_retry_timer, next_try, + &glb_policy->lb_on_call_retry, now); + } + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "lb_on_server_status_received_locked"); +} + +static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); + +static void schedule_next_client_load_report(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + const gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + const gpr_timespec next_client_load_report_time = + gpr_time_add(now, glb_policy->client_stats_report_interval); + GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure, + send_client_load_report_locked, glb_policy, + grpc_combiner_scheduler(glb_policy->base.combiner)); + grpc_timer_init(exec_ctx, &glb_policy->client_load_report_timer, + next_client_load_report_time, + &glb_policy->client_load_report_closure, now); +} + +static void client_load_report_done_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + grpc_byte_buffer_destroy(glb_policy->client_load_report_payload); + glb_policy->client_load_report_payload = NULL; + if (error != GRPC_ERROR_NONE || glb_policy->lb_call == NULL) { + glb_policy->client_load_report_timer_pending = false; + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "client_load_report"); + return; + } + schedule_next_client_load_report(exec_ctx, glb_policy); +} + +static bool load_report_counters_are_zero(grpc_grpclb_request *request) { + grpc_grpclb_dropped_call_counts *drop_entries = + (grpc_grpclb_dropped_call_counts *) + request->client_stats.calls_finished_with_drop.arg; + return request->client_stats.num_calls_started == 0 && + request->client_stats.num_calls_finished == 0 && + request->client_stats.num_calls_finished_with_client_failed_to_send == + 0 && + request->client_stats.num_calls_finished_known_received == 0 && + (drop_entries == NULL || drop_entries->num_entries == 0); +} + +static void send_client_load_report_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + if (error == GRPC_ERROR_CANCELLED || glb_policy->lb_call == NULL) { + glb_policy->client_load_report_timer_pending = false; + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "client_load_report"); + if (glb_policy->lb_call == NULL) { + maybe_restart_lb_call(exec_ctx, glb_policy); + } + return; + } + // Construct message payload. + GPR_ASSERT(glb_policy->client_load_report_payload == NULL); + grpc_grpclb_request *request = + grpc_grpclb_load_report_request_create_locked(glb_policy->client_stats); + // Skip client load report if the counters were all zero in the last + // report and they are still zero in this one. + if (load_report_counters_are_zero(request)) { + if (glb_policy->last_client_load_report_counters_were_zero) { + grpc_grpclb_request_destroy(request); + schedule_next_client_load_report(exec_ctx, glb_policy); + return; + } + glb_policy->last_client_load_report_counters_were_zero = true; + } else { + glb_policy->last_client_load_report_counters_were_zero = false; + } + grpc_slice request_payload_slice = grpc_grpclb_request_encode(request); + glb_policy->client_load_report_payload = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + grpc_slice_unref_internal(exec_ctx, request_payload_slice); + grpc_grpclb_request_destroy(request); + // Send load report message. + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_SEND_MESSAGE; + op.data.send_message.send_message = glb_policy->client_load_report_payload; + GRPC_CLOSURE_INIT(&glb_policy->client_load_report_closure, + client_load_report_done_locked, glb_policy, + grpc_combiner_scheduler(glb_policy->base.combiner)); + grpc_call_error call_error = grpc_call_start_batch_and_execute( + exec_ctx, glb_policy->lb_call, &op, 1, + &glb_policy->client_load_report_closure); + if (call_error != GRPC_CALL_OK) { + gpr_log(GPR_ERROR, "call_error=%d", call_error); + GPR_ASSERT(GRPC_CALL_OK == call_error); + } +} + +static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error); +static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); +static void lb_call_init_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + GPR_ASSERT(glb_policy->server_name != NULL); + GPR_ASSERT(glb_policy->server_name[0] != '\0'); + GPR_ASSERT(glb_policy->lb_call == NULL); + GPR_ASSERT(!glb_policy->shutting_down); + + /* Note the following LB call progresses every time there's activity in \a + * glb_policy->base.interested_parties, which is comprised of the polling + * entities from \a client_channel. */ + grpc_slice host = grpc_slice_from_copied_string(glb_policy->server_name); + gpr_timespec deadline = + glb_policy->lb_call_timeout_ms == 0 + ? gpr_inf_future(GPR_CLOCK_MONOTONIC) + : gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + gpr_time_from_millis(glb_policy->lb_call_timeout_ms, + GPR_TIMESPAN)); + glb_policy->lb_call = grpc_channel_create_pollset_set_call( + exec_ctx, glb_policy->lb_channel, NULL, GRPC_PROPAGATE_DEFAULTS, + glb_policy->base.interested_parties, + GRPC_MDSTR_SLASH_GRPC_DOT_LB_DOT_V1_DOT_LOADBALANCER_SLASH_BALANCELOAD, + &host, deadline, NULL); + grpc_slice_unref_internal(exec_ctx, host); + + if (glb_policy->client_stats != NULL) { + grpc_grpclb_client_stats_unref(glb_policy->client_stats); + } + glb_policy->client_stats = grpc_grpclb_client_stats_create(); + + grpc_metadata_array_init(&glb_policy->lb_initial_metadata_recv); + grpc_metadata_array_init(&glb_policy->lb_trailing_metadata_recv); + + grpc_grpclb_request *request = + grpc_grpclb_request_create(glb_policy->server_name); + grpc_slice request_payload_slice = grpc_grpclb_request_encode(request); + glb_policy->lb_request_payload = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + grpc_slice_unref_internal(exec_ctx, request_payload_slice); + grpc_grpclb_request_destroy(request); + + GRPC_CLOSURE_INIT(&glb_policy->lb_on_server_status_received, + lb_on_server_status_received_locked, glb_policy, + grpc_combiner_scheduler(glb_policy->base.combiner)); + GRPC_CLOSURE_INIT(&glb_policy->lb_on_response_received, + lb_on_response_received_locked, glb_policy, + grpc_combiner_scheduler(glb_policy->base.combiner)); + + gpr_backoff_init(&glb_policy->lb_call_backoff_state, + GRPC_GRPCLB_INITIAL_CONNECT_BACKOFF_SECONDS, + GRPC_GRPCLB_RECONNECT_BACKOFF_MULTIPLIER, + GRPC_GRPCLB_RECONNECT_JITTER, + GRPC_GRPCLB_MIN_CONNECT_TIMEOUT_SECONDS * 1000, + GRPC_GRPCLB_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + + glb_policy->seen_initial_response = false; + glb_policy->last_client_load_report_counters_were_zero = false; +} + +static void lb_call_destroy_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + GPR_ASSERT(glb_policy->lb_call != NULL); + grpc_call_unref(glb_policy->lb_call); + glb_policy->lb_call = NULL; + + grpc_metadata_array_destroy(&glb_policy->lb_initial_metadata_recv); + grpc_metadata_array_destroy(&glb_policy->lb_trailing_metadata_recv); + + grpc_byte_buffer_destroy(glb_policy->lb_request_payload); + grpc_slice_unref_internal(exec_ctx, glb_policy->lb_call_status_details); + + if (glb_policy->client_load_report_timer_pending) { + grpc_timer_cancel(exec_ctx, &glb_policy->client_load_report_timer); + } +} + +/* + * Auxiliary functions and LB client callbacks. + */ +static void query_for_backends_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy) { + GPR_ASSERT(glb_policy->lb_channel != NULL); + if (glb_policy->shutting_down) return; + + lb_call_init_locked(exec_ctx, glb_policy); + + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, + "Query for backends (grpclb: %p, lb_channel: %p, lb_call: %p)", + (void *)glb_policy, (void *)glb_policy->lb_channel, + (void *)glb_policy->lb_call); + } + GPR_ASSERT(glb_policy->lb_call != NULL); + + grpc_call_error call_error; + grpc_op ops[3]; + memset(ops, 0, sizeof(ops)); + + grpc_op *op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = 0; + op->reserved = NULL; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &glb_policy->lb_initial_metadata_recv; + op->flags = 0; + op->reserved = NULL; + op++; + GPR_ASSERT(glb_policy->lb_request_payload != NULL); + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message.send_message = glb_policy->lb_request_payload; + op->flags = 0; + op->reserved = NULL; + op++; + call_error = grpc_call_start_batch_and_execute(exec_ctx, glb_policy->lb_call, + ops, (size_t)(op - ops), NULL); + GPR_ASSERT(GRPC_CALL_OK == call_error); + + op = ops; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = + &glb_policy->lb_trailing_metadata_recv; + op->data.recv_status_on_client.status = &glb_policy->lb_call_status; + op->data.recv_status_on_client.status_details = + &glb_policy->lb_call_status_details; + op->flags = 0; + op->reserved = NULL; + op++; + /* take a weak ref (won't prevent calling of \a glb_shutdown if the strong ref + * count goes to zero) to be unref'd in lb_on_server_status_received_locked */ + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, + "lb_on_server_status_received_locked"); + call_error = grpc_call_start_batch_and_execute( + exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops), + &glb_policy->lb_on_server_status_received); + GPR_ASSERT(GRPC_CALL_OK == call_error); + + op = ops; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &glb_policy->lb_response_payload; + op->flags = 0; + op->reserved = NULL; + op++; + /* take another weak ref to be unref'd/reused in + * lb_on_response_received_locked */ + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "lb_on_response_received_locked"); + call_error = grpc_call_start_batch_and_execute( + exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops), + &glb_policy->lb_on_response_received); + GPR_ASSERT(GRPC_CALL_OK == call_error); +} + +static void lb_on_response_received_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + grpc_op ops[2]; + memset(ops, 0, sizeof(ops)); + grpc_op *op = ops; + if (glb_policy->lb_response_payload != NULL) { + gpr_backoff_reset(&glb_policy->lb_call_backoff_state); + /* Received data from the LB server. Look inside + * glb_policy->lb_response_payload, for a serverlist. */ + grpc_byte_buffer_reader bbr; + grpc_byte_buffer_reader_init(&bbr, glb_policy->lb_response_payload); + grpc_slice response_slice = grpc_byte_buffer_reader_readall(&bbr); + grpc_byte_buffer_reader_destroy(&bbr); + grpc_byte_buffer_destroy(glb_policy->lb_response_payload); + + grpc_grpclb_initial_response *response = NULL; + if (!glb_policy->seen_initial_response && + (response = grpc_grpclb_initial_response_parse(response_slice)) != + NULL) { + if (response->has_client_stats_report_interval) { + glb_policy->client_stats_report_interval = + gpr_time_max(gpr_time_from_seconds(1, GPR_TIMESPAN), + grpc_grpclb_duration_to_timespec( + &response->client_stats_report_interval)); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, + "received initial LB response message; " + "client load reporting interval = %" PRId64 ".%09d sec", + glb_policy->client_stats_report_interval.tv_sec, + glb_policy->client_stats_report_interval.tv_nsec); + } + /* take a weak ref (won't prevent calling of \a glb_shutdown() if the + * strong ref count goes to zero) to be unref'd in + * send_client_load_report_locked() */ + glb_policy->client_load_report_timer_pending = true; + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "client_load_report"); + schedule_next_client_load_report(exec_ctx, glb_policy); + } else if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, + "received initial LB response message; " + "client load reporting NOT enabled"); + } + grpc_grpclb_initial_response_destroy(response); + glb_policy->seen_initial_response = true; + } else { + grpc_grpclb_serverlist *serverlist = + grpc_grpclb_response_parse_serverlist(response_slice); + if (serverlist != NULL) { + GPR_ASSERT(glb_policy->lb_call != NULL); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Serverlist with %lu servers received", + (unsigned long)serverlist->num_servers); + for (size_t i = 0; i < serverlist->num_servers; ++i) { + grpc_resolved_address addr; + parse_server(serverlist->servers[i], &addr); + char *ipport; + grpc_sockaddr_to_string(&ipport, &addr, false); + gpr_log(GPR_INFO, "Serverlist[%lu]: %s", (unsigned long)i, ipport); + gpr_free(ipport); + } + } + /* update serverlist */ + if (serverlist->num_servers > 0) { + if (grpc_grpclb_serverlist_equals(glb_policy->serverlist, + serverlist)) { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, + "Incoming server list identical to current, ignoring."); + } + grpc_grpclb_destroy_serverlist(serverlist); + } else { /* new serverlist */ + if (glb_policy->serverlist != NULL) { + /* dispose of the old serverlist */ + grpc_grpclb_destroy_serverlist(glb_policy->serverlist); + } else { + /* or dispose of the fallback */ + grpc_lb_addresses_destroy(exec_ctx, + glb_policy->fallback_backend_addresses); + glb_policy->fallback_backend_addresses = NULL; + if (glb_policy->fallback_timer_active) { + grpc_timer_cancel(exec_ctx, &glb_policy->lb_fallback_timer); + glb_policy->fallback_timer_active = false; + } + } + /* and update the copy in the glb_lb_policy instance. This + * serverlist instance will be destroyed either upon the next + * update or in glb_destroy() */ + glb_policy->serverlist = serverlist; + glb_policy->serverlist_index = 0; + rr_handover_locked(exec_ctx, glb_policy); + } + } else { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Received empty server list, ignoring."); + } + grpc_grpclb_destroy_serverlist(serverlist); + } + } else { /* serverlist == NULL */ + gpr_log(GPR_ERROR, "Invalid LB response received: '%s'. Ignoring.", + grpc_dump_slice(response_slice, GPR_DUMP_ASCII | GPR_DUMP_HEX)); + } + } + grpc_slice_unref_internal(exec_ctx, response_slice); + if (!glb_policy->shutting_down) { + /* keep listening for serverlist updates */ + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &glb_policy->lb_response_payload; + op->flags = 0; + op->reserved = NULL; + op++; + /* reuse the "lb_on_response_received_locked" weak ref taken in + * query_for_backends_locked() */ + const grpc_call_error call_error = grpc_call_start_batch_and_execute( + exec_ctx, glb_policy->lb_call, ops, (size_t)(op - ops), + &glb_policy->lb_on_response_received); /* loop */ + GPR_ASSERT(GRPC_CALL_OK == call_error); + } else { + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "lb_on_response_received_locked_shutdown"); + } + } else { /* empty payload: call cancelled. */ + /* dispose of the "lb_on_response_received_locked" weak ref taken in + * query_for_backends_locked() and reused in every reception loop */ + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "lb_on_response_received_locked_empty_payload"); + } +} + +static void lb_on_fallback_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + glb_policy->fallback_timer_active = false; + /* If we receive a serverlist after the timer fires but before this callback + * actually runs, don't fall back. */ + if (glb_policy->serverlist == NULL) { + if (!glb_policy->shutting_down && error == GRPC_ERROR_NONE) { + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, + "Falling back to use backends from resolver (grpclb %p)", + (void *)glb_policy); + } + GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL); + rr_handover_locked(exec_ctx, glb_policy); + } + } + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "grpclb_fallback_timer"); +} + +static void lb_on_server_status_received_locked(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + GPR_ASSERT(glb_policy->lb_call != NULL); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + char *status_details = + grpc_slice_to_c_string(glb_policy->lb_call_status_details); + gpr_log(GPR_INFO, + "Status from LB server received. Status = %d, Details = '%s', " + "(call: %p), error %p", + glb_policy->lb_call_status, status_details, + (void *)glb_policy->lb_call, (void *)error); + gpr_free(status_details); + } + /* We need to perform cleanups no matter what. */ + lb_call_destroy_locked(exec_ctx, glb_policy); + // If the load report timer is still pending, we wait for it to be + // called before restarting the call. Otherwise, we restart the call + // here. + if (!glb_policy->client_load_report_timer_pending) { + maybe_restart_lb_call(exec_ctx, glb_policy); + } +} + +static void fallback_update_locked(grpc_exec_ctx *exec_ctx, + glb_lb_policy *glb_policy, + const grpc_lb_addresses *addresses) { + GPR_ASSERT(glb_policy->fallback_backend_addresses != NULL); + grpc_lb_addresses_destroy(exec_ctx, glb_policy->fallback_backend_addresses); + glb_policy->fallback_backend_addresses = + extract_backend_addresses_locked(exec_ctx, addresses); + if (glb_policy->lb_fallback_timeout_ms > 0 && + !glb_policy->fallback_timer_active) { + rr_handover_locked(exec_ctx, glb_policy); + } +} + +static void glb_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + const grpc_lb_policy_args *args) { + glb_lb_policy *glb_policy = (glb_lb_policy *)policy; + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + if (arg == NULL || arg->type != GRPC_ARG_POINTER) { + if (glb_policy->lb_channel == NULL) { + // If we don't have a current channel to the LB, go into TRANSIENT + // FAILURE. + grpc_connectivity_state_set( + exec_ctx, &glb_policy->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), + "glb_update_missing"); + } else { + // otherwise, keep using the current LB channel (ignore this update). + gpr_log(GPR_ERROR, + "No valid LB addresses channel arg for grpclb %p update, " + "ignoring.", + (void *)glb_policy); + } + return; + } + const grpc_lb_addresses *addresses = + (const grpc_lb_addresses *)arg->value.pointer.p; + + if (glb_policy->serverlist == NULL) { + // If a non-empty serverlist hasn't been received from the balancer, + // propagate the update to fallback_backend_addresses. + fallback_update_locked(exec_ctx, glb_policy, addresses); + } else if (glb_policy->updating_lb_channel) { + // If we have recieved serverlist from the balancer, we need to defer update + // when there is an in-progress one. + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, + "Update already in progress for grpclb %p. Deferring update.", + (void *)glb_policy); + } + if (glb_policy->pending_update_args != NULL) { + grpc_channel_args_destroy(exec_ctx, + glb_policy->pending_update_args->args); + gpr_free(glb_policy->pending_update_args); + } + glb_policy->pending_update_args = (grpc_lb_policy_args *)gpr_zalloc( + sizeof(*glb_policy->pending_update_args)); + glb_policy->pending_update_args->client_channel_factory = + args->client_channel_factory; + glb_policy->pending_update_args->args = grpc_channel_args_copy(args->args); + glb_policy->pending_update_args->combiner = args->combiner; + return; + } + + glb_policy->updating_lb_channel = true; + GPR_ASSERT(glb_policy->lb_channel != NULL); + grpc_channel_args *lb_channel_args = build_lb_channel_args( + exec_ctx, addresses, glb_policy->response_generator, args->args); + /* Propagate updates to the LB channel (pick first) through the fake resolver + */ + grpc_fake_resolver_response_generator_set_response( + exec_ctx, glb_policy->response_generator, lb_channel_args); + grpc_channel_args_destroy(exec_ctx, lb_channel_args); + + if (!glb_policy->watching_lb_channel) { + // Watch the LB channel connectivity for connection. + glb_policy->lb_channel_connectivity = grpc_channel_check_connectivity_state( + glb_policy->lb_channel, true /* try to connect */); + grpc_channel_element *client_channel_elem = grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(glb_policy->lb_channel)); + GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); + glb_policy->watching_lb_channel = true; + GRPC_LB_POLICY_WEAK_REF(&glb_policy->base, "watch_lb_channel_connectivity"); + grpc_client_channel_watch_connectivity_state( + exec_ctx, client_channel_elem, + grpc_polling_entity_create_from_pollset_set( + glb_policy->base.interested_parties), + &glb_policy->lb_channel_connectivity, + &glb_policy->lb_channel_on_connectivity_changed, NULL); + } +} + +// Invoked as part of the update process. It continues watching the LB channel +// until it shuts down or becomes READY. It's invoked even if the LB channel +// stayed READY throughout the update (for example if the update is identical). +static void glb_lb_channel_on_connectivity_changed_cb(grpc_exec_ctx *exec_ctx, + void *arg, + grpc_error *error) { + glb_lb_policy *glb_policy = (glb_lb_policy *)arg; + if (glb_policy->shutting_down) goto done; + // Re-initialize the lb_call. This should also take care of updating the + // embedded RR policy. Note that the current RR policy, if any, will stay in + // effect until an update from the new lb_call is received. + switch (glb_policy->lb_channel_connectivity) { + case GRPC_CHANNEL_INIT: + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_TRANSIENT_FAILURE: { + /* resub. */ + grpc_channel_element *client_channel_elem = + grpc_channel_stack_last_element( + grpc_channel_get_channel_stack(glb_policy->lb_channel)); + GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter); + grpc_client_channel_watch_connectivity_state( + exec_ctx, client_channel_elem, + grpc_polling_entity_create_from_pollset_set( + glb_policy->base.interested_parties), + &glb_policy->lb_channel_connectivity, + &glb_policy->lb_channel_on_connectivity_changed, NULL); + break; + } + case GRPC_CHANNEL_IDLE: + // lb channel inactive (probably shutdown prior to update). Restart lb + // call to kick the lb channel into gear. + GPR_ASSERT(glb_policy->lb_call == NULL); + /* fallthrough */ + case GRPC_CHANNEL_READY: + if (glb_policy->lb_call != NULL) { + glb_policy->updating_lb_channel = false; + glb_policy->updating_lb_call = true; + grpc_call_cancel(glb_policy->lb_call, NULL); + // lb_on_server_status_received will pick up the cancel and reinit + // lb_call. + if (glb_policy->pending_update_args != NULL) { + grpc_lb_policy_args *args = glb_policy->pending_update_args; + glb_policy->pending_update_args = NULL; + glb_update_locked(exec_ctx, &glb_policy->base, args); + grpc_channel_args_destroy(exec_ctx, args->args); + gpr_free(args); + } + } else if (glb_policy->started_picking && !glb_policy->shutting_down) { + if (glb_policy->retry_timer_active) { + grpc_timer_cancel(exec_ctx, &glb_policy->lb_call_retry_timer); + glb_policy->retry_timer_active = false; + } + start_picking_locked(exec_ctx, glb_policy); + } + /* fallthrough */ + case GRPC_CHANNEL_SHUTDOWN: + done: + glb_policy->watching_lb_channel = false; + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &glb_policy->base, + "watch_lb_channel_connectivity_cb_shutdown"); + break; + } +} + +/* Code wiring the policy with the rest of the core */ +static const grpc_lb_policy_vtable glb_lb_policy_vtable = { + glb_destroy, + glb_shutdown_locked, + glb_pick_locked, + glb_cancel_pick_locked, + glb_cancel_picks_locked, + glb_ping_one_locked, + glb_exit_idle_locked, + glb_check_connectivity_locked, + glb_notify_on_state_change_locked, + glb_update_locked}; + +static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, + grpc_lb_policy_factory *factory, + grpc_lb_policy_args *args) { + /* Count the number of gRPC-LB addresses. There must be at least one. */ + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + if (arg == NULL || arg->type != GRPC_ARG_POINTER) { + return NULL; + } + grpc_lb_addresses *addresses = (grpc_lb_addresses *)arg->value.pointer.p; + size_t num_grpclb_addrs = 0; + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (addresses->addresses[i].is_balancer) ++num_grpclb_addrs; + } + if (num_grpclb_addrs == 0) return NULL; + + glb_lb_policy *glb_policy = (glb_lb_policy *)gpr_zalloc(sizeof(*glb_policy)); + + /* Get server name. */ + arg = grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI); + GPR_ASSERT(arg != NULL); + GPR_ASSERT(arg->type == GRPC_ARG_STRING); + grpc_uri *uri = grpc_uri_parse(exec_ctx, arg->value.string, true); + GPR_ASSERT(uri->path[0] != '\0'); + glb_policy->server_name = + gpr_strdup(uri->path[0] == '/' ? uri->path + 1 : uri->path); + if (GRPC_TRACER_ON(grpc_lb_glb_trace)) { + gpr_log(GPR_INFO, "Will use '%s' as the server name for LB request.", + glb_policy->server_name); + } + grpc_uri_destroy(uri); + + glb_policy->cc_factory = args->client_channel_factory; + GPR_ASSERT(glb_policy->cc_factory != NULL); + + arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS); + glb_policy->lb_call_timeout_ms = + grpc_channel_arg_get_integer(arg, (grpc_integer_options){0, 0, INT_MAX}); + + arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS); + glb_policy->lb_fallback_timeout_ms = grpc_channel_arg_get_integer( + arg, (grpc_integer_options){GRPC_GRPCLB_DEFAULT_FALLBACK_TIMEOUT_MS, 0, + INT_MAX}); + + // Make sure that GRPC_ARG_LB_POLICY_NAME is set in channel args, + // since we use this to trigger the client_load_reporting filter. + grpc_arg new_arg = grpc_channel_arg_string_create( + (char *)GRPC_ARG_LB_POLICY_NAME, (char *)"grpclb"); + static const char *args_to_remove[] = {GRPC_ARG_LB_POLICY_NAME}; + glb_policy->args = grpc_channel_args_copy_and_add_and_remove( + args->args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove), &new_arg, 1); + + /* Extract the backend addresses (may be empty) from the resolver for + * fallback. */ + glb_policy->fallback_backend_addresses = + extract_backend_addresses_locked(exec_ctx, addresses); + + /* Create a client channel over them to communicate with a LB service */ + glb_policy->response_generator = + grpc_fake_resolver_response_generator_create(); + grpc_channel_args *lb_channel_args = build_lb_channel_args( + exec_ctx, addresses, glb_policy->response_generator, args->args); + char *uri_str; + gpr_asprintf(&uri_str, "fake:///%s", glb_policy->server_name); + glb_policy->lb_channel = grpc_lb_policy_grpclb_create_lb_channel( + exec_ctx, uri_str, args->client_channel_factory, lb_channel_args); + + /* Propagate initial resolution */ + grpc_fake_resolver_response_generator_set_response( + exec_ctx, glb_policy->response_generator, lb_channel_args); + grpc_channel_args_destroy(exec_ctx, lb_channel_args); + gpr_free(uri_str); + if (glb_policy->lb_channel == NULL) { + gpr_free((void *)glb_policy->server_name); + grpc_channel_args_destroy(exec_ctx, glb_policy->args); + gpr_free(glb_policy); + return NULL; + } + grpc_subchannel_index_ref(); + GRPC_CLOSURE_INIT(&glb_policy->lb_channel_on_connectivity_changed, + glb_lb_channel_on_connectivity_changed_cb, glb_policy, + grpc_combiner_scheduler(args->combiner)); + grpc_lb_policy_init(&glb_policy->base, &glb_lb_policy_vtable, args->combiner); + grpc_connectivity_state_init(&glb_policy->state_tracker, GRPC_CHANNEL_IDLE, + "grpclb"); + return &glb_policy->base; +} + +static void glb_factory_ref(grpc_lb_policy_factory *factory) {} + +static void glb_factory_unref(grpc_lb_policy_factory *factory) {} + +static const grpc_lb_policy_factory_vtable glb_factory_vtable = { + glb_factory_ref, glb_factory_unref, glb_create, "grpclb"}; + +static grpc_lb_policy_factory glb_lb_policy_factory = {&glb_factory_vtable}; + +grpc_lb_policy_factory *grpc_glb_lb_factory_create() { + return &glb_lb_policy_factory; +} + +/* Plugin registration */ + +// Only add client_load_reporting filter if the grpclb LB policy is used. +static bool maybe_add_client_load_reporting_filter( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { + const grpc_channel_args *args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_arg *channel_arg = + grpc_channel_args_find(args, GRPC_ARG_LB_POLICY_NAME); + if (channel_arg != NULL && channel_arg->type == GRPC_ARG_STRING && + strcmp(channel_arg->value.string, "grpclb") == 0) { + return grpc_channel_stack_builder_append_filter( + builder, (const grpc_channel_filter *)arg, NULL, NULL); + } + return true; +} + +extern "C" void grpc_lb_policy_grpclb_init() { + grpc_register_lb_policy(grpc_glb_lb_factory_create()); + grpc_register_tracer(&grpc_lb_glb_trace); +#ifndef NDEBUG + grpc_register_tracer(&grpc_trace_lb_policy_refcount); +#endif + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_client_load_reporting_filter, + (void *)&grpc_client_load_reporting_filter); +} + +extern "C" void grpc_lb_policy_grpclb_shutdown() {} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c deleted file mode 100644 index f2967182e2..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/string.h" - -grpc_channel *grpc_lb_policy_grpclb_create_lb_channel( - grpc_exec_ctx *exec_ctx, const char *lb_service_target_addresses, - grpc_client_channel_factory *client_channel_factory, - grpc_channel_args *args) { - grpc_channel *lb_channel = grpc_client_channel_factory_create_channel( - exec_ctx, client_channel_factory, lb_service_target_addresses, - GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, args); - return lb_channel; -} - -grpc_channel_args *grpc_lb_policy_grpclb_build_lb_channel_args( - grpc_exec_ctx *exec_ctx, grpc_slice_hash_table *targets_info, - grpc_fake_resolver_response_generator *response_generator, - const grpc_channel_args *args) { - const grpc_arg to_add[] = { - grpc_fake_resolver_response_generator_arg(response_generator)}; - /* We remove: - * - * - The channel arg for the LB policy name, since we want to use the default - * (pick_first) in this case. - * - * - The channel arg for the resolved addresses, since that will be generated - * by the name resolver used in the LB channel. Note that the LB channel - * will use the fake resolver, so this won't actually generate a query - * to DNS (or some other name service). However, the addresses returned by - * the fake resolver will have is_balancer=false, whereas our own - * addresses have is_balancer=true. We need the LB channel to return - * addresses with is_balancer=false so that it does not wind up recursively - * using the grpclb LB policy, as per the special case logic in - * client_channel.c. - * - * - The channel arg for the server URI, since that will be different for the - * LB channel than for the parent channel (the client channel factory will - * re-add this arg with the right value). - * - * - The fake resolver generator, because we are replacing it with the one - * from the grpclb policy, used to propagate updates to the LB channel. */ - static const char *keys_to_remove[] = { - GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI, - GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR}; - return grpc_channel_args_copy_and_add_and_remove( - args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), to_add, - GPR_ARRAY_SIZE(to_add)); -} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc new file mode 100644 index 0000000000..f2967182e2 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc @@ -0,0 +1,71 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/string.h" + +grpc_channel *grpc_lb_policy_grpclb_create_lb_channel( + grpc_exec_ctx *exec_ctx, const char *lb_service_target_addresses, + grpc_client_channel_factory *client_channel_factory, + grpc_channel_args *args) { + grpc_channel *lb_channel = grpc_client_channel_factory_create_channel( + exec_ctx, client_channel_factory, lb_service_target_addresses, + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, args); + return lb_channel; +} + +grpc_channel_args *grpc_lb_policy_grpclb_build_lb_channel_args( + grpc_exec_ctx *exec_ctx, grpc_slice_hash_table *targets_info, + grpc_fake_resolver_response_generator *response_generator, + const grpc_channel_args *args) { + const grpc_arg to_add[] = { + grpc_fake_resolver_response_generator_arg(response_generator)}; + /* We remove: + * + * - The channel arg for the LB policy name, since we want to use the default + * (pick_first) in this case. + * + * - The channel arg for the resolved addresses, since that will be generated + * by the name resolver used in the LB channel. Note that the LB channel + * will use the fake resolver, so this won't actually generate a query + * to DNS (or some other name service). However, the addresses returned by + * the fake resolver will have is_balancer=false, whereas our own + * addresses have is_balancer=true. We need the LB channel to return + * addresses with is_balancer=false so that it does not wind up recursively + * using the grpclb LB policy, as per the special case logic in + * client_channel.c. + * + * - The channel arg for the server URI, since that will be different for the + * LB channel than for the parent channel (the client channel factory will + * re-add this arg with the right value). + * + * - The fake resolver generator, because we are replacing it with the one + * from the grpclb policy, used to propagate updates to the LB channel. */ + static const char *keys_to_remove[] = { + GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI, + GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR}; + return grpc_channel_args_copy_and_add_and_remove( + args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), to_add, + GPR_ARRAY_SIZE(to_add)); +} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c deleted file mode 100644 index 2681b2a079..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/transport/lb_targets_info.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" - -grpc_channel *grpc_lb_policy_grpclb_create_lb_channel( - grpc_exec_ctx *exec_ctx, const char *lb_service_target_addresses, - grpc_client_channel_factory *client_channel_factory, - grpc_channel_args *args) { - grpc_channel_args *new_args = args; - grpc_channel_credentials *channel_credentials = - grpc_channel_credentials_find_in_args(args); - if (channel_credentials != NULL) { - /* Substitute the channel credentials with a version without call - * credentials: the load balancer is not necessarily trusted to handle - * bearer token credentials */ - static const char *keys_to_remove[] = {GRPC_ARG_CHANNEL_CREDENTIALS}; - grpc_channel_credentials *creds_sans_call_creds = - grpc_channel_credentials_duplicate_without_call_credentials( - channel_credentials); - GPR_ASSERT(creds_sans_call_creds != NULL); - grpc_arg args_to_add[] = { - grpc_channel_credentials_to_arg(creds_sans_call_creds)}; - /* Create the new set of channel args */ - new_args = grpc_channel_args_copy_and_add_and_remove( - args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add, - GPR_ARRAY_SIZE(args_to_add)); - grpc_channel_credentials_unref(exec_ctx, creds_sans_call_creds); - } - grpc_channel *lb_channel = grpc_client_channel_factory_create_channel( - exec_ctx, client_channel_factory, lb_service_target_addresses, - GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, new_args); - if (channel_credentials != NULL) { - grpc_channel_args_destroy(exec_ctx, new_args); - } - return lb_channel; -} - -grpc_channel_args *grpc_lb_policy_grpclb_build_lb_channel_args( - grpc_exec_ctx *exec_ctx, grpc_slice_hash_table *targets_info, - grpc_fake_resolver_response_generator *response_generator, - const grpc_channel_args *args) { - const grpc_arg to_add[] = { - grpc_lb_targets_info_create_channel_arg(targets_info), - grpc_fake_resolver_response_generator_arg(response_generator)}; - /* We remove: - * - * - The channel arg for the LB policy name, since we want to use the default - * (pick_first) in this case. - * - * - The channel arg for the resolved addresses, since that will be generated - * by the name resolver used in the LB channel. Note that the LB channel - * will use the fake resolver, so this won't actually generate a query - * to DNS (or some other name service). However, the addresses returned by - * the fake resolver will have is_balancer=false, whereas our own - * addresses have is_balancer=true. We need the LB channel to return - * addresses with is_balancer=false so that it does not wind up recursively - * using the grpclb LB policy, as per the special case logic in - * client_channel.c. - * - * - The channel arg for the server URI, since that will be different for the - * LB channel than for the parent channel (the client channel factory will - * re-add this arg with the right value). - * - * - The fake resolver generator, because we are replacing it with the one - * from the grpclb policy, used to propagate updates to the LB channel. */ - static const char *keys_to_remove[] = { - GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI, - GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR}; - /* Add the targets info table to be used for secure naming */ - return grpc_channel_args_copy_and_add_and_remove( - args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), to_add, - GPR_ARRAY_SIZE(to_add)); -} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc new file mode 100644 index 0000000000..2681b2a079 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc @@ -0,0 +1,99 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/lb_targets_info.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" + +grpc_channel *grpc_lb_policy_grpclb_create_lb_channel( + grpc_exec_ctx *exec_ctx, const char *lb_service_target_addresses, + grpc_client_channel_factory *client_channel_factory, + grpc_channel_args *args) { + grpc_channel_args *new_args = args; + grpc_channel_credentials *channel_credentials = + grpc_channel_credentials_find_in_args(args); + if (channel_credentials != NULL) { + /* Substitute the channel credentials with a version without call + * credentials: the load balancer is not necessarily trusted to handle + * bearer token credentials */ + static const char *keys_to_remove[] = {GRPC_ARG_CHANNEL_CREDENTIALS}; + grpc_channel_credentials *creds_sans_call_creds = + grpc_channel_credentials_duplicate_without_call_credentials( + channel_credentials); + GPR_ASSERT(creds_sans_call_creds != NULL); + grpc_arg args_to_add[] = { + grpc_channel_credentials_to_arg(creds_sans_call_creds)}; + /* Create the new set of channel args */ + new_args = grpc_channel_args_copy_and_add_and_remove( + args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), args_to_add, + GPR_ARRAY_SIZE(args_to_add)); + grpc_channel_credentials_unref(exec_ctx, creds_sans_call_creds); + } + grpc_channel *lb_channel = grpc_client_channel_factory_create_channel( + exec_ctx, client_channel_factory, lb_service_target_addresses, + GRPC_CLIENT_CHANNEL_TYPE_LOAD_BALANCING, new_args); + if (channel_credentials != NULL) { + grpc_channel_args_destroy(exec_ctx, new_args); + } + return lb_channel; +} + +grpc_channel_args *grpc_lb_policy_grpclb_build_lb_channel_args( + grpc_exec_ctx *exec_ctx, grpc_slice_hash_table *targets_info, + grpc_fake_resolver_response_generator *response_generator, + const grpc_channel_args *args) { + const grpc_arg to_add[] = { + grpc_lb_targets_info_create_channel_arg(targets_info), + grpc_fake_resolver_response_generator_arg(response_generator)}; + /* We remove: + * + * - The channel arg for the LB policy name, since we want to use the default + * (pick_first) in this case. + * + * - The channel arg for the resolved addresses, since that will be generated + * by the name resolver used in the LB channel. Note that the LB channel + * will use the fake resolver, so this won't actually generate a query + * to DNS (or some other name service). However, the addresses returned by + * the fake resolver will have is_balancer=false, whereas our own + * addresses have is_balancer=true. We need the LB channel to return + * addresses with is_balancer=false so that it does not wind up recursively + * using the grpclb LB policy, as per the special case logic in + * client_channel.c. + * + * - The channel arg for the server URI, since that will be different for the + * LB channel than for the parent channel (the client channel factory will + * re-add this arg with the right value). + * + * - The fake resolver generator, because we are replacing it with the one + * from the grpclb policy, used to propagate updates to the LB channel. */ + static const char *keys_to_remove[] = { + GRPC_ARG_LB_POLICY_NAME, GRPC_ARG_LB_ADDRESSES, GRPC_ARG_SERVER_URI, + GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR}; + /* Add the targets info table to be used for secure naming */ + return grpc_channel_args_copy_and_add_and_remove( + args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), to_add, + GPR_ARRAY_SIZE(to_add)); +} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c deleted file mode 100644 index 903120ca7d..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" - -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" - -#define GRPC_ARG_GRPCLB_CLIENT_STATS "grpc.grpclb_client_stats" - -struct grpc_grpclb_client_stats { - gpr_refcount refs; - // This field must only be accessed via *_locked() methods. - grpc_grpclb_dropped_call_counts* drop_token_counts; - // These fields may be accessed from multiple threads at a time. - gpr_atm num_calls_started; - gpr_atm num_calls_finished; - gpr_atm num_calls_finished_with_client_failed_to_send; - gpr_atm num_calls_finished_known_received; -}; - -grpc_grpclb_client_stats* grpc_grpclb_client_stats_create() { - grpc_grpclb_client_stats* client_stats = - (grpc_grpclb_client_stats*)gpr_zalloc(sizeof(*client_stats)); - gpr_ref_init(&client_stats->refs, 1); - return client_stats; -} - -grpc_grpclb_client_stats* grpc_grpclb_client_stats_ref( - grpc_grpclb_client_stats* client_stats) { - gpr_ref(&client_stats->refs); - return client_stats; -} - -void grpc_grpclb_client_stats_unref(grpc_grpclb_client_stats* client_stats) { - if (gpr_unref(&client_stats->refs)) { - grpc_grpclb_dropped_call_counts_destroy(client_stats->drop_token_counts); - gpr_free(client_stats); - } -} - -void grpc_grpclb_client_stats_add_call_started( - grpc_grpclb_client_stats* client_stats) { - gpr_atm_full_fetch_add(&client_stats->num_calls_started, (gpr_atm)1); -} - -void grpc_grpclb_client_stats_add_call_finished( - bool finished_with_client_failed_to_send, bool finished_known_received, - grpc_grpclb_client_stats* client_stats) { - gpr_atm_full_fetch_add(&client_stats->num_calls_finished, (gpr_atm)1); - if (finished_with_client_failed_to_send) { - gpr_atm_full_fetch_add( - &client_stats->num_calls_finished_with_client_failed_to_send, - (gpr_atm)1); - } - if (finished_known_received) { - gpr_atm_full_fetch_add(&client_stats->num_calls_finished_known_received, - (gpr_atm)1); - } -} - -void grpc_grpclb_client_stats_add_call_dropped_locked( - char* token, grpc_grpclb_client_stats* client_stats) { - // Increment num_calls_started and num_calls_finished. - gpr_atm_full_fetch_add(&client_stats->num_calls_started, (gpr_atm)1); - gpr_atm_full_fetch_add(&client_stats->num_calls_finished, (gpr_atm)1); - // Record the drop. - if (client_stats->drop_token_counts == NULL) { - client_stats->drop_token_counts = - (grpc_grpclb_dropped_call_counts*)gpr_zalloc( - sizeof(grpc_grpclb_dropped_call_counts)); - } - grpc_grpclb_dropped_call_counts* drop_token_counts = - client_stats->drop_token_counts; - for (size_t i = 0; i < drop_token_counts->num_entries; ++i) { - if (strcmp(drop_token_counts->token_counts[i].token, token) == 0) { - ++drop_token_counts->token_counts[i].count; - return; - } - } - // Not found, so add a new entry. We double the size of the array each time. - size_t new_num_entries = 2; - while (new_num_entries < drop_token_counts->num_entries + 1) { - new_num_entries *= 2; - } - drop_token_counts->token_counts = (grpc_grpclb_drop_token_count*)gpr_realloc( - drop_token_counts->token_counts, - new_num_entries * sizeof(grpc_grpclb_drop_token_count)); - grpc_grpclb_drop_token_count* new_entry = - &drop_token_counts->token_counts[drop_token_counts->num_entries++]; - new_entry->token = gpr_strdup(token); - new_entry->count = 1; -} - -static void atomic_get_and_reset_counter(int64_t* value, gpr_atm* counter) { - *value = (int64_t)gpr_atm_acq_load(counter); - gpr_atm_full_fetch_add(counter, (gpr_atm)(-*value)); -} - -void grpc_grpclb_client_stats_get_locked( - grpc_grpclb_client_stats* client_stats, int64_t* num_calls_started, - int64_t* num_calls_finished, - int64_t* num_calls_finished_with_client_failed_to_send, - int64_t* num_calls_finished_known_received, - grpc_grpclb_dropped_call_counts** drop_token_counts) { - atomic_get_and_reset_counter(num_calls_started, - &client_stats->num_calls_started); - atomic_get_and_reset_counter(num_calls_finished, - &client_stats->num_calls_finished); - atomic_get_and_reset_counter( - num_calls_finished_with_client_failed_to_send, - &client_stats->num_calls_finished_with_client_failed_to_send); - atomic_get_and_reset_counter( - num_calls_finished_known_received, - &client_stats->num_calls_finished_known_received); - *drop_token_counts = client_stats->drop_token_counts; - client_stats->drop_token_counts = NULL; -} - -void grpc_grpclb_dropped_call_counts_destroy( - grpc_grpclb_dropped_call_counts* drop_entries) { - if (drop_entries != NULL) { - for (size_t i = 0; i < drop_entries->num_entries; ++i) { - gpr_free(drop_entries->token_counts[i].token); - } - gpr_free(drop_entries->token_counts); - gpr_free(drop_entries); - } -} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc new file mode 100644 index 0000000000..903120ca7d --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc @@ -0,0 +1,151 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h" + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" + +#define GRPC_ARG_GRPCLB_CLIENT_STATS "grpc.grpclb_client_stats" + +struct grpc_grpclb_client_stats { + gpr_refcount refs; + // This field must only be accessed via *_locked() methods. + grpc_grpclb_dropped_call_counts* drop_token_counts; + // These fields may be accessed from multiple threads at a time. + gpr_atm num_calls_started; + gpr_atm num_calls_finished; + gpr_atm num_calls_finished_with_client_failed_to_send; + gpr_atm num_calls_finished_known_received; +}; + +grpc_grpclb_client_stats* grpc_grpclb_client_stats_create() { + grpc_grpclb_client_stats* client_stats = + (grpc_grpclb_client_stats*)gpr_zalloc(sizeof(*client_stats)); + gpr_ref_init(&client_stats->refs, 1); + return client_stats; +} + +grpc_grpclb_client_stats* grpc_grpclb_client_stats_ref( + grpc_grpclb_client_stats* client_stats) { + gpr_ref(&client_stats->refs); + return client_stats; +} + +void grpc_grpclb_client_stats_unref(grpc_grpclb_client_stats* client_stats) { + if (gpr_unref(&client_stats->refs)) { + grpc_grpclb_dropped_call_counts_destroy(client_stats->drop_token_counts); + gpr_free(client_stats); + } +} + +void grpc_grpclb_client_stats_add_call_started( + grpc_grpclb_client_stats* client_stats) { + gpr_atm_full_fetch_add(&client_stats->num_calls_started, (gpr_atm)1); +} + +void grpc_grpclb_client_stats_add_call_finished( + bool finished_with_client_failed_to_send, bool finished_known_received, + grpc_grpclb_client_stats* client_stats) { + gpr_atm_full_fetch_add(&client_stats->num_calls_finished, (gpr_atm)1); + if (finished_with_client_failed_to_send) { + gpr_atm_full_fetch_add( + &client_stats->num_calls_finished_with_client_failed_to_send, + (gpr_atm)1); + } + if (finished_known_received) { + gpr_atm_full_fetch_add(&client_stats->num_calls_finished_known_received, + (gpr_atm)1); + } +} + +void grpc_grpclb_client_stats_add_call_dropped_locked( + char* token, grpc_grpclb_client_stats* client_stats) { + // Increment num_calls_started and num_calls_finished. + gpr_atm_full_fetch_add(&client_stats->num_calls_started, (gpr_atm)1); + gpr_atm_full_fetch_add(&client_stats->num_calls_finished, (gpr_atm)1); + // Record the drop. + if (client_stats->drop_token_counts == NULL) { + client_stats->drop_token_counts = + (grpc_grpclb_dropped_call_counts*)gpr_zalloc( + sizeof(grpc_grpclb_dropped_call_counts)); + } + grpc_grpclb_dropped_call_counts* drop_token_counts = + client_stats->drop_token_counts; + for (size_t i = 0; i < drop_token_counts->num_entries; ++i) { + if (strcmp(drop_token_counts->token_counts[i].token, token) == 0) { + ++drop_token_counts->token_counts[i].count; + return; + } + } + // Not found, so add a new entry. We double the size of the array each time. + size_t new_num_entries = 2; + while (new_num_entries < drop_token_counts->num_entries + 1) { + new_num_entries *= 2; + } + drop_token_counts->token_counts = (grpc_grpclb_drop_token_count*)gpr_realloc( + drop_token_counts->token_counts, + new_num_entries * sizeof(grpc_grpclb_drop_token_count)); + grpc_grpclb_drop_token_count* new_entry = + &drop_token_counts->token_counts[drop_token_counts->num_entries++]; + new_entry->token = gpr_strdup(token); + new_entry->count = 1; +} + +static void atomic_get_and_reset_counter(int64_t* value, gpr_atm* counter) { + *value = (int64_t)gpr_atm_acq_load(counter); + gpr_atm_full_fetch_add(counter, (gpr_atm)(-*value)); +} + +void grpc_grpclb_client_stats_get_locked( + grpc_grpclb_client_stats* client_stats, int64_t* num_calls_started, + int64_t* num_calls_finished, + int64_t* num_calls_finished_with_client_failed_to_send, + int64_t* num_calls_finished_known_received, + grpc_grpclb_dropped_call_counts** drop_token_counts) { + atomic_get_and_reset_counter(num_calls_started, + &client_stats->num_calls_started); + atomic_get_and_reset_counter(num_calls_finished, + &client_stats->num_calls_finished); + atomic_get_and_reset_counter( + num_calls_finished_with_client_failed_to_send, + &client_stats->num_calls_finished_with_client_failed_to_send); + atomic_get_and_reset_counter( + num_calls_finished_known_received, + &client_stats->num_calls_finished_known_received); + *drop_token_counts = client_stats->drop_token_counts; + client_stats->drop_token_counts = NULL; +} + +void grpc_grpclb_dropped_call_counts_destroy( + grpc_grpclb_dropped_call_counts* drop_entries) { + if (drop_entries != NULL) { + for (size_t i = 0; i < drop_entries->num_entries; ++i) { + gpr_free(drop_entries->token_counts[i].token); + } + gpr_free(drop_entries->token_counts); + gpr_free(drop_entries); + } +} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c deleted file mode 100644 index 8ef6dfc6f4..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" -#include "third_party/nanopb/pb_decode.h" -#include "third_party/nanopb/pb_encode.h" - -#include - -/* invoked once for every Server in ServerList */ -static bool count_serverlist(pb_istream_t *stream, const pb_field_t *field, - void **arg) { - grpc_grpclb_serverlist *sl = (grpc_grpclb_serverlist *)*arg; - grpc_grpclb_server server; - if (!pb_decode(stream, grpc_lb_v1_Server_fields, &server)) { - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); - return false; - } - ++sl->num_servers; - return true; -} - -typedef struct decode_serverlist_arg { - /* The decoding callback is invoked once per server in serverlist. Remember - * which index of the serverlist are we currently decoding */ - size_t decoding_idx; - /* The decoded serverlist */ - grpc_grpclb_serverlist *serverlist; -} decode_serverlist_arg; - -/* invoked once for every Server in ServerList */ -static bool decode_serverlist(pb_istream_t *stream, const pb_field_t *field, - void **arg) { - decode_serverlist_arg *dec_arg = (decode_serverlist_arg *)*arg; - GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx); - grpc_grpclb_server *server = - (grpc_grpclb_server *)gpr_zalloc(sizeof(grpc_grpclb_server)); - if (!pb_decode(stream, grpc_lb_v1_Server_fields, server)) { - gpr_free(server); - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); - return false; - } - dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server; - return true; -} - -grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name) { - grpc_grpclb_request *req = - (grpc_grpclb_request *)gpr_malloc(sizeof(grpc_grpclb_request)); - req->has_client_stats = false; - req->has_initial_request = true; - req->initial_request.has_name = true; - strncpy(req->initial_request.name, lb_service_name, - GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH); - return req; -} - -static void populate_timestamp(gpr_timespec timestamp, - struct _grpc_lb_v1_Timestamp *timestamp_pb) { - timestamp_pb->has_seconds = true; - timestamp_pb->seconds = timestamp.tv_sec; - timestamp_pb->has_nanos = true; - timestamp_pb->nanos = timestamp.tv_nsec; -} - -static bool encode_string(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { - char *str = (char *)*arg; - if (!pb_encode_tag_for_field(stream, field)) return false; - return pb_encode_string(stream, (uint8_t *)str, strlen(str)); -} - -static bool encode_drops(pb_ostream_t *stream, const pb_field_t *field, - void *const *arg) { - grpc_grpclb_dropped_call_counts *drop_entries = - (grpc_grpclb_dropped_call_counts *)*arg; - if (drop_entries == NULL) return true; - for (size_t i = 0; i < drop_entries->num_entries; ++i) { - if (!pb_encode_tag_for_field(stream, field)) return false; - grpc_lb_v1_ClientStatsPerToken drop_message; - drop_message.load_balance_token.funcs.encode = encode_string; - drop_message.load_balance_token.arg = drop_entries->token_counts[i].token; - drop_message.has_num_calls = true; - drop_message.num_calls = drop_entries->token_counts[i].count; - if (!pb_encode_submessage(stream, grpc_lb_v1_ClientStatsPerToken_fields, - &drop_message)) { - return false; - } - } - return true; -} - -grpc_grpclb_request *grpc_grpclb_load_report_request_create_locked( - grpc_grpclb_client_stats *client_stats) { - grpc_grpclb_request *req = - (grpc_grpclb_request *)gpr_zalloc(sizeof(grpc_grpclb_request)); - req->has_client_stats = true; - req->client_stats.has_timestamp = true; - populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp); - req->client_stats.has_num_calls_started = true; - req->client_stats.has_num_calls_finished = true; - req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; - req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; - req->client_stats.has_num_calls_finished_known_received = true; - req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops; - grpc_grpclb_client_stats_get_locked( - client_stats, &req->client_stats.num_calls_started, - &req->client_stats.num_calls_finished, - &req->client_stats.num_calls_finished_with_client_failed_to_send, - &req->client_stats.num_calls_finished_known_received, - (grpc_grpclb_dropped_call_counts **)&req->client_stats - .calls_finished_with_drop.arg); - return req; -} - -grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request) { - size_t encoded_length; - pb_ostream_t sizestream; - pb_ostream_t outputstream; - grpc_slice slice; - memset(&sizestream, 0, sizeof(pb_ostream_t)); - pb_encode(&sizestream, grpc_lb_v1_LoadBalanceRequest_fields, request); - encoded_length = sizestream.bytes_written; - - slice = GRPC_SLICE_MALLOC(encoded_length); - outputstream = - pb_ostream_from_buffer(GRPC_SLICE_START_PTR(slice), encoded_length); - GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v1_LoadBalanceRequest_fields, - request) != 0); - return slice; -} - -void grpc_grpclb_request_destroy(grpc_grpclb_request *request) { - if (request->has_client_stats) { - grpc_grpclb_dropped_call_counts *drop_entries = - (grpc_grpclb_dropped_call_counts *) - request->client_stats.calls_finished_with_drop.arg; - grpc_grpclb_dropped_call_counts_destroy(drop_entries); - } - gpr_free(request); -} - -typedef grpc_lb_v1_LoadBalanceResponse grpc_grpclb_response; -grpc_grpclb_initial_response *grpc_grpclb_initial_response_parse( - grpc_slice encoded_grpc_grpclb_response) { - pb_istream_t stream = - pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), - GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); - grpc_grpclb_response res; - memset(&res, 0, sizeof(grpc_grpclb_response)); - if (!pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res)) { - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); - return NULL; - } - - if (!res.has_initial_response) return NULL; - - grpc_grpclb_initial_response *initial_res = - (grpc_grpclb_initial_response *)gpr_malloc( - sizeof(grpc_grpclb_initial_response)); - memcpy(initial_res, &res.initial_response, - sizeof(grpc_grpclb_initial_response)); - - return initial_res; -} - -grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist( - grpc_slice encoded_grpc_grpclb_response) { - pb_istream_t stream = - pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), - GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); - pb_istream_t stream_at_start = stream; - grpc_grpclb_serverlist *sl = - (grpc_grpclb_serverlist *)gpr_zalloc(sizeof(grpc_grpclb_serverlist)); - grpc_grpclb_response res; - memset(&res, 0, sizeof(grpc_grpclb_response)); - // First pass: count number of servers. - res.server_list.servers.funcs.decode = count_serverlist; - res.server_list.servers.arg = sl; - bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res); - if (!status) { - gpr_free(sl); - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); - return NULL; - } - // Second pass: populate servers. - if (sl->num_servers > 0) { - sl->servers = (grpc_grpclb_server **)gpr_zalloc( - sizeof(grpc_grpclb_server *) * sl->num_servers); - decode_serverlist_arg decode_arg; - memset(&decode_arg, 0, sizeof(decode_arg)); - decode_arg.serverlist = sl; - res.server_list.servers.funcs.decode = decode_serverlist; - res.server_list.servers.arg = &decode_arg; - status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, - &res); - if (!status) { - grpc_grpclb_destroy_serverlist(sl); - gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); - return NULL; - } - } - if (res.server_list.has_expiration_interval) { - sl->expiration_interval = res.server_list.expiration_interval; - } - return sl; -} - -void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist) { - if (serverlist == NULL) { - return; - } - for (size_t i = 0; i < serverlist->num_servers; i++) { - gpr_free(serverlist->servers[i]); - } - gpr_free(serverlist->servers); - gpr_free(serverlist); -} - -grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy( - const grpc_grpclb_serverlist *sl) { - grpc_grpclb_serverlist *copy = - (grpc_grpclb_serverlist *)gpr_zalloc(sizeof(grpc_grpclb_serverlist)); - copy->num_servers = sl->num_servers; - memcpy(©->expiration_interval, &sl->expiration_interval, - sizeof(grpc_grpclb_duration)); - copy->servers = (grpc_grpclb_server **)gpr_malloc( - sizeof(grpc_grpclb_server *) * sl->num_servers); - for (size_t i = 0; i < sl->num_servers; i++) { - copy->servers[i] = - (grpc_grpclb_server *)gpr_malloc(sizeof(grpc_grpclb_server)); - memcpy(copy->servers[i], sl->servers[i], sizeof(grpc_grpclb_server)); - } - return copy; -} - -bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs, - const grpc_grpclb_serverlist *rhs) { - if (lhs == NULL || rhs == NULL) { - return false; - } - if (lhs->num_servers != rhs->num_servers) { - return false; - } - if (grpc_grpclb_duration_compare(&lhs->expiration_interval, - &rhs->expiration_interval) != 0) { - return false; - } - for (size_t i = 0; i < lhs->num_servers; i++) { - if (!grpc_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) { - return false; - } - } - return true; -} - -bool grpc_grpclb_server_equals(const grpc_grpclb_server *lhs, - const grpc_grpclb_server *rhs) { - return memcmp(lhs, rhs, sizeof(grpc_grpclb_server)) == 0; -} - -int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs, - const grpc_grpclb_duration *rhs) { - GPR_ASSERT(lhs && rhs); - if (lhs->has_seconds && rhs->has_seconds) { - if (lhs->seconds < rhs->seconds) return -1; - if (lhs->seconds > rhs->seconds) return 1; - } else if (lhs->has_seconds) { - return 1; - } else if (rhs->has_seconds) { - return -1; - } - - GPR_ASSERT(lhs->seconds == rhs->seconds); - if (lhs->has_nanos && rhs->has_nanos) { - if (lhs->nanos < rhs->nanos) return -1; - if (lhs->nanos > rhs->nanos) return 1; - } else if (lhs->has_nanos) { - return 1; - } else if (rhs->has_nanos) { - return -1; - } - - return 0; -} - -gpr_timespec grpc_grpclb_duration_to_timespec( - grpc_grpclb_duration *duration_pb) { - gpr_timespec duration; - duration.tv_sec = duration_pb->has_seconds ? duration_pb->seconds : 0; - duration.tv_nsec = duration_pb->has_nanos ? duration_pb->nanos : 0; - duration.clock_type = GPR_TIMESPAN; - return duration; -} - -void grpc_grpclb_initial_response_destroy( - grpc_grpclb_initial_response *response) { - gpr_free(response); -} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc new file mode 100644 index 0000000000..8ef6dfc6f4 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc @@ -0,0 +1,314 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h" +#include "third_party/nanopb/pb_decode.h" +#include "third_party/nanopb/pb_encode.h" + +#include + +/* invoked once for every Server in ServerList */ +static bool count_serverlist(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + grpc_grpclb_serverlist *sl = (grpc_grpclb_serverlist *)*arg; + grpc_grpclb_server server; + if (!pb_decode(stream, grpc_lb_v1_Server_fields, &server)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + ++sl->num_servers; + return true; +} + +typedef struct decode_serverlist_arg { + /* The decoding callback is invoked once per server in serverlist. Remember + * which index of the serverlist are we currently decoding */ + size_t decoding_idx; + /* The decoded serverlist */ + grpc_grpclb_serverlist *serverlist; +} decode_serverlist_arg; + +/* invoked once for every Server in ServerList */ +static bool decode_serverlist(pb_istream_t *stream, const pb_field_t *field, + void **arg) { + decode_serverlist_arg *dec_arg = (decode_serverlist_arg *)*arg; + GPR_ASSERT(dec_arg->serverlist->num_servers >= dec_arg->decoding_idx); + grpc_grpclb_server *server = + (grpc_grpclb_server *)gpr_zalloc(sizeof(grpc_grpclb_server)); + if (!pb_decode(stream, grpc_lb_v1_Server_fields, server)) { + gpr_free(server); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(stream)); + return false; + } + dec_arg->serverlist->servers[dec_arg->decoding_idx++] = server; + return true; +} + +grpc_grpclb_request *grpc_grpclb_request_create(const char *lb_service_name) { + grpc_grpclb_request *req = + (grpc_grpclb_request *)gpr_malloc(sizeof(grpc_grpclb_request)); + req->has_client_stats = false; + req->has_initial_request = true; + req->initial_request.has_name = true; + strncpy(req->initial_request.name, lb_service_name, + GRPC_GRPCLB_SERVICE_NAME_MAX_LENGTH); + return req; +} + +static void populate_timestamp(gpr_timespec timestamp, + struct _grpc_lb_v1_Timestamp *timestamp_pb) { + timestamp_pb->has_seconds = true; + timestamp_pb->seconds = timestamp.tv_sec; + timestamp_pb->has_nanos = true; + timestamp_pb->nanos = timestamp.tv_nsec; +} + +static bool encode_string(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + char *str = (char *)*arg; + if (!pb_encode_tag_for_field(stream, field)) return false; + return pb_encode_string(stream, (uint8_t *)str, strlen(str)); +} + +static bool encode_drops(pb_ostream_t *stream, const pb_field_t *field, + void *const *arg) { + grpc_grpclb_dropped_call_counts *drop_entries = + (grpc_grpclb_dropped_call_counts *)*arg; + if (drop_entries == NULL) return true; + for (size_t i = 0; i < drop_entries->num_entries; ++i) { + if (!pb_encode_tag_for_field(stream, field)) return false; + grpc_lb_v1_ClientStatsPerToken drop_message; + drop_message.load_balance_token.funcs.encode = encode_string; + drop_message.load_balance_token.arg = drop_entries->token_counts[i].token; + drop_message.has_num_calls = true; + drop_message.num_calls = drop_entries->token_counts[i].count; + if (!pb_encode_submessage(stream, grpc_lb_v1_ClientStatsPerToken_fields, + &drop_message)) { + return false; + } + } + return true; +} + +grpc_grpclb_request *grpc_grpclb_load_report_request_create_locked( + grpc_grpclb_client_stats *client_stats) { + grpc_grpclb_request *req = + (grpc_grpclb_request *)gpr_zalloc(sizeof(grpc_grpclb_request)); + req->has_client_stats = true; + req->client_stats.has_timestamp = true; + populate_timestamp(gpr_now(GPR_CLOCK_REALTIME), &req->client_stats.timestamp); + req->client_stats.has_num_calls_started = true; + req->client_stats.has_num_calls_finished = true; + req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; + req->client_stats.has_num_calls_finished_with_client_failed_to_send = true; + req->client_stats.has_num_calls_finished_known_received = true; + req->client_stats.calls_finished_with_drop.funcs.encode = encode_drops; + grpc_grpclb_client_stats_get_locked( + client_stats, &req->client_stats.num_calls_started, + &req->client_stats.num_calls_finished, + &req->client_stats.num_calls_finished_with_client_failed_to_send, + &req->client_stats.num_calls_finished_known_received, + (grpc_grpclb_dropped_call_counts **)&req->client_stats + .calls_finished_with_drop.arg); + return req; +} + +grpc_slice grpc_grpclb_request_encode(const grpc_grpclb_request *request) { + size_t encoded_length; + pb_ostream_t sizestream; + pb_ostream_t outputstream; + grpc_slice slice; + memset(&sizestream, 0, sizeof(pb_ostream_t)); + pb_encode(&sizestream, grpc_lb_v1_LoadBalanceRequest_fields, request); + encoded_length = sizestream.bytes_written; + + slice = GRPC_SLICE_MALLOC(encoded_length); + outputstream = + pb_ostream_from_buffer(GRPC_SLICE_START_PTR(slice), encoded_length); + GPR_ASSERT(pb_encode(&outputstream, grpc_lb_v1_LoadBalanceRequest_fields, + request) != 0); + return slice; +} + +void grpc_grpclb_request_destroy(grpc_grpclb_request *request) { + if (request->has_client_stats) { + grpc_grpclb_dropped_call_counts *drop_entries = + (grpc_grpclb_dropped_call_counts *) + request->client_stats.calls_finished_with_drop.arg; + grpc_grpclb_dropped_call_counts_destroy(drop_entries); + } + gpr_free(request); +} + +typedef grpc_lb_v1_LoadBalanceResponse grpc_grpclb_response; +grpc_grpclb_initial_response *grpc_grpclb_initial_response_parse( + grpc_slice encoded_grpc_grpclb_response) { + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), + GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); + grpc_grpclb_response res; + memset(&res, 0, sizeof(grpc_grpclb_response)); + if (!pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res)) { + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return NULL; + } + + if (!res.has_initial_response) return NULL; + + grpc_grpclb_initial_response *initial_res = + (grpc_grpclb_initial_response *)gpr_malloc( + sizeof(grpc_grpclb_initial_response)); + memcpy(initial_res, &res.initial_response, + sizeof(grpc_grpclb_initial_response)); + + return initial_res; +} + +grpc_grpclb_serverlist *grpc_grpclb_response_parse_serverlist( + grpc_slice encoded_grpc_grpclb_response) { + pb_istream_t stream = + pb_istream_from_buffer(GRPC_SLICE_START_PTR(encoded_grpc_grpclb_response), + GRPC_SLICE_LENGTH(encoded_grpc_grpclb_response)); + pb_istream_t stream_at_start = stream; + grpc_grpclb_serverlist *sl = + (grpc_grpclb_serverlist *)gpr_zalloc(sizeof(grpc_grpclb_serverlist)); + grpc_grpclb_response res; + memset(&res, 0, sizeof(grpc_grpclb_response)); + // First pass: count number of servers. + res.server_list.servers.funcs.decode = count_serverlist; + res.server_list.servers.arg = sl; + bool status = pb_decode(&stream, grpc_lb_v1_LoadBalanceResponse_fields, &res); + if (!status) { + gpr_free(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return NULL; + } + // Second pass: populate servers. + if (sl->num_servers > 0) { + sl->servers = (grpc_grpclb_server **)gpr_zalloc( + sizeof(grpc_grpclb_server *) * sl->num_servers); + decode_serverlist_arg decode_arg; + memset(&decode_arg, 0, sizeof(decode_arg)); + decode_arg.serverlist = sl; + res.server_list.servers.funcs.decode = decode_serverlist; + res.server_list.servers.arg = &decode_arg; + status = pb_decode(&stream_at_start, grpc_lb_v1_LoadBalanceResponse_fields, + &res); + if (!status) { + grpc_grpclb_destroy_serverlist(sl); + gpr_log(GPR_ERROR, "nanopb error: %s", PB_GET_ERROR(&stream)); + return NULL; + } + } + if (res.server_list.has_expiration_interval) { + sl->expiration_interval = res.server_list.expiration_interval; + } + return sl; +} + +void grpc_grpclb_destroy_serverlist(grpc_grpclb_serverlist *serverlist) { + if (serverlist == NULL) { + return; + } + for (size_t i = 0; i < serverlist->num_servers; i++) { + gpr_free(serverlist->servers[i]); + } + gpr_free(serverlist->servers); + gpr_free(serverlist); +} + +grpc_grpclb_serverlist *grpc_grpclb_serverlist_copy( + const grpc_grpclb_serverlist *sl) { + grpc_grpclb_serverlist *copy = + (grpc_grpclb_serverlist *)gpr_zalloc(sizeof(grpc_grpclb_serverlist)); + copy->num_servers = sl->num_servers; + memcpy(©->expiration_interval, &sl->expiration_interval, + sizeof(grpc_grpclb_duration)); + copy->servers = (grpc_grpclb_server **)gpr_malloc( + sizeof(grpc_grpclb_server *) * sl->num_servers); + for (size_t i = 0; i < sl->num_servers; i++) { + copy->servers[i] = + (grpc_grpclb_server *)gpr_malloc(sizeof(grpc_grpclb_server)); + memcpy(copy->servers[i], sl->servers[i], sizeof(grpc_grpclb_server)); + } + return copy; +} + +bool grpc_grpclb_serverlist_equals(const grpc_grpclb_serverlist *lhs, + const grpc_grpclb_serverlist *rhs) { + if (lhs == NULL || rhs == NULL) { + return false; + } + if (lhs->num_servers != rhs->num_servers) { + return false; + } + if (grpc_grpclb_duration_compare(&lhs->expiration_interval, + &rhs->expiration_interval) != 0) { + return false; + } + for (size_t i = 0; i < lhs->num_servers; i++) { + if (!grpc_grpclb_server_equals(lhs->servers[i], rhs->servers[i])) { + return false; + } + } + return true; +} + +bool grpc_grpclb_server_equals(const grpc_grpclb_server *lhs, + const grpc_grpclb_server *rhs) { + return memcmp(lhs, rhs, sizeof(grpc_grpclb_server)) == 0; +} + +int grpc_grpclb_duration_compare(const grpc_grpclb_duration *lhs, + const grpc_grpclb_duration *rhs) { + GPR_ASSERT(lhs && rhs); + if (lhs->has_seconds && rhs->has_seconds) { + if (lhs->seconds < rhs->seconds) return -1; + if (lhs->seconds > rhs->seconds) return 1; + } else if (lhs->has_seconds) { + return 1; + } else if (rhs->has_seconds) { + return -1; + } + + GPR_ASSERT(lhs->seconds == rhs->seconds); + if (lhs->has_nanos && rhs->has_nanos) { + if (lhs->nanos < rhs->nanos) return -1; + if (lhs->nanos > rhs->nanos) return 1; + } else if (lhs->has_nanos) { + return 1; + } else if (rhs->has_nanos) { + return -1; + } + + return 0; +} + +gpr_timespec grpc_grpclb_duration_to_timespec( + grpc_grpclb_duration *duration_pb) { + gpr_timespec duration; + duration.tv_sec = duration_pb->has_seconds ? duration_pb->seconds : 0; + duration.tv_nsec = duration_pb->has_nanos ? duration_pb->nanos : 0; + duration.clock_type = GPR_TIMESPAN; + return duration; +} + +void grpc_grpclb_initial_response_destroy( + grpc_grpclb_initial_response *response) { + gpr_free(response); +} diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c deleted file mode 100644 index 6a5d54c82a..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.3.7-dev */ - -#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" - -/* @@protoc_insertion_point(includes) */ -#if PB_PROTO_HEADER_VERSION != 30 -#error Regenerate this file with the current version of nanopb generator. -#endif - - - -const pb_field_t grpc_lb_v1_Duration_fields[3] = { - PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Duration, seconds, seconds, 0), - PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Duration, nanos, seconds, 0), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_Timestamp_fields[3] = { - PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Timestamp, seconds, seconds, 0), - PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Timestamp, nanos, seconds, 0), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_LoadBalanceRequest_fields[3] = { - PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_LoadBalanceRequest, initial_request, initial_request, &grpc_lb_v1_InitialLoadBalanceRequest_fields), - PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_LoadBalanceRequest, client_stats, initial_request, &grpc_lb_v1_ClientStats_fields), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_InitialLoadBalanceRequest_fields[2] = { - PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v1_InitialLoadBalanceRequest, name, name, 0), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_ClientStatsPerToken_fields[3] = { - PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_lb_v1_ClientStatsPerToken, load_balance_token, load_balance_token, 0), - PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStatsPerToken, num_calls, load_balance_token, 0), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_ClientStats_fields[7] = { - PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_ClientStats, timestamp, timestamp, &grpc_lb_v1_Timestamp_fields), - PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_started, timestamp, 0), - PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished, num_calls_started, 0), - PB_FIELD( 6, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_with_client_failed_to_send, num_calls_finished, 0), - PB_FIELD( 7, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_known_received, num_calls_finished_with_client_failed_to_send, 0), - PB_FIELD( 8, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_lb_v1_ClientStats, calls_finished_with_drop, num_calls_finished_known_received, &grpc_lb_v1_ClientStatsPerToken_fields), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_LoadBalanceResponse_fields[3] = { - PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_LoadBalanceResponse, initial_response, initial_response, &grpc_lb_v1_InitialLoadBalanceResponse_fields), - PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_LoadBalanceResponse, server_list, initial_response, &grpc_lb_v1_ServerList_fields), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3] = { - PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v1_InitialLoadBalanceResponse, load_balancer_delegate, load_balancer_delegate, 0), - PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &grpc_lb_v1_Duration_fields), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_ServerList_fields[3] = { - PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, grpc_lb_v1_ServerList, servers, servers, &grpc_lb_v1_Server_fields), - PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ServerList, expiration_interval, servers, &grpc_lb_v1_Duration_fields), - PB_LAST_FIELD -}; - -const pb_field_t grpc_lb_v1_Server_fields[5] = { - PB_FIELD( 1, BYTES , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Server, ip_address, ip_address, 0), - PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, port, ip_address, 0), - PB_FIELD( 3, STRING , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, load_balance_token, port, 0), - PB_FIELD( 4, BOOL , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, drop, load_balance_token, 0), - PB_LAST_FIELD -}; - - -/* Check that field information fits in pb_field_t */ -#if !defined(PB_FIELD_32BIT) -/* If you get an error here, it means that you need to define PB_FIELD_32BIT - * compile-time option. You can do that in pb.h or on compiler command line. - * - * The reason you need to do this is that some of your messages contain tag - * numbers or field sizes that are larger than what can fit in 8 or 16 bit - * field descriptors. - */ -PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 65536 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 65536 && pb_membersize(grpc_lb_v1_ServerList, servers) < 65536 && pb_membersize(grpc_lb_v1_ServerList, expiration_interval) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_lb_v1_Duration_grpc_lb_v1_Timestamp_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server) -#endif - -#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) -/* If you get an error here, it means that you need to define PB_FIELD_16BIT - * compile-time option. You can do that in pb.h or on compiler command line. - * - * The reason you need to do this is that some of your messages contain tag - * numbers or field sizes that are larger than what can fit in the default - * 8 bit descriptors. - */ -PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 256 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 256 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 256 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 256 && pb_membersize(grpc_lb_v1_ServerList, servers) < 256 && pb_membersize(grpc_lb_v1_ServerList, expiration_interval) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_lb_v1_Duration_grpc_lb_v1_Timestamp_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server) -#endif - - -/* @@protoc_insertion_point(eof) */ diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc new file mode 100644 index 0000000000..6a5d54c82a --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc @@ -0,0 +1,103 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.7-dev */ + +#include "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + + +const pb_field_t grpc_lb_v1_Duration_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Duration, seconds, seconds, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Duration, nanos, seconds, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_Timestamp_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Timestamp, seconds, seconds, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Timestamp, nanos, seconds, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_LoadBalanceRequest_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_LoadBalanceRequest, initial_request, initial_request, &grpc_lb_v1_InitialLoadBalanceRequest_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_LoadBalanceRequest, client_stats, initial_request, &grpc_lb_v1_ClientStats_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_InitialLoadBalanceRequest_fields[2] = { + PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v1_InitialLoadBalanceRequest, name, name, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_ClientStatsPerToken_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, CALLBACK, FIRST, grpc_lb_v1_ClientStatsPerToken, load_balance_token, load_balance_token, 0), + PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStatsPerToken, num_calls, load_balance_token, 0), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_ClientStats_fields[7] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_ClientStats, timestamp, timestamp, &grpc_lb_v1_Timestamp_fields), + PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_started, timestamp, 0), + PB_FIELD( 3, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished, num_calls_started, 0), + PB_FIELD( 6, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_with_client_failed_to_send, num_calls_finished, 0), + PB_FIELD( 7, INT64 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ClientStats, num_calls_finished_known_received, num_calls_finished_with_client_failed_to_send, 0), + PB_FIELD( 8, MESSAGE , REPEATED, CALLBACK, OTHER, grpc_lb_v1_ClientStats, calls_finished_with_drop, num_calls_finished_known_received, &grpc_lb_v1_ClientStatsPerToken_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_LoadBalanceResponse_fields[3] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, grpc_lb_v1_LoadBalanceResponse, initial_response, initial_response, &grpc_lb_v1_InitialLoadBalanceResponse_fields), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_LoadBalanceResponse, server_list, initial_response, &grpc_lb_v1_ServerList_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_InitialLoadBalanceResponse_fields[3] = { + PB_FIELD( 1, STRING , OPTIONAL, STATIC , FIRST, grpc_lb_v1_InitialLoadBalanceResponse, load_balancer_delegate, load_balancer_delegate, 0), + PB_FIELD( 2, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval, load_balancer_delegate, &grpc_lb_v1_Duration_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_ServerList_fields[3] = { + PB_FIELD( 1, MESSAGE , REPEATED, CALLBACK, FIRST, grpc_lb_v1_ServerList, servers, servers, &grpc_lb_v1_Server_fields), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, grpc_lb_v1_ServerList, expiration_interval, servers, &grpc_lb_v1_Duration_fields), + PB_LAST_FIELD +}; + +const pb_field_t grpc_lb_v1_Server_fields[5] = { + PB_FIELD( 1, BYTES , OPTIONAL, STATIC , FIRST, grpc_lb_v1_Server, ip_address, ip_address, 0), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, port, ip_address, 0), + PB_FIELD( 3, STRING , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, load_balance_token, port, 0), + PB_FIELD( 4, BOOL , OPTIONAL, STATIC , OTHER, grpc_lb_v1_Server, drop, load_balance_token, 0), + PB_LAST_FIELD +}; + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 65536 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 65536 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 65536 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 65536 && pb_membersize(grpc_lb_v1_ServerList, servers) < 65536 && pb_membersize(grpc_lb_v1_ServerList, expiration_interval) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_grpc_lb_v1_Duration_grpc_lb_v1_Timestamp_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceRequest, client_stats) < 256 && pb_membersize(grpc_lb_v1_ClientStats, timestamp) < 256 && pb_membersize(grpc_lb_v1_ClientStats, calls_finished_with_drop) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, initial_response) < 256 && pb_membersize(grpc_lb_v1_LoadBalanceResponse, server_list) < 256 && pb_membersize(grpc_lb_v1_InitialLoadBalanceResponse, client_stats_report_interval) < 256 && pb_membersize(grpc_lb_v1_ServerList, servers) < 256 && pb_membersize(grpc_lb_v1_ServerList, expiration_interval) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_grpc_lb_v1_Duration_grpc_lb_v1_Timestamp_grpc_lb_v1_LoadBalanceRequest_grpc_lb_v1_InitialLoadBalanceRequest_grpc_lb_v1_ClientStatsPerToken_grpc_lb_v1_ClientStats_grpc_lb_v1_LoadBalanceResponse_grpc_lb_v1_InitialLoadBalanceResponse_grpc_lb_v1_ServerList_grpc_lb_v1_Server) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c deleted file mode 100644 index d20cbb8388..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/subchannel.h" -#include "src/core/ext/filters/client_channel/subchannel_index.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/transport/connectivity_state.h" - -grpc_tracer_flag grpc_lb_pick_first_trace = - GRPC_TRACER_INITIALIZER(false, "pick_first"); - -typedef struct pending_pick { - struct pending_pick *next; - uint32_t initial_metadata_flags; - grpc_connected_subchannel **target; - grpc_closure *on_complete; -} pending_pick; - -typedef struct { - /** base policy: must be first */ - grpc_lb_policy base; - /** all our subchannels */ - grpc_subchannel **subchannels; - grpc_subchannel **new_subchannels; - size_t num_subchannels; - size_t num_new_subchannels; - - grpc_closure connectivity_changed; - - /** remaining members are protected by the combiner */ - - /** the selected channel */ - grpc_connected_subchannel *selected; - - /** the subchannel key for \a selected, or NULL if \a selected not set */ - const grpc_subchannel_key *selected_key; - - /** have we started picking? */ - bool started_picking; - /** are we shut down? */ - bool shutdown; - /** are we updating the selected subchannel? */ - bool updating_selected; - /** are we updating the subchannel candidates? */ - bool updating_subchannels; - /** args from the latest update received while already updating, or NULL */ - grpc_lb_policy_args *pending_update_args; - /** which subchannel are we watching? */ - size_t checking_subchannel; - /** what is the connectivity of that channel? */ - grpc_connectivity_state checking_connectivity; - /** list of picks that are waiting on connectivity */ - pending_pick *pending_picks; - - /** our connectivity state tracker */ - grpc_connectivity_state_tracker state_tracker; -} pick_first_lb_policy; - -static void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - GPR_ASSERT(p->pending_picks == NULL); - for (size_t i = 0; i < p->num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first_destroy"); - } - if (p->selected != NULL) { - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, p->selected, - "picked_first_destroy"); - } - grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); - grpc_subchannel_index_unref(); - if (p->pending_update_args != NULL) { - grpc_channel_args_destroy(exec_ctx, p->pending_update_args->args); - gpr_free(p->pending_update_args); - } - gpr_free(p->subchannels); - gpr_free(p->new_subchannels); - gpr_free(p); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_DEBUG, "Pick First %p destroyed.", (void *)p); - } -} - -static void pf_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - p->shutdown = true; - pp = p->pending_picks; - p->pending_picks = NULL; - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown"), "shutdown"); - /* cancel subscription */ - if (p->selected != NULL) { - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, p->selected, NULL, NULL, &p->connectivity_changed); - } else if (p->num_subchannels > 0 && p->started_picking) { - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, - &p->connectivity_changed); - } - while (pp != NULL) { - pending_pick *next = pp->next; - *pp->target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); - gpr_free(pp); - pp = next; - } -} - -static void pf_cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_connected_subchannel **target, - grpc_error *error) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - pp = p->pending_picks; - p->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if (pp->target == target) { - *target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick Cancelled", &error, 1)); - gpr_free(pp); - } else { - pp->next = p->pending_picks; - p->pending_picks = pp; - } - pp = next; - } - GRPC_ERROR_UNREF(error); -} - -static void pf_cancel_picks_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - uint32_t initial_metadata_flags_mask, - uint32_t initial_metadata_flags_eq, - grpc_error *error) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - pp = p->pending_picks; - p->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if ((pp->initial_metadata_flags & initial_metadata_flags_mask) == - initial_metadata_flags_eq) { - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick Cancelled", &error, 1)); - gpr_free(pp); - } else { - pp->next = p->pending_picks; - p->pending_picks = pp; - } - pp = next; - } - GRPC_ERROR_UNREF(error); -} - -static void start_picking_locked(grpc_exec_ctx *exec_ctx, - pick_first_lb_policy *p) { - p->started_picking = true; - if (p->subchannels != NULL) { - GPR_ASSERT(p->num_subchannels > 0); - p->checking_subchannel = 0; - p->checking_connectivity = GRPC_CHANNEL_IDLE; - GRPC_LB_POLICY_WEAK_REF(&p->base, "pick_first_connectivity"); - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - } -} - -static void pf_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - if (!p->started_picking) { - start_picking_locked(exec_ctx, p); - } -} - -static int pf_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - const grpc_lb_policy_pick_args *pick_args, - grpc_connected_subchannel **target, - grpc_call_context_element *context, void **user_data, - grpc_closure *on_complete) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - pending_pick *pp; - - /* Check atomically for a selected channel */ - if (p->selected != NULL) { - *target = GRPC_CONNECTED_SUBCHANNEL_REF(p->selected, "picked"); - return 1; - } - - /* No subchannel selected yet, so try again */ - if (!p->started_picking) { - start_picking_locked(exec_ctx, p); - } - pp = (pending_pick *)gpr_malloc(sizeof(*pp)); - pp->next = p->pending_picks; - pp->target = target; - pp->initial_metadata_flags = pick_args->initial_metadata_flags; - pp->on_complete = on_complete; - p->pending_picks = pp; - return 0; -} - -static void destroy_subchannels_locked(grpc_exec_ctx *exec_ctx, - pick_first_lb_policy *p) { - size_t num_subchannels = p->num_subchannels; - grpc_subchannel **subchannels = p->subchannels; - - p->num_subchannels = 0; - p->subchannels = NULL; - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "destroy_subchannels"); - - for (size_t i = 0; i < num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first"); - } - gpr_free(subchannels); -} - -static grpc_connectivity_state pf_check_connectivity_locked( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_error **error) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - return grpc_connectivity_state_get(&p->state_tracker, error); -} - -static void pf_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *pol, - grpc_connectivity_state *current, - grpc_closure *notify) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, - current, notify); -} - -static void pf_ping_one_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_closure *closure) { - pick_first_lb_policy *p = (pick_first_lb_policy *)pol; - if (p->selected) { - grpc_connected_subchannel_ping(exec_ctx, p->selected, closure); - } else { - GRPC_CLOSURE_SCHED(exec_ctx, closure, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Not connected")); - } -} - -/* unsubscribe all subchannels */ -static void stop_connectivity_watchers(grpc_exec_ctx *exec_ctx, - pick_first_lb_policy *p) { - if (p->num_subchannels > 0) { - GPR_ASSERT(p->selected == NULL); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_DEBUG, "Pick First %p unsubscribing from subchannel %p", - (void *)p, (void *)p->subchannels[p->checking_subchannel]); - } - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, - &p->connectivity_changed); - p->updating_subchannels = true; - } else if (p->selected != NULL) { - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_DEBUG, - "Pick First %p unsubscribing from selected subchannel %p", - (void *)p, (void *)p->selected); - } - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, p->selected, NULL, NULL, &p->connectivity_changed); - p->updating_selected = true; - } -} - -/* true upon success */ -static void pf_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - const grpc_lb_policy_args *args) { - pick_first_lb_policy *p = (pick_first_lb_policy *)policy; - const grpc_arg *arg = - grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); - if (arg == NULL || arg->type != GRPC_ARG_POINTER) { - if (p->subchannels == NULL) { - // If we don't have a current subchannel list, go into TRANSIENT FAILURE. - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), - "pf_update_missing"); - } else { - // otherwise, keep using the current subchannel list (ignore this update). - gpr_log(GPR_ERROR, - "No valid LB addresses channel arg for Pick First %p update, " - "ignoring.", - (void *)p); - } - return; - } - const grpc_lb_addresses *addresses = - (const grpc_lb_addresses *)arg->value.pointer.p; - if (addresses->num_addresses == 0) { - // Empty update. Unsubscribe from all current subchannels and put the - // channel in TRANSIENT_FAILURE. - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), - "pf_update_empty"); - stop_connectivity_watchers(exec_ctx, p); - return; - } - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_INFO, "Pick First %p received update with %lu addresses", - (void *)p, (unsigned long)addresses->num_addresses); - } - grpc_subchannel_args *sc_args = (grpc_subchannel_args *)gpr_zalloc( - sizeof(*sc_args) * addresses->num_addresses); - /* We remove the following keys in order for subchannel keys belonging to - * subchannels point to the same address to match. */ - static const char *keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS, - GRPC_ARG_LB_ADDRESSES}; - size_t sc_args_count = 0; - - /* Create list of subchannel args for new addresses in \a args. */ - for (size_t i = 0; i < addresses->num_addresses; i++) { - // If there were any balancer, we would have chosen grpclb policy instead. - GPR_ASSERT(!addresses->addresses[i].is_balancer); - if (addresses->addresses[i].user_data != NULL) { - gpr_log(GPR_ERROR, - "This LB policy doesn't support user data. It will be ignored"); - } - grpc_arg addr_arg = - grpc_create_subchannel_address_arg(&addresses->addresses[i].address); - grpc_channel_args *new_args = grpc_channel_args_copy_and_add_and_remove( - args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg, - 1); - gpr_free(addr_arg.value.string); - sc_args[sc_args_count++].args = new_args; - } - - /* Check if p->selected is amongst them. If so, we are done. */ - if (p->selected != NULL) { - GPR_ASSERT(p->selected_key != NULL); - for (size_t i = 0; i < sc_args_count; i++) { - grpc_subchannel_key *ith_sc_key = grpc_subchannel_key_create(&sc_args[i]); - const bool found_selected = - grpc_subchannel_key_compare(p->selected_key, ith_sc_key) == 0; - grpc_subchannel_key_destroy(exec_ctx, ith_sc_key); - if (found_selected) { - // The currently selected subchannel is in the update: we are done. - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_INFO, - "Pick First %p found already selected subchannel %p amongst " - "updates. Update done.", - (void *)p, (void *)p->selected); - } - for (size_t j = 0; j < sc_args_count; j++) { - grpc_channel_args_destroy(exec_ctx, - (grpc_channel_args *)sc_args[j].args); - } - gpr_free(sc_args); - return; - } - } - } - // We only check for already running updates here because if the previous - // steps were successful, the update can be considered done without any - // interference (ie, no callbacks were scheduled). - if (p->updating_selected || p->updating_subchannels) { - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_INFO, - "Update already in progress for pick first %p. Deferring update.", - (void *)p); - } - if (p->pending_update_args != NULL) { - grpc_channel_args_destroy(exec_ctx, p->pending_update_args->args); - gpr_free(p->pending_update_args); - } - p->pending_update_args = - (grpc_lb_policy_args *)gpr_zalloc(sizeof(*p->pending_update_args)); - p->pending_update_args->client_channel_factory = - args->client_channel_factory; - p->pending_update_args->args = grpc_channel_args_copy(args->args); - p->pending_update_args->combiner = args->combiner; - return; - } - /* Create the subchannels for the new subchannel args/addresses. */ - grpc_subchannel **new_subchannels = - (grpc_subchannel **)gpr_zalloc(sizeof(*new_subchannels) * sc_args_count); - size_t num_new_subchannels = 0; - for (size_t i = 0; i < sc_args_count; i++) { - grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( - exec_ctx, args->client_channel_factory, &sc_args[i]); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - char *address_uri = - grpc_sockaddr_to_uri(&addresses->addresses[i].address); - gpr_log(GPR_INFO, - "Pick First %p created subchannel %p for address uri %s", - (void *)p, (void *)subchannel, address_uri); - gpr_free(address_uri); - } - grpc_channel_args_destroy(exec_ctx, (grpc_channel_args *)sc_args[i].args); - if (subchannel != NULL) new_subchannels[num_new_subchannels++] = subchannel; - } - gpr_free(sc_args); - if (num_new_subchannels == 0) { - gpr_free(new_subchannels); - // Empty update. Unsubscribe from all current subchannels and put the - // channel in TRANSIENT_FAILURE. - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid addresses in update"), - "pf_update_no_valid_addresses"); - stop_connectivity_watchers(exec_ctx, p); - return; - } - - /* Destroy the current subchannels. Repurpose pf_shutdown/destroy. */ - stop_connectivity_watchers(exec_ctx, p); - - /* Save new subchannels. The switch over will happen in - * pf_connectivity_changed_locked */ - if (p->updating_selected || p->updating_subchannels) { - p->num_new_subchannels = num_new_subchannels; - p->new_subchannels = new_subchannels; - } else { /* nothing is updating. Get things moving from here */ - p->num_subchannels = num_new_subchannels; - p->subchannels = new_subchannels; - p->new_subchannels = NULL; - p->num_new_subchannels = 0; - if (p->started_picking) { - p->checking_subchannel = 0; - p->checking_connectivity = GRPC_CHANNEL_IDLE; - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - } - } -} - -static void pf_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - pick_first_lb_policy *p = (pick_first_lb_policy *)arg; - grpc_subchannel *selected_subchannel; - pending_pick *pp; - - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log( - GPR_DEBUG, - "Pick First %p connectivity changed. Updating selected: %d; Updating " - "subchannels: %d; Checking %lu index (%lu total); State: %d; ", - (void *)p, p->updating_selected, p->updating_subchannels, - (unsigned long)p->checking_subchannel, - (unsigned long)p->num_subchannels, p->checking_connectivity); - } - bool restart = false; - if (p->updating_selected && error != GRPC_ERROR_NONE) { - /* Captured the unsubscription for p->selected */ - GPR_ASSERT(p->selected != NULL); - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, p->selected, - "pf_update_connectivity"); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_DEBUG, "Pick First %p unreffing selected subchannel %p", - (void *)p, (void *)p->selected); - } - p->updating_selected = false; - if (p->num_new_subchannels == 0) { - p->selected = NULL; - return; - } - restart = true; - } - if (p->updating_subchannels && error != GRPC_ERROR_NONE) { - /* Captured the unsubscription for the checking subchannel */ - GPR_ASSERT(p->selected == NULL); - for (size_t i = 0; i < p->num_subchannels; i++) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], - "pf_update_connectivity"); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_DEBUG, "Pick First %p unreffing subchannel %p", (void *)p, - (void *)p->subchannels[i]); - } - } - gpr_free(p->subchannels); - p->subchannels = NULL; - p->num_subchannels = 0; - p->updating_subchannels = false; - if (p->num_new_subchannels == 0) return; - restart = true; - } - if (restart) { - p->selected = NULL; - p->selected_key = NULL; - GPR_ASSERT(p->new_subchannels != NULL); - GPR_ASSERT(p->num_new_subchannels > 0); - p->num_subchannels = p->num_new_subchannels; - p->subchannels = p->new_subchannels; - p->num_new_subchannels = 0; - p->new_subchannels = NULL; - if (p->started_picking) { - /* If we were picking, continue to do so over the new subchannels, - * starting from the 0th index. */ - p->checking_subchannel = 0; - p->checking_connectivity = GRPC_CHANNEL_IDLE; - /* reuses the weak ref from start_picking_locked */ - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - } - if (p->pending_update_args != NULL) { - const grpc_lb_policy_args *args = p->pending_update_args; - p->pending_update_args = NULL; - pf_update_locked(exec_ctx, &p->base, args); - } - return; - } - GRPC_ERROR_REF(error); - if (p->shutdown) { - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); - GRPC_ERROR_UNREF(error); - return; - } else if (p->selected != NULL) { - if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { - /* if the selected channel goes bad, we're done */ - p->checking_connectivity = GRPC_CHANNEL_SHUTDOWN; - } - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - p->checking_connectivity, GRPC_ERROR_REF(error), - "selected_changed"); - if (p->checking_connectivity != GRPC_CHANNEL_SHUTDOWN) { - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, p->selected, p->base.interested_parties, - &p->checking_connectivity, &p->connectivity_changed); - } else { - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); - } - } else { - loop: - switch (p->checking_connectivity) { - case GRPC_CHANNEL_INIT: - GPR_UNREACHABLE_CODE(return ); - case GRPC_CHANNEL_READY: - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_READY, GRPC_ERROR_NONE, - "connecting_ready"); - selected_subchannel = p->subchannels[p->checking_subchannel]; - p->selected = GRPC_CONNECTED_SUBCHANNEL_REF( - grpc_subchannel_get_connected_subchannel(selected_subchannel), - "picked_first"); - - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_INFO, - "Pick First %p selected subchannel %p (connected %p)", - (void *)p, (void *)selected_subchannel, (void *)p->selected); - } - p->selected_key = grpc_subchannel_get_key(selected_subchannel); - /* drop the pick list: we are connected now */ - GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels"); - destroy_subchannels_locked(exec_ctx, p); - /* update any calls that were waiting for a pick */ - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(p->selected, "picked"); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_INFO, - "Servicing pending pick with selected subchannel %p", - (void *)p->selected); - } - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); - gpr_free(pp); - } - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, p->selected, p->base.interested_parties, - &p->checking_connectivity, &p->connectivity_changed); - break; - case GRPC_CHANNEL_TRANSIENT_FAILURE: - p->checking_subchannel = - (p->checking_subchannel + 1) % p->num_subchannels; - if (p->checking_subchannel == 0) { - /* only trigger transient failure when we've tried all alternatives - */ - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_REF(error), "connecting_transient_failure"); - } - GRPC_ERROR_UNREF(error); - p->checking_connectivity = grpc_subchannel_check_connectivity( - p->subchannels[p->checking_subchannel], &error); - if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - } else { - goto loop; - } - break; - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_IDLE: - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_CONNECTING, - GRPC_ERROR_REF(error), "connecting_changed"); - grpc_subchannel_notify_on_state_change( - exec_ctx, p->subchannels[p->checking_subchannel], - p->base.interested_parties, &p->checking_connectivity, - &p->connectivity_changed); - break; - case GRPC_CHANNEL_SHUTDOWN: - p->num_subchannels--; - GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], - p->subchannels[p->num_subchannels]); - GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], - "pick_first"); - if (p->num_subchannels == 0) { - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick first exhausted channels", &error, 1), - "no_more_channels"); - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); - gpr_free(pp); - } - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, - "pick_first_connectivity"); - } else { - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_REF(error), "subchannel_failed"); - p->checking_subchannel %= p->num_subchannels; - GRPC_ERROR_UNREF(error); - p->checking_connectivity = grpc_subchannel_check_connectivity( - p->subchannels[p->checking_subchannel], &error); - goto loop; - } - } - } - - GRPC_ERROR_UNREF(error); -} - -static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { - pf_destroy, - pf_shutdown_locked, - pf_pick_locked, - pf_cancel_pick_locked, - pf_cancel_picks_locked, - pf_ping_one_locked, - pf_exit_idle_locked, - pf_check_connectivity_locked, - pf_notify_on_state_change_locked, - pf_update_locked}; - -static void pick_first_factory_ref(grpc_lb_policy_factory *factory) {} - -static void pick_first_factory_unref(grpc_lb_policy_factory *factory) {} - -static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, - grpc_lb_policy_factory *factory, - grpc_lb_policy_args *args) { - GPR_ASSERT(args->client_channel_factory != NULL); - pick_first_lb_policy *p = (pick_first_lb_policy *)gpr_zalloc(sizeof(*p)); - if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { - gpr_log(GPR_DEBUG, "Pick First %p created.", (void *)p); - } - pf_update_locked(exec_ctx, &p->base, args); - grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable, args->combiner); - grpc_subchannel_index_ref(); - GRPC_CLOSURE_INIT(&p->connectivity_changed, pf_connectivity_changed_locked, p, - grpc_combiner_scheduler(args->combiner)); - return &p->base; -} - -static const grpc_lb_policy_factory_vtable pick_first_factory_vtable = { - pick_first_factory_ref, pick_first_factory_unref, create_pick_first, - "pick_first"}; - -static grpc_lb_policy_factory pick_first_lb_policy_factory = { - &pick_first_factory_vtable}; - -static grpc_lb_policy_factory *pick_first_lb_factory_create() { - return &pick_first_lb_policy_factory; -} - -/* Plugin registration */ - -void grpc_lb_policy_pick_first_init() { - grpc_register_lb_policy(pick_first_lb_factory_create()); - grpc_register_tracer(&grpc_lb_pick_first_trace); -} - -void grpc_lb_policy_pick_first_shutdown() {} diff --git a/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc new file mode 100644 index 0000000000..b07fc3b720 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc @@ -0,0 +1,714 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/transport/connectivity_state.h" + +grpc_tracer_flag grpc_lb_pick_first_trace = + GRPC_TRACER_INITIALIZER(false, "pick_first"); + +typedef struct pending_pick { + struct pending_pick *next; + uint32_t initial_metadata_flags; + grpc_connected_subchannel **target; + grpc_closure *on_complete; +} pending_pick; + +typedef struct { + /** base policy: must be first */ + grpc_lb_policy base; + /** all our subchannels */ + grpc_subchannel **subchannels; + grpc_subchannel **new_subchannels; + size_t num_subchannels; + size_t num_new_subchannels; + + grpc_closure connectivity_changed; + + /** remaining members are protected by the combiner */ + + /** the selected channel */ + grpc_connected_subchannel *selected; + + /** the subchannel key for \a selected, or NULL if \a selected not set */ + const grpc_subchannel_key *selected_key; + + /** have we started picking? */ + bool started_picking; + /** are we shut down? */ + bool shutdown; + /** are we updating the selected subchannel? */ + bool updating_selected; + /** are we updating the subchannel candidates? */ + bool updating_subchannels; + /** args from the latest update received while already updating, or NULL */ + grpc_lb_policy_args *pending_update_args; + /** which subchannel are we watching? */ + size_t checking_subchannel; + /** what is the connectivity of that channel? */ + grpc_connectivity_state checking_connectivity; + /** list of picks that are waiting on connectivity */ + pending_pick *pending_picks; + + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker; +} pick_first_lb_policy; + +static void pf_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + GPR_ASSERT(p->pending_picks == NULL); + for (size_t i = 0; i < p->num_subchannels; i++) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], "pick_first_destroy"); + } + if (p->selected != NULL) { + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, p->selected, + "picked_first_destroy"); + } + grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); + grpc_subchannel_index_unref(); + if (p->pending_update_args != NULL) { + grpc_channel_args_destroy(exec_ctx, p->pending_update_args->args); + gpr_free(p->pending_update_args); + } + gpr_free(p->subchannels); + gpr_free(p->new_subchannels); + gpr_free(p); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_DEBUG, "Pick First %p destroyed.", (void *)p); + } +} + +static void pf_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + p->shutdown = true; + pp = p->pending_picks; + p->pending_picks = NULL; + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel shutdown"), "shutdown"); + /* cancel subscription */ + if (p->selected != NULL) { + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, NULL, NULL, &p->connectivity_changed); + } else if (p->num_subchannels > 0 && p->started_picking) { + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, + &p->connectivity_changed); + } + while (pp != NULL) { + pending_pick *next = pp->next; + *pp->target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); + gpr_free(pp); + pp = next; + } +} + +static void pf_cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_connected_subchannel **target, + grpc_error *error) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + pp = p->pending_picks; + p->pending_picks = NULL; + while (pp != NULL) { + pending_pick *next = pp->next; + if (pp->target == target) { + *target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + gpr_free(pp); + } else { + pp->next = p->pending_picks; + p->pending_picks = pp; + } + pp = next; + } + GRPC_ERROR_UNREF(error); +} + +static void pf_cancel_picks_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error *error) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + pp = p->pending_picks; + p->pending_picks = NULL; + while (pp != NULL) { + pending_pick *next = pp->next; + if ((pp->initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick Cancelled", &error, 1)); + gpr_free(pp); + } else { + pp->next = p->pending_picks; + p->pending_picks = pp; + } + pp = next; + } + GRPC_ERROR_UNREF(error); +} + +static void start_picking_locked(grpc_exec_ctx *exec_ctx, + pick_first_lb_policy *p) { + p->started_picking = true; + if (p->subchannels != NULL) { + GPR_ASSERT(p->num_subchannels > 0); + p->checking_subchannel = 0; + p->checking_connectivity = GRPC_CHANNEL_IDLE; + GRPC_LB_POLICY_WEAK_REF(&p->base, "pick_first_connectivity"); + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], + p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); + } +} + +static void pf_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + if (!p->started_picking) { + start_picking_locked(exec_ctx, p); + } +} + +static int pf_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + const grpc_lb_policy_pick_args *pick_args, + grpc_connected_subchannel **target, + grpc_call_context_element *context, void **user_data, + grpc_closure *on_complete) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + pending_pick *pp; + + /* Check atomically for a selected channel */ + if (p->selected != NULL) { + *target = GRPC_CONNECTED_SUBCHANNEL_REF(p->selected, "picked"); + return 1; + } + + /* No subchannel selected yet, so try again */ + if (!p->started_picking) { + start_picking_locked(exec_ctx, p); + } + pp = (pending_pick *)gpr_malloc(sizeof(*pp)); + pp->next = p->pending_picks; + pp->target = target; + pp->initial_metadata_flags = pick_args->initial_metadata_flags; + pp->on_complete = on_complete; + p->pending_picks = pp; + return 0; +} + +static void destroy_subchannels_locked(grpc_exec_ctx *exec_ctx, + pick_first_lb_policy *p) { + size_t num_subchannels = p->num_subchannels; + grpc_subchannel **subchannels = p->subchannels; + + p->num_subchannels = 0; + p->subchannels = NULL; + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "destroy_subchannels"); + + for (size_t i = 0; i < num_subchannels; i++) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannels[i], "pick_first"); + } + gpr_free(subchannels); +} + +static grpc_connectivity_state pf_check_connectivity_locked( + grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_error **error) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + return grpc_connectivity_state_get(&p->state_tracker, error); +} + +static void pf_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_closure *notify) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, + current, notify); +} + +static void pf_ping_one_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_closure *closure) { + pick_first_lb_policy *p = (pick_first_lb_policy *)pol; + if (p->selected) { + grpc_connected_subchannel_ping(exec_ctx, p->selected, closure); + } else { + GRPC_CLOSURE_SCHED(exec_ctx, closure, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Not connected")); + } +} + +/* unsubscribe all subchannels */ +static void stop_connectivity_watchers(grpc_exec_ctx *exec_ctx, + pick_first_lb_policy *p) { + if (p->num_subchannels > 0) { + GPR_ASSERT(p->selected == NULL); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_DEBUG, "Pick First %p unsubscribing from subchannel %p", + (void *)p, (void *)p->subchannels[p->checking_subchannel]); + } + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], NULL, NULL, + &p->connectivity_changed); + p->updating_subchannels = true; + } else if (p->selected != NULL) { + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_DEBUG, + "Pick First %p unsubscribing from selected subchannel %p", + (void *)p, (void *)p->selected); + } + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, NULL, NULL, &p->connectivity_changed); + p->updating_selected = true; + } +} + +/* true upon success */ +static void pf_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + const grpc_lb_policy_args *args) { + pick_first_lb_policy *p = (pick_first_lb_policy *)policy; + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + if (arg == NULL || arg->type != GRPC_ARG_POINTER) { + if (p->subchannels == NULL) { + // If we don't have a current subchannel list, go into TRANSIENT FAILURE. + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), + "pf_update_missing"); + } else { + // otherwise, keep using the current subchannel list (ignore this update). + gpr_log(GPR_ERROR, + "No valid LB addresses channel arg for Pick First %p update, " + "ignoring.", + (void *)p); + } + return; + } + const grpc_lb_addresses *addresses = + (const grpc_lb_addresses *)arg->value.pointer.p; + if (addresses->num_addresses == 0) { + // Empty update. Unsubscribe from all current subchannels and put the + // channel in TRANSIENT_FAILURE. + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), + "pf_update_empty"); + stop_connectivity_watchers(exec_ctx, p); + return; + } + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_INFO, "Pick First %p received update with %lu addresses", + (void *)p, (unsigned long)addresses->num_addresses); + } + grpc_subchannel_args *sc_args = (grpc_subchannel_args *)gpr_zalloc( + sizeof(*sc_args) * addresses->num_addresses); + /* We remove the following keys in order for subchannel keys belonging to + * subchannels point to the same address to match. */ + static const char *keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS, + GRPC_ARG_LB_ADDRESSES}; + size_t sc_args_count = 0; + + /* Create list of subchannel args for new addresses in \a args. */ + for (size_t i = 0; i < addresses->num_addresses; i++) { + // If there were any balancer, we would have chosen grpclb policy instead. + GPR_ASSERT(!addresses->addresses[i].is_balancer); + if (addresses->addresses[i].user_data != NULL) { + gpr_log(GPR_ERROR, + "This LB policy doesn't support user data. It will be ignored"); + } + grpc_arg addr_arg = + grpc_create_subchannel_address_arg(&addresses->addresses[i].address); + grpc_channel_args *new_args = grpc_channel_args_copy_and_add_and_remove( + args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg, + 1); + gpr_free(addr_arg.value.string); + sc_args[sc_args_count++].args = new_args; + } + + /* Check if p->selected is amongst them. If so, we are done. */ + if (p->selected != NULL) { + GPR_ASSERT(p->selected_key != NULL); + for (size_t i = 0; i < sc_args_count; i++) { + grpc_subchannel_key *ith_sc_key = grpc_subchannel_key_create(&sc_args[i]); + const bool found_selected = + grpc_subchannel_key_compare(p->selected_key, ith_sc_key) == 0; + grpc_subchannel_key_destroy(exec_ctx, ith_sc_key); + if (found_selected) { + // The currently selected subchannel is in the update: we are done. + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_INFO, + "Pick First %p found already selected subchannel %p amongst " + "updates. Update done.", + (void *)p, (void *)p->selected); + } + for (size_t j = 0; j < sc_args_count; j++) { + grpc_channel_args_destroy(exec_ctx, + (grpc_channel_args *)sc_args[j].args); + } + gpr_free(sc_args); + return; + } + } + } + // We only check for already running updates here because if the previous + // steps were successful, the update can be considered done without any + // interference (ie, no callbacks were scheduled). + if (p->updating_selected || p->updating_subchannels) { + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_INFO, + "Update already in progress for pick first %p. Deferring update.", + (void *)p); + } + if (p->pending_update_args != NULL) { + grpc_channel_args_destroy(exec_ctx, p->pending_update_args->args); + gpr_free(p->pending_update_args); + } + p->pending_update_args = + (grpc_lb_policy_args *)gpr_zalloc(sizeof(*p->pending_update_args)); + p->pending_update_args->client_channel_factory = + args->client_channel_factory; + p->pending_update_args->args = grpc_channel_args_copy(args->args); + p->pending_update_args->combiner = args->combiner; + return; + } + /* Create the subchannels for the new subchannel args/addresses. */ + grpc_subchannel **new_subchannels = + (grpc_subchannel **)gpr_zalloc(sizeof(*new_subchannels) * sc_args_count); + size_t num_new_subchannels = 0; + for (size_t i = 0; i < sc_args_count; i++) { + grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( + exec_ctx, args->client_channel_factory, &sc_args[i]); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + char *address_uri = + grpc_sockaddr_to_uri(&addresses->addresses[i].address); + gpr_log(GPR_INFO, + "Pick First %p created subchannel %p for address uri %s", + (void *)p, (void *)subchannel, address_uri); + gpr_free(address_uri); + } + grpc_channel_args_destroy(exec_ctx, (grpc_channel_args *)sc_args[i].args); + if (subchannel != NULL) new_subchannels[num_new_subchannels++] = subchannel; + } + gpr_free(sc_args); + if (num_new_subchannels == 0) { + gpr_free(new_subchannels); + // Empty update. Unsubscribe from all current subchannels and put the + // channel in TRANSIENT_FAILURE. + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid addresses in update"), + "pf_update_no_valid_addresses"); + stop_connectivity_watchers(exec_ctx, p); + return; + } + + /* Destroy the current subchannels. Repurpose pf_shutdown/destroy. */ + stop_connectivity_watchers(exec_ctx, p); + + /* Save new subchannels. The switch over will happen in + * pf_connectivity_changed_locked */ + if (p->updating_selected || p->updating_subchannels) { + p->num_new_subchannels = num_new_subchannels; + p->new_subchannels = new_subchannels; + } else { /* nothing is updating. Get things moving from here */ + p->num_subchannels = num_new_subchannels; + p->subchannels = new_subchannels; + p->new_subchannels = NULL; + p->num_new_subchannels = 0; + if (p->started_picking) { + p->checking_subchannel = 0; + p->checking_connectivity = GRPC_CHANNEL_IDLE; + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], + p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); + } + } +} + +static void pf_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + pick_first_lb_policy *p = (pick_first_lb_policy *)arg; + grpc_subchannel *selected_subchannel; + pending_pick *pp; + + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log( + GPR_DEBUG, + "Pick First %p connectivity changed. Updating selected: %d; Updating " + "subchannels: %d; Checking %lu index (%lu total); State: %d; ", + (void *)p, p->updating_selected, p->updating_subchannels, + (unsigned long)p->checking_subchannel, + (unsigned long)p->num_subchannels, p->checking_connectivity); + } + bool restart = false; + if (p->updating_selected && error != GRPC_ERROR_NONE) { + /* Captured the unsubscription for p->selected */ + GPR_ASSERT(p->selected != NULL); + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, p->selected, + "pf_update_connectivity"); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_DEBUG, "Pick First %p unreffing selected subchannel %p", + (void *)p, (void *)p->selected); + } + p->updating_selected = false; + if (p->num_new_subchannels == 0) { + p->selected = NULL; + return; + } + restart = true; + } + if (p->updating_subchannels && error != GRPC_ERROR_NONE) { + /* Captured the unsubscription for the checking subchannel */ + GPR_ASSERT(p->selected == NULL); + for (size_t i = 0; i < p->num_subchannels; i++) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[i], + "pf_update_connectivity"); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_DEBUG, "Pick First %p unreffing subchannel %p", (void *)p, + (void *)p->subchannels[i]); + } + } + gpr_free(p->subchannels); + p->subchannels = NULL; + p->num_subchannels = 0; + p->updating_subchannels = false; + if (p->num_new_subchannels == 0) return; + restart = true; + } + if (restart) { + p->selected = NULL; + p->selected_key = NULL; + GPR_ASSERT(p->new_subchannels != NULL); + GPR_ASSERT(p->num_new_subchannels > 0); + p->num_subchannels = p->num_new_subchannels; + p->subchannels = p->new_subchannels; + p->num_new_subchannels = 0; + p->new_subchannels = NULL; + if (p->started_picking) { + /* If we were picking, continue to do so over the new subchannels, + * starting from the 0th index. */ + p->checking_subchannel = 0; + p->checking_connectivity = GRPC_CHANNEL_IDLE; + /* reuses the weak ref from start_picking_locked */ + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], + p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); + } + if (p->pending_update_args != NULL) { + const grpc_lb_policy_args *args = p->pending_update_args; + p->pending_update_args = NULL; + pf_update_locked(exec_ctx, &p->base, args); + } + return; + } + GRPC_ERROR_REF(error); + if (p->shutdown) { + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); + GRPC_ERROR_UNREF(error); + return; + } else if (p->selected != NULL) { + if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { + /* if the selected channel goes bad, we're done */ + p->checking_connectivity = GRPC_CHANNEL_SHUTDOWN; + } + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + p->checking_connectivity, GRPC_ERROR_REF(error), + "selected_changed"); + if (p->checking_connectivity != GRPC_CHANNEL_SHUTDOWN) { + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, p->base.interested_parties, + &p->checking_connectivity, &p->connectivity_changed); + } else { + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pick_first_connectivity"); + } + } else { + loop: + switch (p->checking_connectivity) { + case GRPC_CHANNEL_INIT: + GPR_UNREACHABLE_CODE(return ); + case GRPC_CHANNEL_READY: + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_READY, GRPC_ERROR_NONE, + "connecting_ready"); + selected_subchannel = p->subchannels[p->checking_subchannel]; + p->selected = GRPC_CONNECTED_SUBCHANNEL_REF( + grpc_subchannel_get_connected_subchannel(selected_subchannel), + "picked_first"); + + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_INFO, + "Pick First %p selected subchannel %p (connected %p)", + (void *)p, (void *)selected_subchannel, (void *)p->selected); + } + p->selected_key = grpc_subchannel_get_key(selected_subchannel); + /* drop the pick list: we are connected now */ + GRPC_LB_POLICY_WEAK_REF(&p->base, "destroy_subchannels"); + destroy_subchannels_locked(exec_ctx, p); + /* update any calls that were waiting for a pick */ + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF(p->selected, "picked"); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_INFO, + "Servicing pending pick with selected subchannel %p", + (void *)p->selected); + } + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); + gpr_free(pp); + } + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, p->selected, p->base.interested_parties, + &p->checking_connectivity, &p->connectivity_changed); + break; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + p->checking_subchannel = + (p->checking_subchannel + 1) % p->num_subchannels; + if (p->checking_subchannel == 0) { + /* only trigger transient failure when we've tried all alternatives + */ + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(error), "connecting_transient_failure"); + } + GRPC_ERROR_UNREF(error); + p->checking_connectivity = grpc_subchannel_check_connectivity( + p->subchannels[p->checking_subchannel], &error); + if (p->checking_connectivity == GRPC_CHANNEL_TRANSIENT_FAILURE) { + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], + p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); + } else { + goto loop; + } + break; + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_CONNECTING, + GRPC_ERROR_REF(error), "connecting_changed"); + grpc_subchannel_notify_on_state_change( + exec_ctx, p->subchannels[p->checking_subchannel], + p->base.interested_parties, &p->checking_connectivity, + &p->connectivity_changed); + break; + case GRPC_CHANNEL_SHUTDOWN: + p->num_subchannels--; + GPR_SWAP(grpc_subchannel *, p->subchannels[p->checking_subchannel], + p->subchannels[p->num_subchannels]); + GRPC_SUBCHANNEL_UNREF(exec_ctx, p->subchannels[p->num_subchannels], + "pick_first"); + if (p->num_subchannels == 0) { + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick first exhausted channels", &error, 1), + "no_more_channels"); + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); + gpr_free(pp); + } + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, + "pick_first_connectivity"); + } else { + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(error), "subchannel_failed"); + p->checking_subchannel %= p->num_subchannels; + GRPC_ERROR_UNREF(error); + p->checking_connectivity = grpc_subchannel_check_connectivity( + p->subchannels[p->checking_subchannel], &error); + goto loop; + } + } + } + + GRPC_ERROR_UNREF(error); +} + +static const grpc_lb_policy_vtable pick_first_lb_policy_vtable = { + pf_destroy, + pf_shutdown_locked, + pf_pick_locked, + pf_cancel_pick_locked, + pf_cancel_picks_locked, + pf_ping_one_locked, + pf_exit_idle_locked, + pf_check_connectivity_locked, + pf_notify_on_state_change_locked, + pf_update_locked}; + +static void pick_first_factory_ref(grpc_lb_policy_factory *factory) {} + +static void pick_first_factory_unref(grpc_lb_policy_factory *factory) {} + +static grpc_lb_policy *create_pick_first(grpc_exec_ctx *exec_ctx, + grpc_lb_policy_factory *factory, + grpc_lb_policy_args *args) { + GPR_ASSERT(args->client_channel_factory != NULL); + pick_first_lb_policy *p = (pick_first_lb_policy *)gpr_zalloc(sizeof(*p)); + if (GRPC_TRACER_ON(grpc_lb_pick_first_trace)) { + gpr_log(GPR_DEBUG, "Pick First %p created.", (void *)p); + } + pf_update_locked(exec_ctx, &p->base, args); + grpc_lb_policy_init(&p->base, &pick_first_lb_policy_vtable, args->combiner); + grpc_subchannel_index_ref(); + GRPC_CLOSURE_INIT(&p->connectivity_changed, pf_connectivity_changed_locked, p, + grpc_combiner_scheduler(args->combiner)); + return &p->base; +} + +static const grpc_lb_policy_factory_vtable pick_first_factory_vtable = { + pick_first_factory_ref, pick_first_factory_unref, create_pick_first, + "pick_first"}; + +static grpc_lb_policy_factory pick_first_lb_policy_factory = { + &pick_first_factory_vtable}; + +static grpc_lb_policy_factory *pick_first_lb_factory_create() { + return &pick_first_lb_policy_factory; +} + +/* Plugin registration */ + +extern "C" void grpc_lb_policy_pick_first_init() { + grpc_register_lb_policy(pick_first_lb_factory_create()); + grpc_register_tracer(&grpc_lb_pick_first_trace); +} + +extern "C" void grpc_lb_policy_pick_first_shutdown() {} diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c deleted file mode 100644 index a3a62e9f3c..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c +++ /dev/null @@ -1,924 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** Round Robin Policy. - * - * Before every pick, the \a get_next_ready_subchannel_index_locked function - * returns the p->subchannel_list->subchannels index for next subchannel, - * respecting the relative - * order of the addresses provided upon creation or updates. Note however that - * updates will start picking from the beginning of the updated list. */ - -#include - -#include - -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/subchannel.h" -#include "src/core/ext/filters/client_channel/subchannel_index.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/transport/connectivity_state.h" -#include "src/core/lib/transport/static_metadata.h" - -grpc_tracer_flag grpc_lb_round_robin_trace = - GRPC_TRACER_INITIALIZER(false, "round_robin"); - -/** List of entities waiting for a pick. - * - * Once a pick is available, \a target is updated and \a on_complete called. */ -typedef struct pending_pick { - struct pending_pick *next; - - /* output argument where to store the pick()ed user_data. It'll be NULL if no - * such data is present or there's an error (the definite test for errors is - * \a target being NULL). */ - void **user_data; - - /* bitmask passed to pick() and used for selective cancelling. See - * grpc_lb_policy_cancel_picks() */ - uint32_t initial_metadata_flags; - - /* output argument where to store the pick()ed connected subchannel, or NULL - * upon error. */ - grpc_connected_subchannel **target; - - /* to be invoked once the pick() has completed (regardless of success) */ - grpc_closure *on_complete; -} pending_pick; - -typedef struct rr_subchannel_list rr_subchannel_list; -typedef struct round_robin_lb_policy { - /** base policy: must be first */ - grpc_lb_policy base; - - rr_subchannel_list *subchannel_list; - - /** have we started picking? */ - bool started_picking; - /** are we shutting down? */ - bool shutdown; - /** List of picks that are waiting on connectivity */ - pending_pick *pending_picks; - - /** our connectivity state tracker */ - grpc_connectivity_state_tracker state_tracker; - - /** Index into subchannels for last pick. */ - size_t last_ready_subchannel_index; - - /** Latest version of the subchannel list. - * Subchannel connectivity callbacks will only promote updated subchannel - * lists if they equal \a latest_pending_subchannel_list. In other words, - * racing callbacks that reference outdated subchannel lists won't perform any - * update. */ - rr_subchannel_list *latest_pending_subchannel_list; -} round_robin_lb_policy; - -typedef struct { - /** backpointer to owning subchannel list */ - rr_subchannel_list *subchannel_list; - /** subchannel itself */ - grpc_subchannel *subchannel; - /** notification that connectivity has changed on subchannel */ - grpc_closure connectivity_changed_closure; - /** last observed connectivity. Not updated by - * \a grpc_subchannel_notify_on_state_change. Used to determine the previous - * state while processing the new state in \a rr_connectivity_changed */ - grpc_connectivity_state prev_connectivity_state; - /** current connectivity state. Updated by \a - * grpc_subchannel_notify_on_state_change */ - grpc_connectivity_state curr_connectivity_state; - /** connectivity state to be updated by the watcher, not guarded by - * the combiner. Will be moved to curr_connectivity_state inside of - * the combiner by rr_connectivity_changed_locked(). */ - grpc_connectivity_state pending_connectivity_state_unsafe; - /** the subchannel's target user data */ - void *user_data; - /** vtable to operate over \a user_data */ - const grpc_lb_user_data_vtable *user_data_vtable; -} subchannel_data; - -struct rr_subchannel_list { - /** backpointer to owning policy */ - round_robin_lb_policy *policy; - - /** all our subchannels */ - size_t num_subchannels; - subchannel_data *subchannels; - - /** how many subchannels are in state READY */ - size_t num_ready; - /** how many subchannels are in state TRANSIENT_FAILURE */ - size_t num_transient_failures; - /** how many subchannels are in state SHUTDOWN */ - size_t num_shutdown; - /** how many subchannels are in state IDLE */ - size_t num_idle; - - /** There will be one ref for each entry in subchannels for which there is a - * pending connectivity state watcher callback. */ - gpr_refcount refcount; - - /** Is this list shutting down? This may be true due to the shutdown of the - * policy itself or because a newer update has arrived while this one hadn't - * finished processing. */ - bool shutting_down; -}; - -static rr_subchannel_list *rr_subchannel_list_create(round_robin_lb_policy *p, - size_t num_subchannels) { - rr_subchannel_list *subchannel_list = - (rr_subchannel_list *)gpr_zalloc(sizeof(*subchannel_list)); - subchannel_list->policy = p; - subchannel_list->subchannels = - (subchannel_data *)gpr_zalloc(sizeof(subchannel_data) * num_subchannels); - subchannel_list->num_subchannels = num_subchannels; - gpr_ref_init(&subchannel_list->refcount, 1); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_INFO, "[RR %p] Created subchannel list %p for %lu subchannels", - (void *)p, (void *)subchannel_list, (unsigned long)num_subchannels); - } - return subchannel_list; -} - -static void rr_subchannel_list_destroy(grpc_exec_ctx *exec_ctx, - rr_subchannel_list *subchannel_list) { - GPR_ASSERT(subchannel_list->shutting_down); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_INFO, "[RR %p] Destroying subchannel_list %p", - (void *)subchannel_list->policy, (void *)subchannel_list); - } - for (size_t i = 0; i < subchannel_list->num_subchannels; i++) { - subchannel_data *sd = &subchannel_list->subchannels[i]; - if (sd->subchannel != NULL) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, - "rr_subchannel_list_destroy"); - } - sd->subchannel = NULL; - if (sd->user_data != NULL) { - GPR_ASSERT(sd->user_data_vtable != NULL); - sd->user_data_vtable->destroy(exec_ctx, sd->user_data); - sd->user_data = NULL; - } - } - gpr_free(subchannel_list->subchannels); - gpr_free(subchannel_list); -} - -static void rr_subchannel_list_ref(rr_subchannel_list *subchannel_list, - const char *reason) { - gpr_ref_non_zero(&subchannel_list->refcount); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count); - gpr_log(GPR_INFO, "[RR %p] subchannel_list %p REF %lu->%lu (%s)", - (void *)subchannel_list->policy, (void *)subchannel_list, - (unsigned long)(count - 1), (unsigned long)count, reason); - } -} - -static void rr_subchannel_list_unref(grpc_exec_ctx *exec_ctx, - rr_subchannel_list *subchannel_list, - const char *reason) { - const bool done = gpr_unref(&subchannel_list->refcount); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count); - gpr_log(GPR_INFO, "[RR %p] subchannel_list %p UNREF %lu->%lu (%s)", - (void *)subchannel_list->policy, (void *)subchannel_list, - (unsigned long)(count + 1), (unsigned long)count, reason); - } - if (done) { - rr_subchannel_list_destroy(exec_ctx, subchannel_list); - } -} - -/** Mark \a subchannel_list as discarded. Unsubscribes all its subchannels. The - * watcher's callback will ultimately unref \a subchannel_list. */ -static void rr_subchannel_list_shutdown_and_unref( - grpc_exec_ctx *exec_ctx, rr_subchannel_list *subchannel_list, - const char *reason) { - GPR_ASSERT(!subchannel_list->shutting_down); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, "[RR %p] Shutting down subchannel_list %p (%s)", - (void *)subchannel_list->policy, (void *)subchannel_list, reason); - } - GPR_ASSERT(!subchannel_list->shutting_down); - subchannel_list->shutting_down = true; - for (size_t i = 0; i < subchannel_list->num_subchannels; i++) { - subchannel_data *sd = &subchannel_list->subchannels[i]; - if (sd->subchannel != NULL) { // if subchannel isn't shutdown, unsubscribe. - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log( - GPR_DEBUG, - "[RR %p] Unsubscribing from subchannel %p as part of shutting down " - "subchannel_list %p", - (void *)subchannel_list->policy, (void *)sd->subchannel, - (void *)subchannel_list); - } - grpc_subchannel_notify_on_state_change(exec_ctx, sd->subchannel, NULL, - NULL, - &sd->connectivity_changed_closure); - } - } - rr_subchannel_list_unref(exec_ctx, subchannel_list, reason); -} - -/** Returns the index into p->subchannel_list->subchannels of the next - * subchannel in READY state, or p->subchannel_list->num_subchannels if no - * subchannel is READY. - * - * Note that this function does *not* update p->last_ready_subchannel_index. - * The caller must do that if it returns a pick. */ -static size_t get_next_ready_subchannel_index_locked( - const round_robin_lb_policy *p) { - GPR_ASSERT(p->subchannel_list != NULL); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_INFO, - "[RR %p] getting next ready subchannel (out of %lu), " - "last_ready_subchannel_index=%lu", - (void *)p, (unsigned long)p->subchannel_list->num_subchannels, - (unsigned long)p->last_ready_subchannel_index); - } - for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) { - const size_t index = (i + p->last_ready_subchannel_index + 1) % - p->subchannel_list->num_subchannels; - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log( - GPR_DEBUG, - "[RR %p] checking subchannel %p, subchannel_list %p, index %lu: " - "state=%s", - (void *)p, (void *)p->subchannel_list->subchannels[index].subchannel, - (void *)p->subchannel_list, (unsigned long)index, - grpc_connectivity_state_name( - p->subchannel_list->subchannels[index].curr_connectivity_state)); - } - if (p->subchannel_list->subchannels[index].curr_connectivity_state == - GRPC_CHANNEL_READY) { - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, - "[RR %p] found next ready subchannel (%p) at index %lu of " - "subchannel_list %p", - (void *)p, - (void *)p->subchannel_list->subchannels[index].subchannel, - (unsigned long)index, (void *)p->subchannel_list); - } - return index; - } - } - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, "[RR %p] no subchannels in ready state", (void *)p); - } - return p->subchannel_list->num_subchannels; -} - -// Sets p->last_ready_subchannel_index to last_ready_index. -static void update_last_ready_subchannel_index_locked(round_robin_lb_policy *p, - size_t last_ready_index) { - GPR_ASSERT(last_ready_index < p->subchannel_list->num_subchannels); - p->last_ready_subchannel_index = last_ready_index; - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log( - GPR_DEBUG, - "[RR %p] setting last_ready_subchannel_index=%lu (SC %p, CSC %p)", - (void *)p, (unsigned long)last_ready_index, - (void *)p->subchannel_list->subchannels[last_ready_index].subchannel, - (void *)grpc_subchannel_get_connected_subchannel( - p->subchannel_list->subchannels[last_ready_index].subchannel)); - } -} - -static void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, "[RR %p] Destroying Round Robin policy at %p", - (void *)pol, (void *)pol); - } - grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); - grpc_subchannel_index_unref(); - gpr_free(p); -} - -static void rr_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, "[RR %p] Shutting down Round Robin policy at %p", - (void *)pol, (void *)pol); - } - p->shutdown = true; - pending_pick *pp; - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = NULL; - GRPC_CLOSURE_SCHED( - exec_ctx, pp->on_complete, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Shutdown")); - gpr_free(pp); - } - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Shutdown"), "rr_shutdown"); - const bool latest_is_current = - p->subchannel_list == p->latest_pending_subchannel_list; - rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, - "sl_shutdown_rr_shutdown"); - p->subchannel_list = NULL; - if (!latest_is_current && p->latest_pending_subchannel_list != NULL && - !p->latest_pending_subchannel_list->shutting_down) { - rr_subchannel_list_shutdown_and_unref(exec_ctx, - p->latest_pending_subchannel_list, - "sl_shutdown_pending_rr_shutdown"); - p->latest_pending_subchannel_list = NULL; - } -} - -static void rr_cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_connected_subchannel **target, - grpc_error *error) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - pending_pick *pp = p->pending_picks; - p->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if (pp->target == target) { - *target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick cancelled", &error, 1)); - gpr_free(pp); - } else { - pp->next = p->pending_picks; - p->pending_picks = pp; - } - pp = next; - } - GRPC_ERROR_UNREF(error); -} - -static void rr_cancel_picks_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - uint32_t initial_metadata_flags_mask, - uint32_t initial_metadata_flags_eq, - grpc_error *error) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - pending_pick *pp = p->pending_picks; - p->pending_picks = NULL; - while (pp != NULL) { - pending_pick *next = pp->next; - if ((pp->initial_metadata_flags & initial_metadata_flags_mask) == - initial_metadata_flags_eq) { - *pp->target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Pick cancelled", &error, 1)); - gpr_free(pp); - } else { - pp->next = p->pending_picks; - p->pending_picks = pp; - } - pp = next; - } - GRPC_ERROR_UNREF(error); -} - -static void start_picking_locked(grpc_exec_ctx *exec_ctx, - round_robin_lb_policy *p) { - p->started_picking = true; - for (size_t i = 0; i < p->subchannel_list->num_subchannels; i++) { - subchannel_data *sd = &p->subchannel_list->subchannels[i]; - GRPC_LB_POLICY_WEAK_REF(&p->base, "start_picking_locked"); - rr_subchannel_list_ref(sd->subchannel_list, "started_picking"); - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, p->base.interested_parties, - &sd->pending_connectivity_state_unsafe, - &sd->connectivity_changed_closure); - } -} - -static void rr_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - if (!p->started_picking) { - start_picking_locked(exec_ctx, p); - } -} - -static int rr_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - const grpc_lb_policy_pick_args *pick_args, - grpc_connected_subchannel **target, - grpc_call_context_element *context, void **user_data, - grpc_closure *on_complete) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - GPR_ASSERT(!p->shutdown); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_INFO, "[RR %p] Trying to pick", (void *)pol); - } - if (p->subchannel_list != NULL) { - const size_t next_ready_index = get_next_ready_subchannel_index_locked(p); - if (next_ready_index < p->subchannel_list->num_subchannels) { - /* readily available, report right away */ - subchannel_data *sd = &p->subchannel_list->subchannels[next_ready_index]; - *target = GRPC_CONNECTED_SUBCHANNEL_REF( - grpc_subchannel_get_connected_subchannel(sd->subchannel), - "rr_picked"); - if (user_data != NULL) { - *user_data = sd->user_data; - } - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log( - GPR_DEBUG, - "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, " - "index %lu)", - (void *)p, (void *)sd->subchannel, (void *)*target, - (void *)sd->subchannel_list, (unsigned long)next_ready_index); - } - /* only advance the last picked pointer if the selection was used */ - update_last_ready_subchannel_index_locked(p, next_ready_index); - return 1; - } - } - /* no pick currently available. Save for later in list of pending picks */ - if (!p->started_picking) { - start_picking_locked(exec_ctx, p); - } - pending_pick *pp = (pending_pick *)gpr_malloc(sizeof(*pp)); - pp->next = p->pending_picks; - pp->target = target; - pp->on_complete = on_complete; - pp->initial_metadata_flags = pick_args->initial_metadata_flags; - pp->user_data = user_data; - p->pending_picks = pp; - return 0; -} - -static void update_state_counters_locked(subchannel_data *sd) { - rr_subchannel_list *subchannel_list = sd->subchannel_list; - if (sd->prev_connectivity_state == GRPC_CHANNEL_READY) { - GPR_ASSERT(subchannel_list->num_ready > 0); - --subchannel_list->num_ready; - } else if (sd->prev_connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { - GPR_ASSERT(subchannel_list->num_transient_failures > 0); - --subchannel_list->num_transient_failures; - } else if (sd->prev_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { - GPR_ASSERT(subchannel_list->num_shutdown > 0); - --subchannel_list->num_shutdown; - } else if (sd->prev_connectivity_state == GRPC_CHANNEL_IDLE) { - GPR_ASSERT(subchannel_list->num_idle > 0); - --subchannel_list->num_idle; - } - if (sd->curr_connectivity_state == GRPC_CHANNEL_READY) { - ++subchannel_list->num_ready; - } else if (sd->curr_connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { - ++subchannel_list->num_transient_failures; - } else if (sd->curr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { - ++subchannel_list->num_shutdown; - } else if (sd->curr_connectivity_state == GRPC_CHANNEL_IDLE) { - ++subchannel_list->num_idle; - } -} - -/** Sets the policy's connectivity status based on that of the passed-in \a sd - * (the subchannel_data associted with the updated subchannel) and the - * subchannel list \a sd belongs to (sd->subchannel_list). \a error will only be - * used upon policy transition to TRANSIENT_FAILURE or SHUTDOWN. Returns the - * connectivity status set. */ -static grpc_connectivity_state update_lb_connectivity_status_locked( - grpc_exec_ctx *exec_ctx, subchannel_data *sd, grpc_error *error) { - /* In priority order. The first rule to match terminates the search (ie, if we - * are on rule n, all previous rules were unfulfilled). - * - * 1) RULE: ANY subchannel is READY => policy is READY. - * CHECK: At least one subchannel is ready iff p->ready_list is NOT empty. - * - * 2) RULE: ANY subchannel is CONNECTING => policy is CONNECTING. - * CHECK: sd->curr_connectivity_state == CONNECTING. - * - * 3) RULE: ALL subchannels are SHUTDOWN => policy is SHUTDOWN. - * CHECK: p->subchannel_list->num_shutdown == - * p->subchannel_list->num_subchannels. - * - * 4) RULE: ALL subchannels are TRANSIENT_FAILURE => policy is - * TRANSIENT_FAILURE. - * CHECK: p->num_transient_failures == p->subchannel_list->num_subchannels. - * - * 5) RULE: ALL subchannels are IDLE => policy is IDLE. - * CHECK: p->num_idle == p->subchannel_list->num_subchannels. - */ - grpc_connectivity_state new_state = sd->curr_connectivity_state; - rr_subchannel_list *subchannel_list = sd->subchannel_list; - round_robin_lb_policy *p = subchannel_list->policy; - if (subchannel_list->num_ready > 0) { /* 1) READY */ - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_READY, - GRPC_ERROR_NONE, "rr_ready"); - new_state = GRPC_CHANNEL_READY; - } else if (sd->curr_connectivity_state == - GRPC_CHANNEL_CONNECTING) { /* 2) CONNECTING */ - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE, - "rr_connecting"); - new_state = GRPC_CHANNEL_CONNECTING; - } else if (p->subchannel_list->num_shutdown == - p->subchannel_list->num_subchannels) { /* 3) SHUTDOWN */ - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error), - "rr_shutdown"); - p->shutdown = true; - new_state = GRPC_CHANNEL_SHUTDOWN; - } else if (subchannel_list->num_transient_failures == - p->subchannel_list->num_subchannels) { /* 4) TRANSIENT_FAILURE */ - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, - GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_REF(error), "rr_transient_failure"); - new_state = GRPC_CHANNEL_TRANSIENT_FAILURE; - } else if (subchannel_list->num_idle == - p->subchannel_list->num_subchannels) { /* 5) IDLE */ - grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_IDLE, - GRPC_ERROR_NONE, "rr_idle"); - new_state = GRPC_CHANNEL_IDLE; - } - GRPC_ERROR_UNREF(error); - return new_state; -} - -static void rr_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - subchannel_data *sd = (subchannel_data *)arg; - round_robin_lb_policy *p = sd->subchannel_list->policy; - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log( - GPR_DEBUG, - "[RR %p] connectivity changed for subchannel %p, subchannel_list %p: " - "prev_state=%s new_state=%s p->shutdown=%d " - "sd->subchannel_list->shutting_down=%d error=%s", - (void *)p, (void *)sd->subchannel, (void *)sd->subchannel_list, - grpc_connectivity_state_name(sd->prev_connectivity_state), - grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe), - p->shutdown, sd->subchannel_list->shutting_down, - grpc_error_string(error)); - } - // If the policy is shutting down, unref and return. - if (p->shutdown) { - rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, - "pol_shutdown+started_picking"); - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pol_shutdown"); - return; - } - if (sd->subchannel_list->shutting_down && error == GRPC_ERROR_CANCELLED) { - // the subchannel list associated with sd has been discarded. This callback - // corresponds to the unsubscription. The unrefs correspond to the picking - // ref (start_picking_locked or update_started_picking). - rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, - "sl_shutdown+started_picking"); - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "sl_shutdown+picking"); - return; - } - // Dispose of outdated subchannel lists. - if (sd->subchannel_list != p->subchannel_list && - sd->subchannel_list != p->latest_pending_subchannel_list) { - const char *reason = NULL; - if (sd->subchannel_list->shutting_down) { - reason = "sl_outdated_straggler"; - rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, reason); - } else { - reason = "sl_outdated"; - rr_subchannel_list_shutdown_and_unref(exec_ctx, sd->subchannel_list, - reason); - } - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, reason); - return; - } - // Now that we're inside the combiner, copy the pending connectivity - // state (which was set by the connectivity state watcher) to - // curr_connectivity_state, which is what we use inside of the combiner. - sd->curr_connectivity_state = sd->pending_connectivity_state_unsafe; - // Update state counters and determine new overall state. - update_state_counters_locked(sd); - sd->prev_connectivity_state = sd->curr_connectivity_state; - const grpc_connectivity_state new_policy_connectivity_state = - update_lb_connectivity_status_locked(exec_ctx, sd, GRPC_ERROR_REF(error)); - // If the sd's new state is SHUTDOWN, unref the subchannel, and if the new - // policy's state is SHUTDOWN, clean up. - if (sd->curr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "rr_subchannel_shutdown"); - sd->subchannel = NULL; - if (sd->user_data != NULL) { - GPR_ASSERT(sd->user_data_vtable != NULL); - sd->user_data_vtable->destroy(exec_ctx, sd->user_data); - sd->user_data = NULL; - } - if (new_policy_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { - // the policy is shutting down. Flush all the pending picks... - pending_pick *pp; - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); - gpr_free(pp); - } - } - rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, - "sd_shutdown+started_picking"); - // unref the "rr_connectivity_update" weak ref from start_picking. - GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, - "rr_connectivity_sd_shutdown"); - } else { // sd not in SHUTDOWN - if (sd->curr_connectivity_state == GRPC_CHANNEL_READY) { - if (sd->subchannel_list != p->subchannel_list) { - // promote sd->subchannel_list to p->subchannel_list. - // sd->subchannel_list must be equal to - // p->latest_pending_subchannel_list because we have already filtered - // for sds belonging to outdated subchannel lists. - GPR_ASSERT(sd->subchannel_list == p->latest_pending_subchannel_list); - GPR_ASSERT(!sd->subchannel_list->shutting_down); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - const unsigned long num_subchannels = - p->subchannel_list != NULL - ? (unsigned long)p->subchannel_list->num_subchannels - : 0; - gpr_log(GPR_DEBUG, - "[RR %p] phasing out subchannel list %p (size %lu) in favor " - "of %p (size %lu)", - (void *)p, (void *)p->subchannel_list, num_subchannels, - (void *)sd->subchannel_list, num_subchannels); - } - if (p->subchannel_list != NULL) { - // dispose of the current subchannel_list - rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, - "sl_phase_out_shutdown"); - } - p->subchannel_list = p->latest_pending_subchannel_list; - p->latest_pending_subchannel_list = NULL; - } - /* at this point we know there's at least one suitable subchannel. Go - * ahead and pick one and notify the pending suitors in - * p->pending_picks. This preemtively replicates rr_pick()'s actions. */ - const size_t next_ready_index = get_next_ready_subchannel_index_locked(p); - GPR_ASSERT(next_ready_index < p->subchannel_list->num_subchannels); - subchannel_data *selected = - &p->subchannel_list->subchannels[next_ready_index]; - if (p->pending_picks != NULL) { - // if the selected subchannel is going to be used for the pending - // picks, update the last picked pointer - update_last_ready_subchannel_index_locked(p, next_ready_index); - } - pending_pick *pp; - while ((pp = p->pending_picks)) { - p->pending_picks = pp->next; - *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF( - grpc_subchannel_get_connected_subchannel(selected->subchannel), - "rr_picked"); - if (pp->user_data != NULL) { - *pp->user_data = selected->user_data; - } - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, - "[RR %p] Fulfilling pending pick. Target <-- subchannel %p " - "(subchannel_list %p, index %lu)", - (void *)p, (void *)selected->subchannel, - (void *)p->subchannel_list, (unsigned long)next_ready_index); - } - GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); - gpr_free(pp); - } - } - /* renew notification: reuses the "rr_connectivity_update" weak ref on the - * policy as well as the sd->subchannel_list ref. */ - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, p->base.interested_parties, - &sd->pending_connectivity_state_unsafe, - &sd->connectivity_changed_closure); - } -} - -static grpc_connectivity_state rr_check_connectivity_locked( - grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_error **error) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - return grpc_connectivity_state_get(&p->state_tracker, error); -} - -static void rr_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx, - grpc_lb_policy *pol, - grpc_connectivity_state *current, - grpc_closure *notify) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, - current, notify); -} - -static void rr_ping_one_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, - grpc_closure *closure) { - round_robin_lb_policy *p = (round_robin_lb_policy *)pol; - const size_t next_ready_index = get_next_ready_subchannel_index_locked(p); - if (next_ready_index < p->subchannel_list->num_subchannels) { - subchannel_data *selected = - &p->subchannel_list->subchannels[next_ready_index]; - grpc_connected_subchannel *target = GRPC_CONNECTED_SUBCHANNEL_REF( - grpc_subchannel_get_connected_subchannel(selected->subchannel), - "rr_picked"); - grpc_connected_subchannel_ping(exec_ctx, target, closure); - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, target, "rr_picked"); - } else { - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Round Robin not connected")); - } -} - -static void rr_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, - const grpc_lb_policy_args *args) { - round_robin_lb_policy *p = (round_robin_lb_policy *)policy; - const grpc_arg *arg = - grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); - if (arg == NULL || arg->type != GRPC_ARG_POINTER) { - if (p->subchannel_list == NULL) { - // If we don't have a current subchannel list, go into TRANSIENT FAILURE. - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), - "rr_update_missing"); - } else { - // otherwise, keep using the current subchannel list (ignore this update). - gpr_log(GPR_ERROR, - "[RR %p] No valid LB addresses channel arg for update, ignoring.", - (void *)p); - } - return; - } - grpc_lb_addresses *addresses = (grpc_lb_addresses *)arg->value.pointer.p; - rr_subchannel_list *subchannel_list = - rr_subchannel_list_create(p, addresses->num_addresses); - if (addresses->num_addresses == 0) { - grpc_connectivity_state_set( - exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), - "rr_update_empty"); - if (p->subchannel_list != NULL) { - rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, - "sl_shutdown_empty_update"); - } - p->subchannel_list = subchannel_list; // empty list - return; - } - size_t subchannel_index = 0; - if (p->latest_pending_subchannel_list != NULL && p->started_picking) { - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, - "[RR %p] Shutting down latest pending subchannel list %p, about " - "to be replaced by newer latest %p", - (void *)p, (void *)p->latest_pending_subchannel_list, - (void *)subchannel_list); - } - rr_subchannel_list_shutdown_and_unref( - exec_ctx, p->latest_pending_subchannel_list, "sl_outdated_dont_smash"); - } - p->latest_pending_subchannel_list = subchannel_list; - grpc_subchannel_args sc_args; - /* We need to remove the LB addresses in order to be able to compare the - * subchannel keys of subchannels from a different batch of addresses. */ - static const char *keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS, - GRPC_ARG_LB_ADDRESSES}; - /* Create subchannels for addresses in the update. */ - for (size_t i = 0; i < addresses->num_addresses; i++) { - // If there were any balancer, we would have chosen grpclb policy instead. - GPR_ASSERT(!addresses->addresses[i].is_balancer); - memset(&sc_args, 0, sizeof(grpc_subchannel_args)); - grpc_arg addr_arg = - grpc_create_subchannel_address_arg(&addresses->addresses[i].address); - grpc_channel_args *new_args = grpc_channel_args_copy_and_add_and_remove( - args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg, - 1); - gpr_free(addr_arg.value.string); - sc_args.args = new_args; - grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( - exec_ctx, args->client_channel_factory, &sc_args); - grpc_channel_args_destroy(exec_ctx, new_args); - grpc_error *error; - // Get the connectivity state of the subchannel. Already existing ones may - // be in a state other than INIT. - const grpc_connectivity_state subchannel_connectivity_state = - grpc_subchannel_check_connectivity(subchannel, &error); - if (error != GRPC_ERROR_NONE) { - // The subchannel is in error (e.g. shutting down). Ignore it. - GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannel, "new_sc_connectivity_error"); - GRPC_ERROR_UNREF(error); - continue; - } - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - char *address_uri = - grpc_sockaddr_to_uri(&addresses->addresses[i].address); - gpr_log( - GPR_DEBUG, - "[RR %p] index %lu: Created subchannel %p for address uri %s into " - "subchannel_list %p. Connectivity state %s", - (void *)p, (unsigned long)subchannel_index, (void *)subchannel, - address_uri, (void *)subchannel_list, - grpc_connectivity_state_name(subchannel_connectivity_state)); - gpr_free(address_uri); - } - subchannel_data *sd = &subchannel_list->subchannels[subchannel_index++]; - sd->subchannel_list = subchannel_list; - sd->subchannel = subchannel; - GRPC_CLOSURE_INIT(&sd->connectivity_changed_closure, - rr_connectivity_changed_locked, sd, - grpc_combiner_scheduler(args->combiner)); - /* use some sentinel value outside of the range of - * grpc_connectivity_state to signal an undefined previous state. We - * won't be referring to this value again and it'll be overwritten after - * the first call to rr_connectivity_changed_locked */ - sd->prev_connectivity_state = GRPC_CHANNEL_INIT; - sd->curr_connectivity_state = subchannel_connectivity_state; - sd->user_data_vtable = addresses->user_data_vtable; - if (sd->user_data_vtable != NULL) { - sd->user_data = - sd->user_data_vtable->copy(addresses->addresses[i].user_data); - } - if (p->started_picking) { - rr_subchannel_list_ref(sd->subchannel_list, "update_started_picking"); - GRPC_LB_POLICY_WEAK_REF(&p->base, "rr_connectivity_update"); - /* 2. Watch every new subchannel. A subchannel list becomes active the - * moment one of its subchannels is READY. At that moment, we swap - * p->subchannel_list for sd->subchannel_list, provided the subchannel - * list is still valid (ie, isn't shutting down) */ - grpc_subchannel_notify_on_state_change( - exec_ctx, sd->subchannel, p->base.interested_parties, - &sd->pending_connectivity_state_unsafe, - &sd->connectivity_changed_closure); - } - } - if (!p->started_picking) { - // The policy isn't picking yet. Save the update for later, disposing of - // previous version if any. - if (p->subchannel_list != NULL) { - rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, - "rr_update_before_started_picking"); - } - p->subchannel_list = subchannel_list; - p->latest_pending_subchannel_list = NULL; - } -} - -static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = { - rr_destroy, - rr_shutdown_locked, - rr_pick_locked, - rr_cancel_pick_locked, - rr_cancel_picks_locked, - rr_ping_one_locked, - rr_exit_idle_locked, - rr_check_connectivity_locked, - rr_notify_on_state_change_locked, - rr_update_locked}; - -static void round_robin_factory_ref(grpc_lb_policy_factory *factory) {} - -static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {} - -static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, - grpc_lb_policy_factory *factory, - grpc_lb_policy_args *args) { - GPR_ASSERT(args->client_channel_factory != NULL); - round_robin_lb_policy *p = (round_robin_lb_policy *)gpr_zalloc(sizeof(*p)); - grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable, args->combiner); - grpc_subchannel_index_ref(); - grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, - "round_robin"); - rr_update_locked(exec_ctx, &p->base, args); - if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { - gpr_log(GPR_DEBUG, "[RR %p] Created with %lu subchannels", (void *)p, - (unsigned long)p->subchannel_list->num_subchannels); - } - return &p->base; -} - -static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = { - round_robin_factory_ref, round_robin_factory_unref, round_robin_create, - "round_robin"}; - -static grpc_lb_policy_factory round_robin_lb_policy_factory = { - &round_robin_factory_vtable}; - -static grpc_lb_policy_factory *round_robin_lb_factory_create() { - return &round_robin_lb_policy_factory; -} - -/* Plugin registration */ - -void grpc_lb_policy_round_robin_init() { - grpc_register_lb_policy(round_robin_lb_factory_create()); - grpc_register_tracer(&grpc_lb_round_robin_trace); -} - -void grpc_lb_policy_round_robin_shutdown() {} diff --git a/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc new file mode 100644 index 0000000000..6812bb50cd --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc @@ -0,0 +1,924 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** Round Robin Policy. + * + * Before every pick, the \a get_next_ready_subchannel_index_locked function + * returns the p->subchannel_list->subchannels index for next subchannel, + * respecting the relative + * order of the addresses provided upon creation or updates. Note however that + * updates will start picking from the beginning of the updated list. */ + +#include + +#include + +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/static_metadata.h" + +grpc_tracer_flag grpc_lb_round_robin_trace = + GRPC_TRACER_INITIALIZER(false, "round_robin"); + +/** List of entities waiting for a pick. + * + * Once a pick is available, \a target is updated and \a on_complete called. */ +typedef struct pending_pick { + struct pending_pick *next; + + /* output argument where to store the pick()ed user_data. It'll be NULL if no + * such data is present or there's an error (the definite test for errors is + * \a target being NULL). */ + void **user_data; + + /* bitmask passed to pick() and used for selective cancelling. See + * grpc_lb_policy_cancel_picks() */ + uint32_t initial_metadata_flags; + + /* output argument where to store the pick()ed connected subchannel, or NULL + * upon error. */ + grpc_connected_subchannel **target; + + /* to be invoked once the pick() has completed (regardless of success) */ + grpc_closure *on_complete; +} pending_pick; + +typedef struct rr_subchannel_list rr_subchannel_list; +typedef struct round_robin_lb_policy { + /** base policy: must be first */ + grpc_lb_policy base; + + rr_subchannel_list *subchannel_list; + + /** have we started picking? */ + bool started_picking; + /** are we shutting down? */ + bool shutdown; + /** List of picks that are waiting on connectivity */ + pending_pick *pending_picks; + + /** our connectivity state tracker */ + grpc_connectivity_state_tracker state_tracker; + + /** Index into subchannels for last pick. */ + size_t last_ready_subchannel_index; + + /** Latest version of the subchannel list. + * Subchannel connectivity callbacks will only promote updated subchannel + * lists if they equal \a latest_pending_subchannel_list. In other words, + * racing callbacks that reference outdated subchannel lists won't perform any + * update. */ + rr_subchannel_list *latest_pending_subchannel_list; +} round_robin_lb_policy; + +typedef struct { + /** backpointer to owning subchannel list */ + rr_subchannel_list *subchannel_list; + /** subchannel itself */ + grpc_subchannel *subchannel; + /** notification that connectivity has changed on subchannel */ + grpc_closure connectivity_changed_closure; + /** last observed connectivity. Not updated by + * \a grpc_subchannel_notify_on_state_change. Used to determine the previous + * state while processing the new state in \a rr_connectivity_changed */ + grpc_connectivity_state prev_connectivity_state; + /** current connectivity state. Updated by \a + * grpc_subchannel_notify_on_state_change */ + grpc_connectivity_state curr_connectivity_state; + /** connectivity state to be updated by the watcher, not guarded by + * the combiner. Will be moved to curr_connectivity_state inside of + * the combiner by rr_connectivity_changed_locked(). */ + grpc_connectivity_state pending_connectivity_state_unsafe; + /** the subchannel's target user data */ + void *user_data; + /** vtable to operate over \a user_data */ + const grpc_lb_user_data_vtable *user_data_vtable; +} subchannel_data; + +struct rr_subchannel_list { + /** backpointer to owning policy */ + round_robin_lb_policy *policy; + + /** all our subchannels */ + size_t num_subchannels; + subchannel_data *subchannels; + + /** how many subchannels are in state READY */ + size_t num_ready; + /** how many subchannels are in state TRANSIENT_FAILURE */ + size_t num_transient_failures; + /** how many subchannels are in state SHUTDOWN */ + size_t num_shutdown; + /** how many subchannels are in state IDLE */ + size_t num_idle; + + /** There will be one ref for each entry in subchannels for which there is a + * pending connectivity state watcher callback. */ + gpr_refcount refcount; + + /** Is this list shutting down? This may be true due to the shutdown of the + * policy itself or because a newer update has arrived while this one hadn't + * finished processing. */ + bool shutting_down; +}; + +static rr_subchannel_list *rr_subchannel_list_create(round_robin_lb_policy *p, + size_t num_subchannels) { + rr_subchannel_list *subchannel_list = + (rr_subchannel_list *)gpr_zalloc(sizeof(*subchannel_list)); + subchannel_list->policy = p; + subchannel_list->subchannels = + (subchannel_data *)gpr_zalloc(sizeof(subchannel_data) * num_subchannels); + subchannel_list->num_subchannels = num_subchannels; + gpr_ref_init(&subchannel_list->refcount, 1); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_INFO, "[RR %p] Created subchannel list %p for %lu subchannels", + (void *)p, (void *)subchannel_list, (unsigned long)num_subchannels); + } + return subchannel_list; +} + +static void rr_subchannel_list_destroy(grpc_exec_ctx *exec_ctx, + rr_subchannel_list *subchannel_list) { + GPR_ASSERT(subchannel_list->shutting_down); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_INFO, "[RR %p] Destroying subchannel_list %p", + (void *)subchannel_list->policy, (void *)subchannel_list); + } + for (size_t i = 0; i < subchannel_list->num_subchannels; i++) { + subchannel_data *sd = &subchannel_list->subchannels[i]; + if (sd->subchannel != NULL) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, + "rr_subchannel_list_destroy"); + } + sd->subchannel = NULL; + if (sd->user_data != NULL) { + GPR_ASSERT(sd->user_data_vtable != NULL); + sd->user_data_vtable->destroy(exec_ctx, sd->user_data); + sd->user_data = NULL; + } + } + gpr_free(subchannel_list->subchannels); + gpr_free(subchannel_list); +} + +static void rr_subchannel_list_ref(rr_subchannel_list *subchannel_list, + const char *reason) { + gpr_ref_non_zero(&subchannel_list->refcount); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count); + gpr_log(GPR_INFO, "[RR %p] subchannel_list %p REF %lu->%lu (%s)", + (void *)subchannel_list->policy, (void *)subchannel_list, + (unsigned long)(count - 1), (unsigned long)count, reason); + } +} + +static void rr_subchannel_list_unref(grpc_exec_ctx *exec_ctx, + rr_subchannel_list *subchannel_list, + const char *reason) { + const bool done = gpr_unref(&subchannel_list->refcount); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + const gpr_atm count = gpr_atm_acq_load(&subchannel_list->refcount.count); + gpr_log(GPR_INFO, "[RR %p] subchannel_list %p UNREF %lu->%lu (%s)", + (void *)subchannel_list->policy, (void *)subchannel_list, + (unsigned long)(count + 1), (unsigned long)count, reason); + } + if (done) { + rr_subchannel_list_destroy(exec_ctx, subchannel_list); + } +} + +/** Mark \a subchannel_list as discarded. Unsubscribes all its subchannels. The + * watcher's callback will ultimately unref \a subchannel_list. */ +static void rr_subchannel_list_shutdown_and_unref( + grpc_exec_ctx *exec_ctx, rr_subchannel_list *subchannel_list, + const char *reason) { + GPR_ASSERT(!subchannel_list->shutting_down); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, "[RR %p] Shutting down subchannel_list %p (%s)", + (void *)subchannel_list->policy, (void *)subchannel_list, reason); + } + GPR_ASSERT(!subchannel_list->shutting_down); + subchannel_list->shutting_down = true; + for (size_t i = 0; i < subchannel_list->num_subchannels; i++) { + subchannel_data *sd = &subchannel_list->subchannels[i]; + if (sd->subchannel != NULL) { // if subchannel isn't shutdown, unsubscribe. + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log( + GPR_DEBUG, + "[RR %p] Unsubscribing from subchannel %p as part of shutting down " + "subchannel_list %p", + (void *)subchannel_list->policy, (void *)sd->subchannel, + (void *)subchannel_list); + } + grpc_subchannel_notify_on_state_change(exec_ctx, sd->subchannel, NULL, + NULL, + &sd->connectivity_changed_closure); + } + } + rr_subchannel_list_unref(exec_ctx, subchannel_list, reason); +} + +/** Returns the index into p->subchannel_list->subchannels of the next + * subchannel in READY state, or p->subchannel_list->num_subchannels if no + * subchannel is READY. + * + * Note that this function does *not* update p->last_ready_subchannel_index. + * The caller must do that if it returns a pick. */ +static size_t get_next_ready_subchannel_index_locked( + const round_robin_lb_policy *p) { + GPR_ASSERT(p->subchannel_list != NULL); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_INFO, + "[RR %p] getting next ready subchannel (out of %lu), " + "last_ready_subchannel_index=%lu", + (void *)p, (unsigned long)p->subchannel_list->num_subchannels, + (unsigned long)p->last_ready_subchannel_index); + } + for (size_t i = 0; i < p->subchannel_list->num_subchannels; ++i) { + const size_t index = (i + p->last_ready_subchannel_index + 1) % + p->subchannel_list->num_subchannels; + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log( + GPR_DEBUG, + "[RR %p] checking subchannel %p, subchannel_list %p, index %lu: " + "state=%s", + (void *)p, (void *)p->subchannel_list->subchannels[index].subchannel, + (void *)p->subchannel_list, (unsigned long)index, + grpc_connectivity_state_name( + p->subchannel_list->subchannels[index].curr_connectivity_state)); + } + if (p->subchannel_list->subchannels[index].curr_connectivity_state == + GRPC_CHANNEL_READY) { + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, + "[RR %p] found next ready subchannel (%p) at index %lu of " + "subchannel_list %p", + (void *)p, + (void *)p->subchannel_list->subchannels[index].subchannel, + (unsigned long)index, (void *)p->subchannel_list); + } + return index; + } + } + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, "[RR %p] no subchannels in ready state", (void *)p); + } + return p->subchannel_list->num_subchannels; +} + +// Sets p->last_ready_subchannel_index to last_ready_index. +static void update_last_ready_subchannel_index_locked(round_robin_lb_policy *p, + size_t last_ready_index) { + GPR_ASSERT(last_ready_index < p->subchannel_list->num_subchannels); + p->last_ready_subchannel_index = last_ready_index; + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log( + GPR_DEBUG, + "[RR %p] setting last_ready_subchannel_index=%lu (SC %p, CSC %p)", + (void *)p, (unsigned long)last_ready_index, + (void *)p->subchannel_list->subchannels[last_ready_index].subchannel, + (void *)grpc_subchannel_get_connected_subchannel( + p->subchannel_list->subchannels[last_ready_index].subchannel)); + } +} + +static void rr_destroy(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, "[RR %p] Destroying Round Robin policy at %p", + (void *)pol, (void *)pol); + } + grpc_connectivity_state_destroy(exec_ctx, &p->state_tracker); + grpc_subchannel_index_unref(); + gpr_free(p); +} + +static void rr_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, "[RR %p] Shutting down Round Robin policy at %p", + (void *)pol, (void *)pol); + } + p->shutdown = true; + pending_pick *pp; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + GRPC_CLOSURE_SCHED( + exec_ctx, pp->on_complete, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Shutdown")); + gpr_free(pp); + } + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Shutdown"), "rr_shutdown"); + const bool latest_is_current = + p->subchannel_list == p->latest_pending_subchannel_list; + rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, + "sl_shutdown_rr_shutdown"); + p->subchannel_list = NULL; + if (!latest_is_current && p->latest_pending_subchannel_list != NULL && + !p->latest_pending_subchannel_list->shutting_down) { + rr_subchannel_list_shutdown_and_unref(exec_ctx, + p->latest_pending_subchannel_list, + "sl_shutdown_pending_rr_shutdown"); + p->latest_pending_subchannel_list = NULL; + } +} + +static void rr_cancel_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_connected_subchannel **target, + grpc_error *error) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + pending_pick *pp = p->pending_picks; + p->pending_picks = NULL; + while (pp != NULL) { + pending_pick *next = pp->next; + if (pp->target == target) { + *target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick cancelled", &error, 1)); + gpr_free(pp); + } else { + pp->next = p->pending_picks; + p->pending_picks = pp; + } + pp = next; + } + GRPC_ERROR_UNREF(error); +} + +static void rr_cancel_picks_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + uint32_t initial_metadata_flags_mask, + uint32_t initial_metadata_flags_eq, + grpc_error *error) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + pending_pick *pp = p->pending_picks; + p->pending_picks = NULL; + while (pp != NULL) { + pending_pick *next = pp->next; + if ((pp->initial_metadata_flags & initial_metadata_flags_mask) == + initial_metadata_flags_eq) { + *pp->target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Pick cancelled", &error, 1)); + gpr_free(pp); + } else { + pp->next = p->pending_picks; + p->pending_picks = pp; + } + pp = next; + } + GRPC_ERROR_UNREF(error); +} + +static void start_picking_locked(grpc_exec_ctx *exec_ctx, + round_robin_lb_policy *p) { + p->started_picking = true; + for (size_t i = 0; i < p->subchannel_list->num_subchannels; i++) { + subchannel_data *sd = &p->subchannel_list->subchannels[i]; + GRPC_LB_POLICY_WEAK_REF(&p->base, "start_picking_locked"); + rr_subchannel_list_ref(sd->subchannel_list, "started_picking"); + grpc_subchannel_notify_on_state_change( + exec_ctx, sd->subchannel, p->base.interested_parties, + &sd->pending_connectivity_state_unsafe, + &sd->connectivity_changed_closure); + } +} + +static void rr_exit_idle_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + if (!p->started_picking) { + start_picking_locked(exec_ctx, p); + } +} + +static int rr_pick_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + const grpc_lb_policy_pick_args *pick_args, + grpc_connected_subchannel **target, + grpc_call_context_element *context, void **user_data, + grpc_closure *on_complete) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + GPR_ASSERT(!p->shutdown); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_INFO, "[RR %p] Trying to pick", (void *)pol); + } + if (p->subchannel_list != NULL) { + const size_t next_ready_index = get_next_ready_subchannel_index_locked(p); + if (next_ready_index < p->subchannel_list->num_subchannels) { + /* readily available, report right away */ + subchannel_data *sd = &p->subchannel_list->subchannels[next_ready_index]; + *target = GRPC_CONNECTED_SUBCHANNEL_REF( + grpc_subchannel_get_connected_subchannel(sd->subchannel), + "rr_picked"); + if (user_data != NULL) { + *user_data = sd->user_data; + } + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log( + GPR_DEBUG, + "[RR %p] Picked target <-- Subchannel %p (connected %p) (sl %p, " + "index %lu)", + (void *)p, (void *)sd->subchannel, (void *)*target, + (void *)sd->subchannel_list, (unsigned long)next_ready_index); + } + /* only advance the last picked pointer if the selection was used */ + update_last_ready_subchannel_index_locked(p, next_ready_index); + return 1; + } + } + /* no pick currently available. Save for later in list of pending picks */ + if (!p->started_picking) { + start_picking_locked(exec_ctx, p); + } + pending_pick *pp = (pending_pick *)gpr_malloc(sizeof(*pp)); + pp->next = p->pending_picks; + pp->target = target; + pp->on_complete = on_complete; + pp->initial_metadata_flags = pick_args->initial_metadata_flags; + pp->user_data = user_data; + p->pending_picks = pp; + return 0; +} + +static void update_state_counters_locked(subchannel_data *sd) { + rr_subchannel_list *subchannel_list = sd->subchannel_list; + if (sd->prev_connectivity_state == GRPC_CHANNEL_READY) { + GPR_ASSERT(subchannel_list->num_ready > 0); + --subchannel_list->num_ready; + } else if (sd->prev_connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + GPR_ASSERT(subchannel_list->num_transient_failures > 0); + --subchannel_list->num_transient_failures; + } else if (sd->prev_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { + GPR_ASSERT(subchannel_list->num_shutdown > 0); + --subchannel_list->num_shutdown; + } else if (sd->prev_connectivity_state == GRPC_CHANNEL_IDLE) { + GPR_ASSERT(subchannel_list->num_idle > 0); + --subchannel_list->num_idle; + } + if (sd->curr_connectivity_state == GRPC_CHANNEL_READY) { + ++subchannel_list->num_ready; + } else if (sd->curr_connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + ++subchannel_list->num_transient_failures; + } else if (sd->curr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { + ++subchannel_list->num_shutdown; + } else if (sd->curr_connectivity_state == GRPC_CHANNEL_IDLE) { + ++subchannel_list->num_idle; + } +} + +/** Sets the policy's connectivity status based on that of the passed-in \a sd + * (the subchannel_data associted with the updated subchannel) and the + * subchannel list \a sd belongs to (sd->subchannel_list). \a error will only be + * used upon policy transition to TRANSIENT_FAILURE or SHUTDOWN. Returns the + * connectivity status set. */ +static grpc_connectivity_state update_lb_connectivity_status_locked( + grpc_exec_ctx *exec_ctx, subchannel_data *sd, grpc_error *error) { + /* In priority order. The first rule to match terminates the search (ie, if we + * are on rule n, all previous rules were unfulfilled). + * + * 1) RULE: ANY subchannel is READY => policy is READY. + * CHECK: At least one subchannel is ready iff p->ready_list is NOT empty. + * + * 2) RULE: ANY subchannel is CONNECTING => policy is CONNECTING. + * CHECK: sd->curr_connectivity_state == CONNECTING. + * + * 3) RULE: ALL subchannels are SHUTDOWN => policy is SHUTDOWN. + * CHECK: p->subchannel_list->num_shutdown == + * p->subchannel_list->num_subchannels. + * + * 4) RULE: ALL subchannels are TRANSIENT_FAILURE => policy is + * TRANSIENT_FAILURE. + * CHECK: p->num_transient_failures == p->subchannel_list->num_subchannels. + * + * 5) RULE: ALL subchannels are IDLE => policy is IDLE. + * CHECK: p->num_idle == p->subchannel_list->num_subchannels. + */ + grpc_connectivity_state new_state = sd->curr_connectivity_state; + rr_subchannel_list *subchannel_list = sd->subchannel_list; + round_robin_lb_policy *p = subchannel_list->policy; + if (subchannel_list->num_ready > 0) { /* 1) READY */ + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_READY, + GRPC_ERROR_NONE, "rr_ready"); + new_state = GRPC_CHANNEL_READY; + } else if (sd->curr_connectivity_state == + GRPC_CHANNEL_CONNECTING) { /* 2) CONNECTING */ + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE, + "rr_connecting"); + new_state = GRPC_CHANNEL_CONNECTING; + } else if (p->subchannel_list->num_shutdown == + p->subchannel_list->num_subchannels) { /* 3) SHUTDOWN */ + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_SHUTDOWN, GRPC_ERROR_REF(error), + "rr_shutdown"); + p->shutdown = true; + new_state = GRPC_CHANNEL_SHUTDOWN; + } else if (subchannel_list->num_transient_failures == + p->subchannel_list->num_subchannels) { /* 4) TRANSIENT_FAILURE */ + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, + GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_REF(error), "rr_transient_failure"); + new_state = GRPC_CHANNEL_TRANSIENT_FAILURE; + } else if (subchannel_list->num_idle == + p->subchannel_list->num_subchannels) { /* 5) IDLE */ + grpc_connectivity_state_set(exec_ctx, &p->state_tracker, GRPC_CHANNEL_IDLE, + GRPC_ERROR_NONE, "rr_idle"); + new_state = GRPC_CHANNEL_IDLE; + } + GRPC_ERROR_UNREF(error); + return new_state; +} + +static void rr_connectivity_changed_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + subchannel_data *sd = (subchannel_data *)arg; + round_robin_lb_policy *p = sd->subchannel_list->policy; + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log( + GPR_DEBUG, + "[RR %p] connectivity changed for subchannel %p, subchannel_list %p: " + "prev_state=%s new_state=%s p->shutdown=%d " + "sd->subchannel_list->shutting_down=%d error=%s", + (void *)p, (void *)sd->subchannel, (void *)sd->subchannel_list, + grpc_connectivity_state_name(sd->prev_connectivity_state), + grpc_connectivity_state_name(sd->pending_connectivity_state_unsafe), + p->shutdown, sd->subchannel_list->shutting_down, + grpc_error_string(error)); + } + // If the policy is shutting down, unref and return. + if (p->shutdown) { + rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, + "pol_shutdown+started_picking"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "pol_shutdown"); + return; + } + if (sd->subchannel_list->shutting_down && error == GRPC_ERROR_CANCELLED) { + // the subchannel list associated with sd has been discarded. This callback + // corresponds to the unsubscription. The unrefs correspond to the picking + // ref (start_picking_locked or update_started_picking). + rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, + "sl_shutdown+started_picking"); + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, "sl_shutdown+picking"); + return; + } + // Dispose of outdated subchannel lists. + if (sd->subchannel_list != p->subchannel_list && + sd->subchannel_list != p->latest_pending_subchannel_list) { + const char *reason = NULL; + if (sd->subchannel_list->shutting_down) { + reason = "sl_outdated_straggler"; + rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, reason); + } else { + reason = "sl_outdated"; + rr_subchannel_list_shutdown_and_unref(exec_ctx, sd->subchannel_list, + reason); + } + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, reason); + return; + } + // Now that we're inside the combiner, copy the pending connectivity + // state (which was set by the connectivity state watcher) to + // curr_connectivity_state, which is what we use inside of the combiner. + sd->curr_connectivity_state = sd->pending_connectivity_state_unsafe; + // Update state counters and determine new overall state. + update_state_counters_locked(sd); + sd->prev_connectivity_state = sd->curr_connectivity_state; + const grpc_connectivity_state new_policy_connectivity_state = + update_lb_connectivity_status_locked(exec_ctx, sd, GRPC_ERROR_REF(error)); + // If the sd's new state is SHUTDOWN, unref the subchannel, and if the new + // policy's state is SHUTDOWN, clean up. + if (sd->curr_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, sd->subchannel, "rr_subchannel_shutdown"); + sd->subchannel = NULL; + if (sd->user_data != NULL) { + GPR_ASSERT(sd->user_data_vtable != NULL); + sd->user_data_vtable->destroy(exec_ctx, sd->user_data); + sd->user_data = NULL; + } + if (new_policy_connectivity_state == GRPC_CHANNEL_SHUTDOWN) { + // the policy is shutting down. Flush all the pending picks... + pending_pick *pp; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); + gpr_free(pp); + } + } + rr_subchannel_list_unref(exec_ctx, sd->subchannel_list, + "sd_shutdown+started_picking"); + // unref the "rr_connectivity_update" weak ref from start_picking. + GRPC_LB_POLICY_WEAK_UNREF(exec_ctx, &p->base, + "rr_connectivity_sd_shutdown"); + } else { // sd not in SHUTDOWN + if (sd->curr_connectivity_state == GRPC_CHANNEL_READY) { + if (sd->subchannel_list != p->subchannel_list) { + // promote sd->subchannel_list to p->subchannel_list. + // sd->subchannel_list must be equal to + // p->latest_pending_subchannel_list because we have already filtered + // for sds belonging to outdated subchannel lists. + GPR_ASSERT(sd->subchannel_list == p->latest_pending_subchannel_list); + GPR_ASSERT(!sd->subchannel_list->shutting_down); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + const unsigned long num_subchannels = + p->subchannel_list != NULL + ? (unsigned long)p->subchannel_list->num_subchannels + : 0; + gpr_log(GPR_DEBUG, + "[RR %p] phasing out subchannel list %p (size %lu) in favor " + "of %p (size %lu)", + (void *)p, (void *)p->subchannel_list, num_subchannels, + (void *)sd->subchannel_list, num_subchannels); + } + if (p->subchannel_list != NULL) { + // dispose of the current subchannel_list + rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, + "sl_phase_out_shutdown"); + } + p->subchannel_list = p->latest_pending_subchannel_list; + p->latest_pending_subchannel_list = NULL; + } + /* at this point we know there's at least one suitable subchannel. Go + * ahead and pick one and notify the pending suitors in + * p->pending_picks. This preemtively replicates rr_pick()'s actions. */ + const size_t next_ready_index = get_next_ready_subchannel_index_locked(p); + GPR_ASSERT(next_ready_index < p->subchannel_list->num_subchannels); + subchannel_data *selected = + &p->subchannel_list->subchannels[next_ready_index]; + if (p->pending_picks != NULL) { + // if the selected subchannel is going to be used for the pending + // picks, update the last picked pointer + update_last_ready_subchannel_index_locked(p, next_ready_index); + } + pending_pick *pp; + while ((pp = p->pending_picks)) { + p->pending_picks = pp->next; + *pp->target = GRPC_CONNECTED_SUBCHANNEL_REF( + grpc_subchannel_get_connected_subchannel(selected->subchannel), + "rr_picked"); + if (pp->user_data != NULL) { + *pp->user_data = selected->user_data; + } + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, + "[RR %p] Fulfilling pending pick. Target <-- subchannel %p " + "(subchannel_list %p, index %lu)", + (void *)p, (void *)selected->subchannel, + (void *)p->subchannel_list, (unsigned long)next_ready_index); + } + GRPC_CLOSURE_SCHED(exec_ctx, pp->on_complete, GRPC_ERROR_NONE); + gpr_free(pp); + } + } + /* renew notification: reuses the "rr_connectivity_update" weak ref on the + * policy as well as the sd->subchannel_list ref. */ + grpc_subchannel_notify_on_state_change( + exec_ctx, sd->subchannel, p->base.interested_parties, + &sd->pending_connectivity_state_unsafe, + &sd->connectivity_changed_closure); + } +} + +static grpc_connectivity_state rr_check_connectivity_locked( + grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, grpc_error **error) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + return grpc_connectivity_state_get(&p->state_tracker, error); +} + +static void rr_notify_on_state_change_locked(grpc_exec_ctx *exec_ctx, + grpc_lb_policy *pol, + grpc_connectivity_state *current, + grpc_closure *notify) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + grpc_connectivity_state_notify_on_state_change(exec_ctx, &p->state_tracker, + current, notify); +} + +static void rr_ping_one_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *pol, + grpc_closure *closure) { + round_robin_lb_policy *p = (round_robin_lb_policy *)pol; + const size_t next_ready_index = get_next_ready_subchannel_index_locked(p); + if (next_ready_index < p->subchannel_list->num_subchannels) { + subchannel_data *selected = + &p->subchannel_list->subchannels[next_ready_index]; + grpc_connected_subchannel *target = GRPC_CONNECTED_SUBCHANNEL_REF( + grpc_subchannel_get_connected_subchannel(selected->subchannel), + "rr_picked"); + grpc_connected_subchannel_ping(exec_ctx, target, closure); + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, target, "rr_picked"); + } else { + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Round Robin not connected")); + } +} + +static void rr_update_locked(grpc_exec_ctx *exec_ctx, grpc_lb_policy *policy, + const grpc_lb_policy_args *args) { + round_robin_lb_policy *p = (round_robin_lb_policy *)policy; + const grpc_arg *arg = + grpc_channel_args_find(args->args, GRPC_ARG_LB_ADDRESSES); + if (arg == NULL || arg->type != GRPC_ARG_POINTER) { + if (p->subchannel_list == NULL) { + // If we don't have a current subchannel list, go into TRANSIENT FAILURE. + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing update in args"), + "rr_update_missing"); + } else { + // otherwise, keep using the current subchannel list (ignore this update). + gpr_log(GPR_ERROR, + "[RR %p] No valid LB addresses channel arg for update, ignoring.", + (void *)p); + } + return; + } + grpc_lb_addresses *addresses = (grpc_lb_addresses *)arg->value.pointer.p; + rr_subchannel_list *subchannel_list = + rr_subchannel_list_create(p, addresses->num_addresses); + if (addresses->num_addresses == 0) { + grpc_connectivity_state_set( + exec_ctx, &p->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), + "rr_update_empty"); + if (p->subchannel_list != NULL) { + rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, + "sl_shutdown_empty_update"); + } + p->subchannel_list = subchannel_list; // empty list + return; + } + size_t subchannel_index = 0; + if (p->latest_pending_subchannel_list != NULL && p->started_picking) { + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, + "[RR %p] Shutting down latest pending subchannel list %p, about " + "to be replaced by newer latest %p", + (void *)p, (void *)p->latest_pending_subchannel_list, + (void *)subchannel_list); + } + rr_subchannel_list_shutdown_and_unref( + exec_ctx, p->latest_pending_subchannel_list, "sl_outdated_dont_smash"); + } + p->latest_pending_subchannel_list = subchannel_list; + grpc_subchannel_args sc_args; + /* We need to remove the LB addresses in order to be able to compare the + * subchannel keys of subchannels from a different batch of addresses. */ + static const char *keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS, + GRPC_ARG_LB_ADDRESSES}; + /* Create subchannels for addresses in the update. */ + for (size_t i = 0; i < addresses->num_addresses; i++) { + // If there were any balancer, we would have chosen grpclb policy instead. + GPR_ASSERT(!addresses->addresses[i].is_balancer); + memset(&sc_args, 0, sizeof(grpc_subchannel_args)); + grpc_arg addr_arg = + grpc_create_subchannel_address_arg(&addresses->addresses[i].address); + grpc_channel_args *new_args = grpc_channel_args_copy_and_add_and_remove( + args->args, keys_to_remove, GPR_ARRAY_SIZE(keys_to_remove), &addr_arg, + 1); + gpr_free(addr_arg.value.string); + sc_args.args = new_args; + grpc_subchannel *subchannel = grpc_client_channel_factory_create_subchannel( + exec_ctx, args->client_channel_factory, &sc_args); + grpc_channel_args_destroy(exec_ctx, new_args); + grpc_error *error; + // Get the connectivity state of the subchannel. Already existing ones may + // be in a state other than INIT. + const grpc_connectivity_state subchannel_connectivity_state = + grpc_subchannel_check_connectivity(subchannel, &error); + if (error != GRPC_ERROR_NONE) { + // The subchannel is in error (e.g. shutting down). Ignore it. + GRPC_SUBCHANNEL_UNREF(exec_ctx, subchannel, "new_sc_connectivity_error"); + GRPC_ERROR_UNREF(error); + continue; + } + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + char *address_uri = + grpc_sockaddr_to_uri(&addresses->addresses[i].address); + gpr_log( + GPR_DEBUG, + "[RR %p] index %lu: Created subchannel %p for address uri %s into " + "subchannel_list %p. Connectivity state %s", + (void *)p, (unsigned long)subchannel_index, (void *)subchannel, + address_uri, (void *)subchannel_list, + grpc_connectivity_state_name(subchannel_connectivity_state)); + gpr_free(address_uri); + } + subchannel_data *sd = &subchannel_list->subchannels[subchannel_index++]; + sd->subchannel_list = subchannel_list; + sd->subchannel = subchannel; + GRPC_CLOSURE_INIT(&sd->connectivity_changed_closure, + rr_connectivity_changed_locked, sd, + grpc_combiner_scheduler(args->combiner)); + /* use some sentinel value outside of the range of + * grpc_connectivity_state to signal an undefined previous state. We + * won't be referring to this value again and it'll be overwritten after + * the first call to rr_connectivity_changed_locked */ + sd->prev_connectivity_state = GRPC_CHANNEL_INIT; + sd->curr_connectivity_state = subchannel_connectivity_state; + sd->user_data_vtable = addresses->user_data_vtable; + if (sd->user_data_vtable != NULL) { + sd->user_data = + sd->user_data_vtable->copy(addresses->addresses[i].user_data); + } + if (p->started_picking) { + rr_subchannel_list_ref(sd->subchannel_list, "update_started_picking"); + GRPC_LB_POLICY_WEAK_REF(&p->base, "rr_connectivity_update"); + /* 2. Watch every new subchannel. A subchannel list becomes active the + * moment one of its subchannels is READY. At that moment, we swap + * p->subchannel_list for sd->subchannel_list, provided the subchannel + * list is still valid (ie, isn't shutting down) */ + grpc_subchannel_notify_on_state_change( + exec_ctx, sd->subchannel, p->base.interested_parties, + &sd->pending_connectivity_state_unsafe, + &sd->connectivity_changed_closure); + } + } + if (!p->started_picking) { + // The policy isn't picking yet. Save the update for later, disposing of + // previous version if any. + if (p->subchannel_list != NULL) { + rr_subchannel_list_shutdown_and_unref(exec_ctx, p->subchannel_list, + "rr_update_before_started_picking"); + } + p->subchannel_list = subchannel_list; + p->latest_pending_subchannel_list = NULL; + } +} + +static const grpc_lb_policy_vtable round_robin_lb_policy_vtable = { + rr_destroy, + rr_shutdown_locked, + rr_pick_locked, + rr_cancel_pick_locked, + rr_cancel_picks_locked, + rr_ping_one_locked, + rr_exit_idle_locked, + rr_check_connectivity_locked, + rr_notify_on_state_change_locked, + rr_update_locked}; + +static void round_robin_factory_ref(grpc_lb_policy_factory *factory) {} + +static void round_robin_factory_unref(grpc_lb_policy_factory *factory) {} + +static grpc_lb_policy *round_robin_create(grpc_exec_ctx *exec_ctx, + grpc_lb_policy_factory *factory, + grpc_lb_policy_args *args) { + GPR_ASSERT(args->client_channel_factory != NULL); + round_robin_lb_policy *p = (round_robin_lb_policy *)gpr_zalloc(sizeof(*p)); + grpc_lb_policy_init(&p->base, &round_robin_lb_policy_vtable, args->combiner); + grpc_subchannel_index_ref(); + grpc_connectivity_state_init(&p->state_tracker, GRPC_CHANNEL_IDLE, + "round_robin"); + rr_update_locked(exec_ctx, &p->base, args); + if (GRPC_TRACER_ON(grpc_lb_round_robin_trace)) { + gpr_log(GPR_DEBUG, "[RR %p] Created with %lu subchannels", (void *)p, + (unsigned long)p->subchannel_list->num_subchannels); + } + return &p->base; +} + +static const grpc_lb_policy_factory_vtable round_robin_factory_vtable = { + round_robin_factory_ref, round_robin_factory_unref, round_robin_create, + "round_robin"}; + +static grpc_lb_policy_factory round_robin_lb_policy_factory = { + &round_robin_factory_vtable}; + +static grpc_lb_policy_factory *round_robin_lb_factory_create() { + return &round_robin_lb_policy_factory; +} + +/* Plugin registration */ + +extern "C" void grpc_lb_policy_round_robin_init() { + grpc_register_lb_policy(round_robin_lb_factory_create()); + grpc_register_tracer(&grpc_lb_round_robin_trace); +} + +extern "C" void grpc_lb_policy_round_robin_shutdown() {} diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.c b/src/core/ext/filters/client_channel/lb_policy_factory.c deleted file mode 100644 index 05ab43d0b6..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy_factory.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" - -#include "src/core/ext/filters/client_channel/lb_policy_factory.h" -#include "src/core/ext/filters/client_channel/parse_address.h" - -grpc_lb_addresses* grpc_lb_addresses_create( - size_t num_addresses, const grpc_lb_user_data_vtable* user_data_vtable) { - grpc_lb_addresses* addresses = - (grpc_lb_addresses*)gpr_zalloc(sizeof(grpc_lb_addresses)); - addresses->num_addresses = num_addresses; - addresses->user_data_vtable = user_data_vtable; - const size_t addresses_size = sizeof(grpc_lb_address) * num_addresses; - addresses->addresses = (grpc_lb_address*)gpr_zalloc(addresses_size); - return addresses; -} - -grpc_lb_addresses* grpc_lb_addresses_copy(const grpc_lb_addresses* addresses) { - grpc_lb_addresses* new_addresses = grpc_lb_addresses_create( - addresses->num_addresses, addresses->user_data_vtable); - memcpy(new_addresses->addresses, addresses->addresses, - sizeof(grpc_lb_address) * addresses->num_addresses); - for (size_t i = 0; i < addresses->num_addresses; ++i) { - if (new_addresses->addresses[i].balancer_name != NULL) { - new_addresses->addresses[i].balancer_name = - gpr_strdup(new_addresses->addresses[i].balancer_name); - } - if (new_addresses->addresses[i].user_data != NULL) { - new_addresses->addresses[i].user_data = addresses->user_data_vtable->copy( - new_addresses->addresses[i].user_data); - } - } - return new_addresses; -} - -void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, - const void* address, size_t address_len, - bool is_balancer, const char* balancer_name, - void* user_data) { - GPR_ASSERT(index < addresses->num_addresses); - if (user_data != NULL) GPR_ASSERT(addresses->user_data_vtable != NULL); - grpc_lb_address* target = &addresses->addresses[index]; - memcpy(target->address.addr, address, address_len); - target->address.len = address_len; - target->is_balancer = is_balancer; - target->balancer_name = gpr_strdup(balancer_name); - target->user_data = user_data; -} - -bool grpc_lb_addresses_set_address_from_uri(grpc_lb_addresses* addresses, - size_t index, const grpc_uri* uri, - bool is_balancer, - const char* balancer_name, - void* user_data) { - grpc_resolved_address address; - if (!grpc_parse_uri(uri, &address)) return false; - grpc_lb_addresses_set_address(addresses, index, address.addr, address.len, - is_balancer, balancer_name, user_data); - return true; -} - -int grpc_lb_addresses_cmp(const grpc_lb_addresses* addresses1, - const grpc_lb_addresses* addresses2) { - if (addresses1->num_addresses > addresses2->num_addresses) return 1; - if (addresses1->num_addresses < addresses2->num_addresses) return -1; - if (addresses1->user_data_vtable > addresses2->user_data_vtable) return 1; - if (addresses1->user_data_vtable < addresses2->user_data_vtable) return -1; - for (size_t i = 0; i < addresses1->num_addresses; ++i) { - const grpc_lb_address* target1 = &addresses1->addresses[i]; - const grpc_lb_address* target2 = &addresses2->addresses[i]; - if (target1->address.len > target2->address.len) return 1; - if (target1->address.len < target2->address.len) return -1; - int retval = memcmp(target1->address.addr, target2->address.addr, - target1->address.len); - if (retval != 0) return retval; - if (target1->is_balancer > target2->is_balancer) return 1; - if (target1->is_balancer < target2->is_balancer) return -1; - const char* balancer_name1 = - target1->balancer_name != NULL ? target1->balancer_name : ""; - const char* balancer_name2 = - target2->balancer_name != NULL ? target2->balancer_name : ""; - retval = strcmp(balancer_name1, balancer_name2); - if (retval != 0) return retval; - if (addresses1->user_data_vtable != NULL) { - retval = addresses1->user_data_vtable->cmp(target1->user_data, - target2->user_data); - if (retval != 0) return retval; - } - } - return 0; -} - -void grpc_lb_addresses_destroy(grpc_exec_ctx* exec_ctx, - grpc_lb_addresses* addresses) { - for (size_t i = 0; i < addresses->num_addresses; ++i) { - gpr_free(addresses->addresses[i].balancer_name); - if (addresses->addresses[i].user_data != NULL) { - addresses->user_data_vtable->destroy(exec_ctx, - addresses->addresses[i].user_data); - } - } - gpr_free(addresses->addresses); - gpr_free(addresses); -} - -static void* lb_addresses_copy(void* addresses) { - return grpc_lb_addresses_copy((grpc_lb_addresses*)addresses); -} -static void lb_addresses_destroy(grpc_exec_ctx* exec_ctx, void* addresses) { - grpc_lb_addresses_destroy(exec_ctx, (grpc_lb_addresses*)addresses); -} -static int lb_addresses_cmp(void* addresses1, void* addresses2) { - return grpc_lb_addresses_cmp((grpc_lb_addresses*)addresses1, - (grpc_lb_addresses*)addresses2); -} -static const grpc_arg_pointer_vtable lb_addresses_arg_vtable = { - lb_addresses_copy, lb_addresses_destroy, lb_addresses_cmp}; - -grpc_arg grpc_lb_addresses_create_channel_arg( - const grpc_lb_addresses* addresses) { - return grpc_channel_arg_pointer_create( - (char*)GRPC_ARG_LB_ADDRESSES, (void*)addresses, &lb_addresses_arg_vtable); -} - -grpc_lb_addresses* grpc_lb_addresses_find_channel_arg( - const grpc_channel_args* channel_args) { - const grpc_arg* lb_addresses_arg = - grpc_channel_args_find(channel_args, GRPC_ARG_LB_ADDRESSES); - if (lb_addresses_arg == NULL || lb_addresses_arg->type != GRPC_ARG_POINTER) - return NULL; - return (grpc_lb_addresses*)lb_addresses_arg->value.pointer.p; -} - -void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) { - factory->vtable->ref(factory); -} - -void grpc_lb_policy_factory_unref(grpc_lb_policy_factory* factory) { - factory->vtable->unref(factory); -} - -grpc_lb_policy* grpc_lb_policy_factory_create_lb_policy( - grpc_exec_ctx* exec_ctx, grpc_lb_policy_factory* factory, - grpc_lb_policy_args* args) { - if (factory == NULL) return NULL; - return factory->vtable->create_lb_policy(exec_ctx, factory, args); -} diff --git a/src/core/ext/filters/client_channel/lb_policy_factory.cc b/src/core/ext/filters/client_channel/lb_policy_factory.cc new file mode 100644 index 0000000000..05ab43d0b6 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy_factory.cc @@ -0,0 +1,169 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/channel/channel_args.h" + +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/parse_address.h" + +grpc_lb_addresses* grpc_lb_addresses_create( + size_t num_addresses, const grpc_lb_user_data_vtable* user_data_vtable) { + grpc_lb_addresses* addresses = + (grpc_lb_addresses*)gpr_zalloc(sizeof(grpc_lb_addresses)); + addresses->num_addresses = num_addresses; + addresses->user_data_vtable = user_data_vtable; + const size_t addresses_size = sizeof(grpc_lb_address) * num_addresses; + addresses->addresses = (grpc_lb_address*)gpr_zalloc(addresses_size); + return addresses; +} + +grpc_lb_addresses* grpc_lb_addresses_copy(const grpc_lb_addresses* addresses) { + grpc_lb_addresses* new_addresses = grpc_lb_addresses_create( + addresses->num_addresses, addresses->user_data_vtable); + memcpy(new_addresses->addresses, addresses->addresses, + sizeof(grpc_lb_address) * addresses->num_addresses); + for (size_t i = 0; i < addresses->num_addresses; ++i) { + if (new_addresses->addresses[i].balancer_name != NULL) { + new_addresses->addresses[i].balancer_name = + gpr_strdup(new_addresses->addresses[i].balancer_name); + } + if (new_addresses->addresses[i].user_data != NULL) { + new_addresses->addresses[i].user_data = addresses->user_data_vtable->copy( + new_addresses->addresses[i].user_data); + } + } + return new_addresses; +} + +void grpc_lb_addresses_set_address(grpc_lb_addresses* addresses, size_t index, + const void* address, size_t address_len, + bool is_balancer, const char* balancer_name, + void* user_data) { + GPR_ASSERT(index < addresses->num_addresses); + if (user_data != NULL) GPR_ASSERT(addresses->user_data_vtable != NULL); + grpc_lb_address* target = &addresses->addresses[index]; + memcpy(target->address.addr, address, address_len); + target->address.len = address_len; + target->is_balancer = is_balancer; + target->balancer_name = gpr_strdup(balancer_name); + target->user_data = user_data; +} + +bool grpc_lb_addresses_set_address_from_uri(grpc_lb_addresses* addresses, + size_t index, const grpc_uri* uri, + bool is_balancer, + const char* balancer_name, + void* user_data) { + grpc_resolved_address address; + if (!grpc_parse_uri(uri, &address)) return false; + grpc_lb_addresses_set_address(addresses, index, address.addr, address.len, + is_balancer, balancer_name, user_data); + return true; +} + +int grpc_lb_addresses_cmp(const grpc_lb_addresses* addresses1, + const grpc_lb_addresses* addresses2) { + if (addresses1->num_addresses > addresses2->num_addresses) return 1; + if (addresses1->num_addresses < addresses2->num_addresses) return -1; + if (addresses1->user_data_vtable > addresses2->user_data_vtable) return 1; + if (addresses1->user_data_vtable < addresses2->user_data_vtable) return -1; + for (size_t i = 0; i < addresses1->num_addresses; ++i) { + const grpc_lb_address* target1 = &addresses1->addresses[i]; + const grpc_lb_address* target2 = &addresses2->addresses[i]; + if (target1->address.len > target2->address.len) return 1; + if (target1->address.len < target2->address.len) return -1; + int retval = memcmp(target1->address.addr, target2->address.addr, + target1->address.len); + if (retval != 0) return retval; + if (target1->is_balancer > target2->is_balancer) return 1; + if (target1->is_balancer < target2->is_balancer) return -1; + const char* balancer_name1 = + target1->balancer_name != NULL ? target1->balancer_name : ""; + const char* balancer_name2 = + target2->balancer_name != NULL ? target2->balancer_name : ""; + retval = strcmp(balancer_name1, balancer_name2); + if (retval != 0) return retval; + if (addresses1->user_data_vtable != NULL) { + retval = addresses1->user_data_vtable->cmp(target1->user_data, + target2->user_data); + if (retval != 0) return retval; + } + } + return 0; +} + +void grpc_lb_addresses_destroy(grpc_exec_ctx* exec_ctx, + grpc_lb_addresses* addresses) { + for (size_t i = 0; i < addresses->num_addresses; ++i) { + gpr_free(addresses->addresses[i].balancer_name); + if (addresses->addresses[i].user_data != NULL) { + addresses->user_data_vtable->destroy(exec_ctx, + addresses->addresses[i].user_data); + } + } + gpr_free(addresses->addresses); + gpr_free(addresses); +} + +static void* lb_addresses_copy(void* addresses) { + return grpc_lb_addresses_copy((grpc_lb_addresses*)addresses); +} +static void lb_addresses_destroy(grpc_exec_ctx* exec_ctx, void* addresses) { + grpc_lb_addresses_destroy(exec_ctx, (grpc_lb_addresses*)addresses); +} +static int lb_addresses_cmp(void* addresses1, void* addresses2) { + return grpc_lb_addresses_cmp((grpc_lb_addresses*)addresses1, + (grpc_lb_addresses*)addresses2); +} +static const grpc_arg_pointer_vtable lb_addresses_arg_vtable = { + lb_addresses_copy, lb_addresses_destroy, lb_addresses_cmp}; + +grpc_arg grpc_lb_addresses_create_channel_arg( + const grpc_lb_addresses* addresses) { + return grpc_channel_arg_pointer_create( + (char*)GRPC_ARG_LB_ADDRESSES, (void*)addresses, &lb_addresses_arg_vtable); +} + +grpc_lb_addresses* grpc_lb_addresses_find_channel_arg( + const grpc_channel_args* channel_args) { + const grpc_arg* lb_addresses_arg = + grpc_channel_args_find(channel_args, GRPC_ARG_LB_ADDRESSES); + if (lb_addresses_arg == NULL || lb_addresses_arg->type != GRPC_ARG_POINTER) + return NULL; + return (grpc_lb_addresses*)lb_addresses_arg->value.pointer.p; +} + +void grpc_lb_policy_factory_ref(grpc_lb_policy_factory* factory) { + factory->vtable->ref(factory); +} + +void grpc_lb_policy_factory_unref(grpc_lb_policy_factory* factory) { + factory->vtable->unref(factory); +} + +grpc_lb_policy* grpc_lb_policy_factory_create_lb_policy( + grpc_exec_ctx* exec_ctx, grpc_lb_policy_factory* factory, + grpc_lb_policy_args* args) { + if (factory == NULL) return NULL; + return factory->vtable->create_lb_policy(exec_ctx, factory, args); +} diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.c b/src/core/ext/filters/client_channel/lb_policy_registry.c deleted file mode 100644 index f2460f8304..0000000000 --- a/src/core/ext/filters/client_channel/lb_policy_registry.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" - -#include - -#include "src/core/lib/support/string.h" - -#define MAX_POLICIES 10 - -static grpc_lb_policy_factory *g_all_of_the_lb_policies[MAX_POLICIES]; -static int g_number_of_lb_policies = 0; - -void grpc_lb_policy_registry_init(void) { g_number_of_lb_policies = 0; } - -void grpc_lb_policy_registry_shutdown(void) { - int i; - for (i = 0; i < g_number_of_lb_policies; i++) { - grpc_lb_policy_factory_unref(g_all_of_the_lb_policies[i]); - } -} - -void grpc_register_lb_policy(grpc_lb_policy_factory *factory) { - int i; - for (i = 0; i < g_number_of_lb_policies; i++) { - GPR_ASSERT(0 != gpr_stricmp(factory->vtable->name, - g_all_of_the_lb_policies[i]->vtable->name)); - } - GPR_ASSERT(g_number_of_lb_policies != MAX_POLICIES); - grpc_lb_policy_factory_ref(factory); - g_all_of_the_lb_policies[g_number_of_lb_policies++] = factory; -} - -static grpc_lb_policy_factory *lookup_factory(const char *name) { - int i; - - if (name == NULL) return NULL; - - for (i = 0; i < g_number_of_lb_policies; i++) { - if (0 == gpr_stricmp(name, g_all_of_the_lb_policies[i]->vtable->name)) { - return g_all_of_the_lb_policies[i]; - } - } - - return NULL; -} - -grpc_lb_policy *grpc_lb_policy_create(grpc_exec_ctx *exec_ctx, const char *name, - grpc_lb_policy_args *args) { - grpc_lb_policy_factory *factory = lookup_factory(name); - grpc_lb_policy *lb_policy = - grpc_lb_policy_factory_create_lb_policy(exec_ctx, factory, args); - return lb_policy; -} diff --git a/src/core/ext/filters/client_channel/lb_policy_registry.cc b/src/core/ext/filters/client_channel/lb_policy_registry.cc new file mode 100644 index 0000000000..f2460f8304 --- /dev/null +++ b/src/core/ext/filters/client_channel/lb_policy_registry.cc @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" + +#include + +#include "src/core/lib/support/string.h" + +#define MAX_POLICIES 10 + +static grpc_lb_policy_factory *g_all_of_the_lb_policies[MAX_POLICIES]; +static int g_number_of_lb_policies = 0; + +void grpc_lb_policy_registry_init(void) { g_number_of_lb_policies = 0; } + +void grpc_lb_policy_registry_shutdown(void) { + int i; + for (i = 0; i < g_number_of_lb_policies; i++) { + grpc_lb_policy_factory_unref(g_all_of_the_lb_policies[i]); + } +} + +void grpc_register_lb_policy(grpc_lb_policy_factory *factory) { + int i; + for (i = 0; i < g_number_of_lb_policies; i++) { + GPR_ASSERT(0 != gpr_stricmp(factory->vtable->name, + g_all_of_the_lb_policies[i]->vtable->name)); + } + GPR_ASSERT(g_number_of_lb_policies != MAX_POLICIES); + grpc_lb_policy_factory_ref(factory); + g_all_of_the_lb_policies[g_number_of_lb_policies++] = factory; +} + +static grpc_lb_policy_factory *lookup_factory(const char *name) { + int i; + + if (name == NULL) return NULL; + + for (i = 0; i < g_number_of_lb_policies; i++) { + if (0 == gpr_stricmp(name, g_all_of_the_lb_policies[i]->vtable->name)) { + return g_all_of_the_lb_policies[i]; + } + } + + return NULL; +} + +grpc_lb_policy *grpc_lb_policy_create(grpc_exec_ctx *exec_ctx, const char *name, + grpc_lb_policy_args *args) { + grpc_lb_policy_factory *factory = lookup_factory(name); + grpc_lb_policy *lb_policy = + grpc_lb_policy_factory_create_lb_policy(exec_ctx, factory, args); + return lb_policy; +} diff --git a/src/core/ext/filters/client_channel/parse_address.c b/src/core/ext/filters/client_channel/parse_address.c deleted file mode 100644 index 2152b5a1e9..0000000000 --- a/src/core/ext/filters/client_channel/parse_address.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/parse_address.h" -#include "src/core/lib/iomgr/sockaddr.h" - -#include -#include -#ifdef GRPC_HAVE_UNIX_SOCKET -#include -#endif - -#include -#include -#include -#include -#include "src/core/lib/support/string.h" - -#ifdef GRPC_HAVE_UNIX_SOCKET - -bool grpc_parse_unix(const grpc_uri *uri, - grpc_resolved_address *resolved_addr) { - if (strcmp("unix", uri->scheme) != 0) { - gpr_log(GPR_ERROR, "Expected 'unix' scheme, got '%s'", uri->scheme); - return false; - } - struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; - const size_t maxlen = sizeof(un->sun_path); - const size_t path_len = strnlen(uri->path, maxlen); - if (path_len == maxlen) return false; - un->sun_family = AF_UNIX; - strcpy(un->sun_path, uri->path); - resolved_addr->len = sizeof(*un); - return true; -} - -#else /* GRPC_HAVE_UNIX_SOCKET */ - -bool grpc_parse_unix(const grpc_uri *uri, - grpc_resolved_address *resolved_addr) { - abort(); -} - -#endif /* GRPC_HAVE_UNIX_SOCKET */ - -bool grpc_parse_ipv4_hostport(const char *hostport, grpc_resolved_address *addr, - bool log_errors) { - bool success = false; - // Split host and port. - char *host; - char *port; - if (!gpr_split_host_port(hostport, &host, &port)) return false; - // Parse IP address. - memset(addr, 0, sizeof(*addr)); - addr->len = sizeof(struct sockaddr_in); - struct sockaddr_in *in = (struct sockaddr_in *)addr->addr; - in->sin_family = AF_INET; - if (inet_pton(AF_INET, host, &in->sin_addr) == 0) { - if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); - goto done; - } - // Parse port. - if (port == NULL) { - if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv4 scheme"); - goto done; - } - int port_num; - if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { - if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); - goto done; - } - in->sin_port = htons((uint16_t)port_num); - success = true; -done: - gpr_free(host); - gpr_free(port); - return success; -} - -bool grpc_parse_ipv4(const grpc_uri *uri, - grpc_resolved_address *resolved_addr) { - if (strcmp("ipv4", uri->scheme) != 0) { - gpr_log(GPR_ERROR, "Expected 'ipv4' scheme, got '%s'", uri->scheme); - return false; - } - const char *host_port = uri->path; - if (*host_port == '/') ++host_port; - return grpc_parse_ipv4_hostport(host_port, resolved_addr, - true /* log_errors */); -} - -bool grpc_parse_ipv6_hostport(const char *hostport, grpc_resolved_address *addr, - bool log_errors) { - bool success = false; - // Split host and port. - char *host; - char *port; - if (!gpr_split_host_port(hostport, &host, &port)) return false; - // Parse IP address. - memset(addr, 0, sizeof(*addr)); - addr->len = sizeof(struct sockaddr_in6); - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr->addr; - in6->sin6_family = AF_INET6; - // Handle the RFC6874 syntax for IPv6 zone identifiers. - char *host_end = (char *)gpr_memrchr(host, '%', strlen(host)); - if (host_end != NULL) { - GPR_ASSERT(host_end >= host); - char host_without_scope[INET6_ADDRSTRLEN]; - size_t host_without_scope_len = (size_t)(host_end - host); - uint32_t sin6_scope_id = 0; - strncpy(host_without_scope, host, host_without_scope_len); - host_without_scope[host_without_scope_len] = '\0'; - if (inet_pton(AF_INET6, host_without_scope, &in6->sin6_addr) == 0) { - gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); - goto done; - } - if (gpr_parse_bytes_to_uint32(host_end + 1, - strlen(host) - host_without_scope_len - 1, - &sin6_scope_id) == 0) { - gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1); - goto done; - } - // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027. - in6->sin6_scope_id = sin6_scope_id; - } else { - if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) { - gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); - goto done; - } - } - // Parse port. - if (port == NULL) { - if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv6 scheme"); - goto done; - } - int port_num; - if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { - if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); - goto done; - } - in6->sin6_port = htons((uint16_t)port_num); - success = true; -done: - gpr_free(host); - gpr_free(port); - return success; -} - -bool grpc_parse_ipv6(const grpc_uri *uri, - grpc_resolved_address *resolved_addr) { - if (strcmp("ipv6", uri->scheme) != 0) { - gpr_log(GPR_ERROR, "Expected 'ipv6' scheme, got '%s'", uri->scheme); - return false; - } - const char *host_port = uri->path; - if (*host_port == '/') ++host_port; - return grpc_parse_ipv6_hostport(host_port, resolved_addr, - true /* log_errors */); -} - -bool grpc_parse_uri(const grpc_uri *uri, grpc_resolved_address *resolved_addr) { - if (strcmp("unix", uri->scheme) == 0) { - return grpc_parse_unix(uri, resolved_addr); - } else if (strcmp("ipv4", uri->scheme) == 0) { - return grpc_parse_ipv4(uri, resolved_addr); - } else if (strcmp("ipv6", uri->scheme) == 0) { - return grpc_parse_ipv6(uri, resolved_addr); - } - gpr_log(GPR_ERROR, "Can't parse scheme '%s'", uri->scheme); - return false; -} diff --git a/src/core/ext/filters/client_channel/parse_address.cc b/src/core/ext/filters/client_channel/parse_address.cc new file mode 100644 index 0000000000..2152b5a1e9 --- /dev/null +++ b/src/core/ext/filters/client_channel/parse_address.cc @@ -0,0 +1,186 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include +#ifdef GRPC_HAVE_UNIX_SOCKET +#include +#endif + +#include +#include +#include +#include +#include "src/core/lib/support/string.h" + +#ifdef GRPC_HAVE_UNIX_SOCKET + +bool grpc_parse_unix(const grpc_uri *uri, + grpc_resolved_address *resolved_addr) { + if (strcmp("unix", uri->scheme) != 0) { + gpr_log(GPR_ERROR, "Expected 'unix' scheme, got '%s'", uri->scheme); + return false; + } + struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; + const size_t maxlen = sizeof(un->sun_path); + const size_t path_len = strnlen(uri->path, maxlen); + if (path_len == maxlen) return false; + un->sun_family = AF_UNIX; + strcpy(un->sun_path, uri->path); + resolved_addr->len = sizeof(*un); + return true; +} + +#else /* GRPC_HAVE_UNIX_SOCKET */ + +bool grpc_parse_unix(const grpc_uri *uri, + grpc_resolved_address *resolved_addr) { + abort(); +} + +#endif /* GRPC_HAVE_UNIX_SOCKET */ + +bool grpc_parse_ipv4_hostport(const char *hostport, grpc_resolved_address *addr, + bool log_errors) { + bool success = false; + // Split host and port. + char *host; + char *port; + if (!gpr_split_host_port(hostport, &host, &port)) return false; + // Parse IP address. + memset(addr, 0, sizeof(*addr)); + addr->len = sizeof(struct sockaddr_in); + struct sockaddr_in *in = (struct sockaddr_in *)addr->addr; + in->sin_family = AF_INET; + if (inet_pton(AF_INET, host, &in->sin_addr) == 0) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 address: '%s'", host); + goto done; + } + // Parse port. + if (port == NULL) { + if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv4 scheme"); + goto done; + } + int port_num; + if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv4 port: '%s'", port); + goto done; + } + in->sin_port = htons((uint16_t)port_num); + success = true; +done: + gpr_free(host); + gpr_free(port); + return success; +} + +bool grpc_parse_ipv4(const grpc_uri *uri, + grpc_resolved_address *resolved_addr) { + if (strcmp("ipv4", uri->scheme) != 0) { + gpr_log(GPR_ERROR, "Expected 'ipv4' scheme, got '%s'", uri->scheme); + return false; + } + const char *host_port = uri->path; + if (*host_port == '/') ++host_port; + return grpc_parse_ipv4_hostport(host_port, resolved_addr, + true /* log_errors */); +} + +bool grpc_parse_ipv6_hostport(const char *hostport, grpc_resolved_address *addr, + bool log_errors) { + bool success = false; + // Split host and port. + char *host; + char *port; + if (!gpr_split_host_port(hostport, &host, &port)) return false; + // Parse IP address. + memset(addr, 0, sizeof(*addr)); + addr->len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr->addr; + in6->sin6_family = AF_INET6; + // Handle the RFC6874 syntax for IPv6 zone identifiers. + char *host_end = (char *)gpr_memrchr(host, '%', strlen(host)); + if (host_end != NULL) { + GPR_ASSERT(host_end >= host); + char host_without_scope[INET6_ADDRSTRLEN]; + size_t host_without_scope_len = (size_t)(host_end - host); + uint32_t sin6_scope_id = 0; + strncpy(host_without_scope, host, host_without_scope_len); + host_without_scope[host_without_scope_len] = '\0'; + if (inet_pton(AF_INET6, host_without_scope, &in6->sin6_addr) == 0) { + gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host_without_scope); + goto done; + } + if (gpr_parse_bytes_to_uint32(host_end + 1, + strlen(host) - host_without_scope_len - 1, + &sin6_scope_id) == 0) { + gpr_log(GPR_ERROR, "invalid ipv6 scope id: '%s'", host_end + 1); + goto done; + } + // Handle "sin6_scope_id" being type "u_long". See grpc issue #10027. + in6->sin6_scope_id = sin6_scope_id; + } else { + if (inet_pton(AF_INET6, host, &in6->sin6_addr) == 0) { + gpr_log(GPR_ERROR, "invalid ipv6 address: '%s'", host); + goto done; + } + } + // Parse port. + if (port == NULL) { + if (log_errors) gpr_log(GPR_ERROR, "no port given for ipv6 scheme"); + goto done; + } + int port_num; + if (sscanf(port, "%d", &port_num) != 1 || port_num < 0 || port_num > 65535) { + if (log_errors) gpr_log(GPR_ERROR, "invalid ipv6 port: '%s'", port); + goto done; + } + in6->sin6_port = htons((uint16_t)port_num); + success = true; +done: + gpr_free(host); + gpr_free(port); + return success; +} + +bool grpc_parse_ipv6(const grpc_uri *uri, + grpc_resolved_address *resolved_addr) { + if (strcmp("ipv6", uri->scheme) != 0) { + gpr_log(GPR_ERROR, "Expected 'ipv6' scheme, got '%s'", uri->scheme); + return false; + } + const char *host_port = uri->path; + if (*host_port == '/') ++host_port; + return grpc_parse_ipv6_hostport(host_port, resolved_addr, + true /* log_errors */); +} + +bool grpc_parse_uri(const grpc_uri *uri, grpc_resolved_address *resolved_addr) { + if (strcmp("unix", uri->scheme) == 0) { + return grpc_parse_unix(uri, resolved_addr); + } else if (strcmp("ipv4", uri->scheme) == 0) { + return grpc_parse_ipv4(uri, resolved_addr); + } else if (strcmp("ipv6", uri->scheme) == 0) { + return grpc_parse_ipv6(uri, resolved_addr); + } + gpr_log(GPR_ERROR, "Can't parse scheme '%s'", uri->scheme); + return false; +} diff --git a/src/core/ext/filters/client_channel/proxy_mapper.c b/src/core/ext/filters/client_channel/proxy_mapper.c deleted file mode 100644 index c6ea5fc680..0000000000 --- a/src/core/ext/filters/client_channel/proxy_mapper.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/proxy_mapper.h" - -void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable, - grpc_proxy_mapper* mapper) { - mapper->vtable = vtable; -} - -bool grpc_proxy_mapper_map_name(grpc_exec_ctx* exec_ctx, - grpc_proxy_mapper* mapper, - const char* server_uri, - const grpc_channel_args* args, - char** name_to_resolve, - grpc_channel_args** new_args) { - return mapper->vtable->map_name(exec_ctx, mapper, server_uri, args, - name_to_resolve, new_args); -} - -bool grpc_proxy_mapper_map_address(grpc_exec_ctx* exec_ctx, - grpc_proxy_mapper* mapper, - const grpc_resolved_address* address, - const grpc_channel_args* args, - grpc_resolved_address** new_address, - grpc_channel_args** new_args) { - return mapper->vtable->map_address(exec_ctx, mapper, address, args, - new_address, new_args); -} - -void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper) { - mapper->vtable->destroy(mapper); -} diff --git a/src/core/ext/filters/client_channel/proxy_mapper.cc b/src/core/ext/filters/client_channel/proxy_mapper.cc new file mode 100644 index 0000000000..c6ea5fc680 --- /dev/null +++ b/src/core/ext/filters/client_channel/proxy_mapper.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/proxy_mapper.h" + +void grpc_proxy_mapper_init(const grpc_proxy_mapper_vtable* vtable, + grpc_proxy_mapper* mapper) { + mapper->vtable = vtable; +} + +bool grpc_proxy_mapper_map_name(grpc_exec_ctx* exec_ctx, + grpc_proxy_mapper* mapper, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + return mapper->vtable->map_name(exec_ctx, mapper, server_uri, args, + name_to_resolve, new_args); +} + +bool grpc_proxy_mapper_map_address(grpc_exec_ctx* exec_ctx, + grpc_proxy_mapper* mapper, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + return mapper->vtable->map_address(exec_ctx, mapper, address, args, + new_address, new_args); +} + +void grpc_proxy_mapper_destroy(grpc_proxy_mapper* mapper) { + mapper->vtable->destroy(mapper); +} diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.c b/src/core/ext/filters/client_channel/proxy_mapper_registry.c deleted file mode 100644 index 09967eea3c..0000000000 --- a/src/core/ext/filters/client_channel/proxy_mapper_registry.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" - -#include - -#include - -// -// grpc_proxy_mapper_list -// - -typedef struct { - grpc_proxy_mapper** list; - size_t num_mappers; -} grpc_proxy_mapper_list; - -static void grpc_proxy_mapper_list_register(grpc_proxy_mapper_list* list, - bool at_start, - grpc_proxy_mapper* mapper) { - list->list = (grpc_proxy_mapper**)gpr_realloc( - list->list, (list->num_mappers + 1) * sizeof(grpc_proxy_mapper*)); - if (at_start) { - memmove(list->list + 1, list->list, - sizeof(grpc_proxy_mapper*) * list->num_mappers); - list->list[0] = mapper; - } else { - list->list[list->num_mappers] = mapper; - } - ++list->num_mappers; -} - -static bool grpc_proxy_mapper_list_map_name(grpc_exec_ctx* exec_ctx, - grpc_proxy_mapper_list* list, - const char* server_uri, - const grpc_channel_args* args, - char** name_to_resolve, - grpc_channel_args** new_args) { - for (size_t i = 0; i < list->num_mappers; ++i) { - if (grpc_proxy_mapper_map_name(exec_ctx, list->list[i], server_uri, args, - name_to_resolve, new_args)) { - return true; - } - } - return false; -} - -static bool grpc_proxy_mapper_list_map_address( - grpc_exec_ctx* exec_ctx, grpc_proxy_mapper_list* list, - const grpc_resolved_address* address, const grpc_channel_args* args, - grpc_resolved_address** new_address, grpc_channel_args** new_args) { - for (size_t i = 0; i < list->num_mappers; ++i) { - if (grpc_proxy_mapper_map_address(exec_ctx, list->list[i], address, args, - new_address, new_args)) { - return true; - } - } - return false; -} - -static void grpc_proxy_mapper_list_destroy(grpc_proxy_mapper_list* list) { - for (size_t i = 0; i < list->num_mappers; ++i) { - grpc_proxy_mapper_destroy(list->list[i]); - } - gpr_free(list->list); - // Clean up in case we re-initialze later. - // TODO(ctiller): This should ideally live in - // grpc_proxy_mapper_registry_init(). However, if we did this there, - // then we would do it AFTER we start registering proxy mappers from - // third-party plugins, so they'd never show up (and would leak memory). - // We probably need some sort of dependency system for plugins to fix - // this. - memset(list, 0, sizeof(*list)); -} - -// -// plugin -// - -static grpc_proxy_mapper_list g_proxy_mapper_list; - -void grpc_proxy_mapper_registry_init() {} - -void grpc_proxy_mapper_registry_shutdown() { - grpc_proxy_mapper_list_destroy(&g_proxy_mapper_list); -} - -void grpc_proxy_mapper_register(bool at_start, grpc_proxy_mapper* mapper) { - grpc_proxy_mapper_list_register(&g_proxy_mapper_list, at_start, mapper); -} - -bool grpc_proxy_mappers_map_name(grpc_exec_ctx* exec_ctx, - const char* server_uri, - const grpc_channel_args* args, - char** name_to_resolve, - grpc_channel_args** new_args) { - return grpc_proxy_mapper_list_map_name(exec_ctx, &g_proxy_mapper_list, - server_uri, args, name_to_resolve, - new_args); -} -bool grpc_proxy_mappers_map_address(grpc_exec_ctx* exec_ctx, - const grpc_resolved_address* address, - const grpc_channel_args* args, - grpc_resolved_address** new_address, - grpc_channel_args** new_args) { - return grpc_proxy_mapper_list_map_address( - exec_ctx, &g_proxy_mapper_list, address, args, new_address, new_args); -} diff --git a/src/core/ext/filters/client_channel/proxy_mapper_registry.cc b/src/core/ext/filters/client_channel/proxy_mapper_registry.cc new file mode 100644 index 0000000000..09967eea3c --- /dev/null +++ b/src/core/ext/filters/client_channel/proxy_mapper_registry.cc @@ -0,0 +1,124 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" + +#include + +#include + +// +// grpc_proxy_mapper_list +// + +typedef struct { + grpc_proxy_mapper** list; + size_t num_mappers; +} grpc_proxy_mapper_list; + +static void grpc_proxy_mapper_list_register(grpc_proxy_mapper_list* list, + bool at_start, + grpc_proxy_mapper* mapper) { + list->list = (grpc_proxy_mapper**)gpr_realloc( + list->list, (list->num_mappers + 1) * sizeof(grpc_proxy_mapper*)); + if (at_start) { + memmove(list->list + 1, list->list, + sizeof(grpc_proxy_mapper*) * list->num_mappers); + list->list[0] = mapper; + } else { + list->list[list->num_mappers] = mapper; + } + ++list->num_mappers; +} + +static bool grpc_proxy_mapper_list_map_name(grpc_exec_ctx* exec_ctx, + grpc_proxy_mapper_list* list, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + for (size_t i = 0; i < list->num_mappers; ++i) { + if (grpc_proxy_mapper_map_name(exec_ctx, list->list[i], server_uri, args, + name_to_resolve, new_args)) { + return true; + } + } + return false; +} + +static bool grpc_proxy_mapper_list_map_address( + grpc_exec_ctx* exec_ctx, grpc_proxy_mapper_list* list, + const grpc_resolved_address* address, const grpc_channel_args* args, + grpc_resolved_address** new_address, grpc_channel_args** new_args) { + for (size_t i = 0; i < list->num_mappers; ++i) { + if (grpc_proxy_mapper_map_address(exec_ctx, list->list[i], address, args, + new_address, new_args)) { + return true; + } + } + return false; +} + +static void grpc_proxy_mapper_list_destroy(grpc_proxy_mapper_list* list) { + for (size_t i = 0; i < list->num_mappers; ++i) { + grpc_proxy_mapper_destroy(list->list[i]); + } + gpr_free(list->list); + // Clean up in case we re-initialze later. + // TODO(ctiller): This should ideally live in + // grpc_proxy_mapper_registry_init(). However, if we did this there, + // then we would do it AFTER we start registering proxy mappers from + // third-party plugins, so they'd never show up (and would leak memory). + // We probably need some sort of dependency system for plugins to fix + // this. + memset(list, 0, sizeof(*list)); +} + +// +// plugin +// + +static grpc_proxy_mapper_list g_proxy_mapper_list; + +void grpc_proxy_mapper_registry_init() {} + +void grpc_proxy_mapper_registry_shutdown() { + grpc_proxy_mapper_list_destroy(&g_proxy_mapper_list); +} + +void grpc_proxy_mapper_register(bool at_start, grpc_proxy_mapper* mapper) { + grpc_proxy_mapper_list_register(&g_proxy_mapper_list, at_start, mapper); +} + +bool grpc_proxy_mappers_map_name(grpc_exec_ctx* exec_ctx, + const char* server_uri, + const grpc_channel_args* args, + char** name_to_resolve, + grpc_channel_args** new_args) { + return grpc_proxy_mapper_list_map_name(exec_ctx, &g_proxy_mapper_list, + server_uri, args, name_to_resolve, + new_args); +} +bool grpc_proxy_mappers_map_address(grpc_exec_ctx* exec_ctx, + const grpc_resolved_address* address, + const grpc_channel_args* args, + grpc_resolved_address** new_address, + grpc_channel_args** new_args) { + return grpc_proxy_mapper_list_map_address( + exec_ctx, &g_proxy_mapper_list, address, args, new_address, new_args); +} diff --git a/src/core/ext/filters/client_channel/resolver.c b/src/core/ext/filters/client_channel/resolver.c deleted file mode 100644 index 8401504fcf..0000000000 --- a/src/core/ext/filters/client_channel/resolver.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/resolver.h" -#include "src/core/lib/iomgr/combiner.h" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_resolver_refcount = - GRPC_TRACER_INITIALIZER(false, "resolver_refcount"); -#endif - -void grpc_resolver_init(grpc_resolver *resolver, - const grpc_resolver_vtable *vtable, - grpc_combiner *combiner) { - resolver->vtable = vtable; - resolver->combiner = GRPC_COMBINER_REF(combiner, "resolver"); - gpr_ref_init(&resolver->refs, 1); -} - -#ifndef NDEBUG -void grpc_resolver_ref(grpc_resolver *resolver, const char *file, int line, - const char *reason) { - if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) { - gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "RESOLVER:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", resolver, - old_refs, old_refs + 1, reason); - } -#else -void grpc_resolver_ref(grpc_resolver *resolver) { -#endif - gpr_ref(&resolver->refs); -} - -#ifndef NDEBUG -void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - const char *file, int line, const char *reason) { - if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) { - gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "RESOLVER:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", resolver, - old_refs, old_refs - 1, reason); - } -#else -void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { -#endif - if (gpr_unref(&resolver->refs)) { - grpc_combiner *combiner = resolver->combiner; - resolver->vtable->destroy(exec_ctx, resolver); - GRPC_COMBINER_UNREF(exec_ctx, combiner, "resolver"); - } -} - -void grpc_resolver_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - resolver->vtable->shutdown_locked(exec_ctx, resolver); -} - -void grpc_resolver_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - resolver->vtable->channel_saw_error_locked(exec_ctx, resolver); -} - -void grpc_resolver_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_channel_args **result, - grpc_closure *on_complete) { - resolver->vtable->next_locked(exec_ctx, resolver, result, on_complete); -} diff --git a/src/core/ext/filters/client_channel/resolver.cc b/src/core/ext/filters/client_channel/resolver.cc new file mode 100644 index 0000000000..8401504fcf --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver.cc @@ -0,0 +1,83 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/resolver.h" +#include "src/core/lib/iomgr/combiner.h" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_resolver_refcount = + GRPC_TRACER_INITIALIZER(false, "resolver_refcount"); +#endif + +void grpc_resolver_init(grpc_resolver *resolver, + const grpc_resolver_vtable *vtable, + grpc_combiner *combiner) { + resolver->vtable = vtable; + resolver->combiner = GRPC_COMBINER_REF(combiner, "resolver"); + gpr_ref_init(&resolver->refs, 1); +} + +#ifndef NDEBUG +void grpc_resolver_ref(grpc_resolver *resolver, const char *file, int line, + const char *reason) { + if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) { + gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "RESOLVER:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", resolver, + old_refs, old_refs + 1, reason); + } +#else +void grpc_resolver_ref(grpc_resolver *resolver) { +#endif + gpr_ref(&resolver->refs); +} + +#ifndef NDEBUG +void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, + const char *file, int line, const char *reason) { + if (GRPC_TRACER_ON(grpc_trace_resolver_refcount)) { + gpr_atm old_refs = gpr_atm_no_barrier_load(&resolver->refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "RESOLVER:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", resolver, + old_refs, old_refs - 1, reason); + } +#else +void grpc_resolver_unref(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver) { +#endif + if (gpr_unref(&resolver->refs)) { + grpc_combiner *combiner = resolver->combiner; + resolver->vtable->destroy(exec_ctx, resolver); + GRPC_COMBINER_UNREF(exec_ctx, combiner, "resolver"); + } +} + +void grpc_resolver_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + resolver->vtable->shutdown_locked(exec_ctx, resolver); +} + +void grpc_resolver_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + resolver->vtable->channel_saw_error_locked(exec_ctx, resolver); +} + +void grpc_resolver_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, + grpc_channel_args **result, + grpc_closure *on_complete) { + resolver->vtable->next_locked(exec_ctx, resolver, result, on_complete); +} diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c deleted file mode 100644 index 9bb229ad95..0000000000 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#if GRPC_ARES == 1 && !defined(GRPC_UV) - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/gethostname.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/json/json.h" -#include "src/core/lib/support/backoff.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/service_config.h" - -#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 -#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 -#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 -#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 -#define GRPC_DNS_RECONNECT_JITTER 0.2 - -typedef struct { - /** base class: must be first */ - grpc_resolver base; - /** DNS server to use (if not system default) */ - char *dns_server; - /** name to resolve (usually the same as target_name) */ - char *name_to_resolve; - /** default port to use */ - char *default_port; - /** channel args. */ - grpc_channel_args *channel_args; - /** whether to request the service config */ - bool request_service_config; - /** pollset_set to drive the name resolution process */ - grpc_pollset_set *interested_parties; - - /** Closures used by the combiner */ - grpc_closure dns_ares_on_retry_timer_locked; - grpc_closure dns_ares_on_resolved_locked; - - /** Combiner guarding the rest of the state */ - grpc_combiner *combiner; - /** are we currently resolving? */ - bool resolving; - /** the pending resolving request */ - grpc_ares_request *pending_request; - /** which version of the result have we published? */ - int published_version; - /** which version of the result is current? */ - int resolved_version; - /** pending next completion, or NULL */ - grpc_closure *next_completion; - /** target result address for next completion */ - grpc_channel_args **target_result; - /** current (fully resolved) result */ - grpc_channel_args *resolved_result; - /** retry timer */ - bool have_retry_timer; - grpc_timer retry_timer; - /** retry backoff state */ - gpr_backoff backoff_state; - - /** currently resolving addresses */ - grpc_lb_addresses *lb_addresses; - /** currently resolving service config */ - char *service_config_json; -} ares_dns_resolver; - -static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); - -static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, - ares_dns_resolver *r); -static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - ares_dns_resolver *r); - -static void dns_ares_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void dns_ares_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *r); -static void dns_ares_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_channel_args **target_result, - grpc_closure *on_complete); - -static const grpc_resolver_vtable dns_ares_resolver_vtable = { - dns_ares_destroy, dns_ares_shutdown_locked, - dns_ares_channel_saw_error_locked, dns_ares_next_locked}; - -static void dns_ares_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - ares_dns_resolver *r = (ares_dns_resolver *)resolver; - if (r->have_retry_timer) { - grpc_timer_cancel(exec_ctx, &r->retry_timer); - } - if (r->pending_request != NULL) { - grpc_cancel_ares_request(exec_ctx, r->pending_request); - } - if (r->next_completion != NULL) { - *r->target_result = NULL; - GRPC_CLOSURE_SCHED( - exec_ctx, r->next_completion, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); - r->next_completion = NULL; - } -} - -static void dns_ares_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - ares_dns_resolver *r = (ares_dns_resolver *)resolver; - if (!r->resolving) { - gpr_backoff_reset(&r->backoff_state); - dns_ares_start_resolving_locked(exec_ctx, r); - } -} - -static void dns_ares_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - ares_dns_resolver *r = (ares_dns_resolver *)arg; - r->have_retry_timer = false; - if (error == GRPC_ERROR_NONE) { - if (!r->resolving) { - dns_ares_start_resolving_locked(exec_ctx, r); - } - } - GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); -} - -static bool value_in_json_array(grpc_json *array, const char *value) { - for (grpc_json *entry = array->child; entry != NULL; entry = entry->next) { - if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) { - return true; - } - } - return false; -} - -static char *choose_service_config(char *service_config_choice_json) { - grpc_json *choices_json = grpc_json_parse_string(service_config_choice_json); - if (choices_json == NULL || choices_json->type != GRPC_JSON_ARRAY) { - gpr_log(GPR_ERROR, "cannot parse service config JSON string"); - return NULL; - } - char *service_config = NULL; - for (grpc_json *choice = choices_json->child; choice != NULL; - choice = choice->next) { - if (choice->type != GRPC_JSON_OBJECT) { - gpr_log(GPR_ERROR, "cannot parse service config JSON string"); - break; - } - grpc_json *service_config_json = NULL; - for (grpc_json *field = choice->child; field != NULL; field = field->next) { - // Check client language, if specified. - if (strcmp(field->key, "clientLanguage") == 0) { - if (field->type != GRPC_JSON_ARRAY || - !value_in_json_array(field, "c++")) { - service_config_json = NULL; - break; - } - } - // Check client hostname, if specified. - if (strcmp(field->key, "clientHostname") == 0) { - char *hostname = grpc_gethostname(); - if (hostname == NULL || field->type != GRPC_JSON_ARRAY || - !value_in_json_array(field, hostname)) { - service_config_json = NULL; - break; - } - } - // Check percentage, if specified. - if (strcmp(field->key, "percentage") == 0) { - if (field->type != GRPC_JSON_NUMBER) { - service_config_json = NULL; - break; - } - int random_pct = rand() % 100; - int percentage; - if (sscanf(field->value, "%d", &percentage) != 1 || - random_pct > percentage || percentage == 0) { - service_config_json = NULL; - break; - } - } - // Save service config. - if (strcmp(field->key, "serviceConfig") == 0) { - if (field->type == GRPC_JSON_OBJECT) { - service_config_json = field; - } - } - } - if (service_config_json != NULL) { - service_config = grpc_json_dump_to_string(service_config_json, 0); - break; - } - } - grpc_json_destroy(choices_json); - return service_config; -} - -static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - ares_dns_resolver *r = (ares_dns_resolver *)arg; - grpc_channel_args *result = NULL; - GPR_ASSERT(r->resolving); - r->resolving = false; - r->pending_request = NULL; - if (r->lb_addresses != NULL) { - static const char *args_to_remove[2]; - size_t num_args_to_remove = 0; - grpc_arg new_args[3]; - size_t num_args_to_add = 0; - new_args[num_args_to_add++] = - grpc_lb_addresses_create_channel_arg(r->lb_addresses); - grpc_service_config *service_config = NULL; - char *service_config_string = NULL; - if (r->service_config_json != NULL) { - service_config_string = choose_service_config(r->service_config_json); - gpr_free(r->service_config_json); - if (service_config_string != NULL) { - gpr_log(GPR_INFO, "selected service config choice: %s", - service_config_string); - args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG; - new_args[num_args_to_add++] = grpc_channel_arg_string_create( - (char *)GRPC_ARG_SERVICE_CONFIG, service_config_string); - service_config = grpc_service_config_create(service_config_string); - if (service_config != NULL) { - const char *lb_policy_name = - grpc_service_config_get_lb_policy_name(service_config); - if (lb_policy_name != NULL) { - args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME; - new_args[num_args_to_add++] = grpc_channel_arg_string_create( - (char *)GRPC_ARG_LB_POLICY_NAME, (char *)lb_policy_name); - } - } - } - } - result = grpc_channel_args_copy_and_add_and_remove( - r->channel_args, args_to_remove, num_args_to_remove, new_args, - num_args_to_add); - if (service_config != NULL) grpc_service_config_destroy(service_config); - gpr_free(service_config_string); - grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses); - } else { - const char *msg = grpc_error_string(error); - gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg); - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); - gpr_timespec timeout = gpr_time_sub(next_try, now); - gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", - grpc_error_string(error)); - GPR_ASSERT(!r->have_retry_timer); - r->have_retry_timer = true; - GRPC_RESOLVER_REF(&r->base, "retry-timer"); - if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { - gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec, - timeout.tv_nsec); - } else { - gpr_log(GPR_DEBUG, "retrying immediately"); - } - grpc_timer_init(exec_ctx, &r->retry_timer, next_try, - &r->dns_ares_on_retry_timer_locked, now); - } - if (r->resolved_result != NULL) { - grpc_channel_args_destroy(exec_ctx, r->resolved_result); - } - r->resolved_result = result; - r->resolved_version++; - dns_ares_maybe_finish_next_locked(exec_ctx, r); - GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving"); -} - -static void dns_ares_next_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver, - grpc_channel_args **target_result, - grpc_closure *on_complete) { - gpr_log(GPR_DEBUG, "dns_ares_next is called."); - ares_dns_resolver *r = (ares_dns_resolver *)resolver; - GPR_ASSERT(!r->next_completion); - r->next_completion = on_complete; - r->target_result = target_result; - if (r->resolved_version == 0 && !r->resolving) { - gpr_backoff_reset(&r->backoff_state); - dns_ares_start_resolving_locked(exec_ctx, r); - } else { - dns_ares_maybe_finish_next_locked(exec_ctx, r); - } -} - -static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, - ares_dns_resolver *r) { - GRPC_RESOLVER_REF(&r->base, "dns-resolving"); - GPR_ASSERT(!r->resolving); - r->resolving = true; - r->lb_addresses = NULL; - r->service_config_json = NULL; - r->pending_request = grpc_dns_lookup_ares( - exec_ctx, r->dns_server, r->name_to_resolve, r->default_port, - r->interested_parties, &r->dns_ares_on_resolved_locked, &r->lb_addresses, - true /* check_grpclb */, - r->request_service_config ? &r->service_config_json : NULL); -} - -static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - ares_dns_resolver *r) { - if (r->next_completion != NULL && - r->resolved_version != r->published_version) { - *r->target_result = r->resolved_result == NULL - ? NULL - : grpc_channel_args_copy(r->resolved_result); - gpr_log(GPR_DEBUG, "dns_ares_maybe_finish_next_locked"); - GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); - r->next_completion = NULL; - r->published_version = r->resolved_version; - } -} - -static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { - gpr_log(GPR_DEBUG, "dns_ares_destroy"); - ares_dns_resolver *r = (ares_dns_resolver *)gr; - if (r->resolved_result != NULL) { - grpc_channel_args_destroy(exec_ctx, r->resolved_result); - } - grpc_pollset_set_destroy(exec_ctx, r->interested_parties); - gpr_free(r->dns_server); - gpr_free(r->name_to_resolve); - gpr_free(r->default_port); - grpc_channel_args_destroy(exec_ctx, r->channel_args); - gpr_free(r); -} - -static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx, - grpc_resolver_args *args, - const char *default_port) { - /* Get name from args. */ - const char *path = args->uri->path; - if (path[0] == '/') ++path; - /* Create resolver. */ - ares_dns_resolver *r = - (ares_dns_resolver *)gpr_zalloc(sizeof(ares_dns_resolver)); - grpc_resolver_init(&r->base, &dns_ares_resolver_vtable, args->combiner); - if (0 != strcmp(args->uri->authority, "")) { - r->dns_server = gpr_strdup(args->uri->authority); - } - r->name_to_resolve = gpr_strdup(path); - r->default_port = gpr_strdup(default_port); - r->channel_args = grpc_channel_args_copy(args->args); - const grpc_arg *arg = grpc_channel_args_find( - r->channel_args, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION); - r->request_service_config = !grpc_channel_arg_get_integer( - arg, (grpc_integer_options){false, false, true}); - r->interested_parties = grpc_pollset_set_create(); - if (args->pollset_set != NULL) { - grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, - args->pollset_set); - } - gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS, - GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, - GRPC_DNS_RECONNECT_JITTER, - GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, - GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); - GRPC_CLOSURE_INIT(&r->dns_ares_on_retry_timer_locked, - dns_ares_on_retry_timer_locked, r, - grpc_combiner_scheduler(r->base.combiner)); - GRPC_CLOSURE_INIT(&r->dns_ares_on_resolved_locked, - dns_ares_on_resolved_locked, r, - grpc_combiner_scheduler(r->base.combiner)); - return &r->base; -} - -/* - * FACTORY - */ - -static void dns_ares_factory_ref(grpc_resolver_factory *factory) {} - -static void dns_ares_factory_unref(grpc_resolver_factory *factory) {} - -static grpc_resolver *dns_factory_create_resolver( - grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, - grpc_resolver_args *args) { - return dns_ares_create(exec_ctx, args, "https"); -} - -static char *dns_ares_factory_get_default_host_name( - grpc_resolver_factory *factory, grpc_uri *uri) { - const char *path = uri->path; - if (path[0] == '/') ++path; - return gpr_strdup(path); -} - -static const grpc_resolver_factory_vtable dns_ares_factory_vtable = { - dns_ares_factory_ref, dns_ares_factory_unref, dns_factory_create_resolver, - dns_ares_factory_get_default_host_name, "dns"}; -static grpc_resolver_factory dns_resolver_factory = {&dns_ares_factory_vtable}; - -static grpc_resolver_factory *dns_ares_resolver_factory_create() { - return &dns_resolver_factory; -} - -void grpc_resolver_dns_ares_init(void) { - char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); - /* TODO(zyc): Turn on c-ares based resolver by default after the address - sorter and the CNAME support are added. */ - if (resolver != NULL && gpr_stricmp(resolver, "ares") == 0) { - grpc_error *error = grpc_ares_init(); - if (error != GRPC_ERROR_NONE) { - GRPC_LOG_IF_ERROR("ares_library_init() failed", error); - return; - } - grpc_resolve_address = grpc_resolve_address_ares; - grpc_register_resolver_type(dns_ares_resolver_factory_create()); - } - gpr_free(resolver); -} - -void grpc_resolver_dns_ares_shutdown(void) { - char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); - if (resolver != NULL && gpr_stricmp(resolver, "ares") == 0) { - grpc_ares_cleanup(); - } - gpr_free(resolver); -} - -#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */ - -void grpc_resolver_dns_ares_init(void) {} - -void grpc_resolver_dns_ares_shutdown(void) {} - -#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc new file mode 100644 index 0000000000..69f5877b00 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc @@ -0,0 +1,458 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/gethostname.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/support/backoff.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/service_config.h" + +#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 +#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_DNS_RECONNECT_JITTER 0.2 + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** DNS server to use (if not system default) */ + char *dns_server; + /** name to resolve (usually the same as target_name) */ + char *name_to_resolve; + /** default port to use */ + char *default_port; + /** channel args. */ + grpc_channel_args *channel_args; + /** whether to request the service config */ + bool request_service_config; + /** pollset_set to drive the name resolution process */ + grpc_pollset_set *interested_parties; + + /** Closures used by the combiner */ + grpc_closure dns_ares_on_retry_timer_locked; + grpc_closure dns_ares_on_resolved_locked; + + /** Combiner guarding the rest of the state */ + grpc_combiner *combiner; + /** are we currently resolving? */ + bool resolving; + /** the pending resolving request */ + grpc_ares_request *pending_request; + /** which version of the result have we published? */ + int published_version; + /** which version of the result is current? */ + int resolved_version; + /** pending next completion, or NULL */ + grpc_closure *next_completion; + /** target result address for next completion */ + grpc_channel_args **target_result; + /** current (fully resolved) result */ + grpc_channel_args *resolved_result; + /** retry timer */ + bool have_retry_timer; + grpc_timer retry_timer; + /** retry backoff state */ + gpr_backoff backoff_state; + + /** currently resolving addresses */ + grpc_lb_addresses *lb_addresses; + /** currently resolving service config */ + char *service_config_json; +} ares_dns_resolver; + +static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); + +static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r); +static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r); + +static void dns_ares_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void dns_ares_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *r); +static void dns_ares_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_channel_args **target_result, + grpc_closure *on_complete); + +static const grpc_resolver_vtable dns_ares_resolver_vtable = { + dns_ares_destroy, dns_ares_shutdown_locked, + dns_ares_channel_saw_error_locked, dns_ares_next_locked}; + +static void dns_ares_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + ares_dns_resolver *r = (ares_dns_resolver *)resolver; + if (r->have_retry_timer) { + grpc_timer_cancel(exec_ctx, &r->retry_timer); + } + if (r->pending_request != NULL) { + grpc_cancel_ares_request(exec_ctx, r->pending_request); + } + if (r->next_completion != NULL) { + *r->target_result = NULL; + GRPC_CLOSURE_SCHED( + exec_ctx, r->next_completion, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); + r->next_completion = NULL; + } +} + +static void dns_ares_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + ares_dns_resolver *r = (ares_dns_resolver *)resolver; + if (!r->resolving) { + gpr_backoff_reset(&r->backoff_state); + dns_ares_start_resolving_locked(exec_ctx, r); + } +} + +static void dns_ares_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + ares_dns_resolver *r = (ares_dns_resolver *)arg; + r->have_retry_timer = false; + if (error == GRPC_ERROR_NONE) { + if (!r->resolving) { + dns_ares_start_resolving_locked(exec_ctx, r); + } + } + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); +} + +static bool value_in_json_array(grpc_json *array, const char *value) { + for (grpc_json *entry = array->child; entry != NULL; entry = entry->next) { + if (entry->type == GRPC_JSON_STRING && strcmp(entry->value, value) == 0) { + return true; + } + } + return false; +} + +static char *choose_service_config(char *service_config_choice_json) { + grpc_json *choices_json = grpc_json_parse_string(service_config_choice_json); + if (choices_json == NULL || choices_json->type != GRPC_JSON_ARRAY) { + gpr_log(GPR_ERROR, "cannot parse service config JSON string"); + return NULL; + } + char *service_config = NULL; + for (grpc_json *choice = choices_json->child; choice != NULL; + choice = choice->next) { + if (choice->type != GRPC_JSON_OBJECT) { + gpr_log(GPR_ERROR, "cannot parse service config JSON string"); + break; + } + grpc_json *service_config_json = NULL; + for (grpc_json *field = choice->child; field != NULL; field = field->next) { + // Check client language, if specified. + if (strcmp(field->key, "clientLanguage") == 0) { + if (field->type != GRPC_JSON_ARRAY || + !value_in_json_array(field, "c++")) { + service_config_json = NULL; + break; + } + } + // Check client hostname, if specified. + if (strcmp(field->key, "clientHostname") == 0) { + char *hostname = grpc_gethostname(); + if (hostname == NULL || field->type != GRPC_JSON_ARRAY || + !value_in_json_array(field, hostname)) { + service_config_json = NULL; + break; + } + } + // Check percentage, if specified. + if (strcmp(field->key, "percentage") == 0) { + if (field->type != GRPC_JSON_NUMBER) { + service_config_json = NULL; + break; + } + int random_pct = rand() % 100; + int percentage; + if (sscanf(field->value, "%d", &percentage) != 1 || + random_pct > percentage || percentage == 0) { + service_config_json = NULL; + break; + } + } + // Save service config. + if (strcmp(field->key, "serviceConfig") == 0) { + if (field->type == GRPC_JSON_OBJECT) { + service_config_json = field; + } + } + } + if (service_config_json != NULL) { + service_config = grpc_json_dump_to_string(service_config_json, 0); + break; + } + } + grpc_json_destroy(choices_json); + return service_config; +} + +static void dns_ares_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + ares_dns_resolver *r = (ares_dns_resolver *)arg; + grpc_channel_args *result = NULL; + GPR_ASSERT(r->resolving); + r->resolving = false; + r->pending_request = NULL; + if (r->lb_addresses != NULL) { + static const char *args_to_remove[2]; + size_t num_args_to_remove = 0; + grpc_arg new_args[3]; + size_t num_args_to_add = 0; + new_args[num_args_to_add++] = + grpc_lb_addresses_create_channel_arg(r->lb_addresses); + grpc_service_config *service_config = NULL; + char *service_config_string = NULL; + if (r->service_config_json != NULL) { + service_config_string = choose_service_config(r->service_config_json); + gpr_free(r->service_config_json); + if (service_config_string != NULL) { + gpr_log(GPR_INFO, "selected service config choice: %s", + service_config_string); + args_to_remove[num_args_to_remove++] = GRPC_ARG_SERVICE_CONFIG; + new_args[num_args_to_add++] = grpc_channel_arg_string_create( + (char *)GRPC_ARG_SERVICE_CONFIG, service_config_string); + service_config = grpc_service_config_create(service_config_string); + if (service_config != NULL) { + const char *lb_policy_name = + grpc_service_config_get_lb_policy_name(service_config); + if (lb_policy_name != NULL) { + args_to_remove[num_args_to_remove++] = GRPC_ARG_LB_POLICY_NAME; + new_args[num_args_to_add++] = grpc_channel_arg_string_create( + (char *)GRPC_ARG_LB_POLICY_NAME, (char *)lb_policy_name); + } + } + } + } + result = grpc_channel_args_copy_and_add_and_remove( + r->channel_args, args_to_remove, num_args_to_remove, new_args, + num_args_to_add); + if (service_config != NULL) grpc_service_config_destroy(service_config); + gpr_free(service_config_string); + grpc_lb_addresses_destroy(exec_ctx, r->lb_addresses); + } else { + const char *msg = grpc_error_string(error); + gpr_log(GPR_DEBUG, "dns resolution failed: %s", msg); + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); + gpr_timespec timeout = gpr_time_sub(next_try, now); + gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", + grpc_error_string(error)); + GPR_ASSERT(!r->have_retry_timer); + r->have_retry_timer = true; + GRPC_RESOLVER_REF(&r->base, "retry-timer"); + if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { + gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec, + timeout.tv_nsec); + } else { + gpr_log(GPR_DEBUG, "retrying immediately"); + } + grpc_timer_init(exec_ctx, &r->retry_timer, next_try, + &r->dns_ares_on_retry_timer_locked, now); + } + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(exec_ctx, r->resolved_result); + } + r->resolved_result = result; + r->resolved_version++; + dns_ares_maybe_finish_next_locked(exec_ctx, r); + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving"); +} + +static void dns_ares_next_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, + grpc_channel_args **target_result, + grpc_closure *on_complete) { + gpr_log(GPR_DEBUG, "dns_ares_next is called."); + ares_dns_resolver *r = (ares_dns_resolver *)resolver; + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_result = target_result; + if (r->resolved_version == 0 && !r->resolving) { + gpr_backoff_reset(&r->backoff_state); + dns_ares_start_resolving_locked(exec_ctx, r); + } else { + dns_ares_maybe_finish_next_locked(exec_ctx, r); + } +} + +static void dns_ares_start_resolving_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r) { + GRPC_RESOLVER_REF(&r->base, "dns-resolving"); + GPR_ASSERT(!r->resolving); + r->resolving = true; + r->lb_addresses = NULL; + r->service_config_json = NULL; + r->pending_request = grpc_dns_lookup_ares( + exec_ctx, r->dns_server, r->name_to_resolve, r->default_port, + r->interested_parties, &r->dns_ares_on_resolved_locked, &r->lb_addresses, + true /* check_grpclb */, + r->request_service_config ? &r->service_config_json : NULL); +} + +static void dns_ares_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + ares_dns_resolver *r) { + if (r->next_completion != NULL && + r->resolved_version != r->published_version) { + *r->target_result = r->resolved_result == NULL + ? NULL + : grpc_channel_args_copy(r->resolved_result); + gpr_log(GPR_DEBUG, "dns_ares_maybe_finish_next_locked"); + GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); + r->next_completion = NULL; + r->published_version = r->resolved_version; + } +} + +static void dns_ares_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { + gpr_log(GPR_DEBUG, "dns_ares_destroy"); + ares_dns_resolver *r = (ares_dns_resolver *)gr; + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(exec_ctx, r->resolved_result); + } + grpc_pollset_set_destroy(exec_ctx, r->interested_parties); + gpr_free(r->dns_server); + gpr_free(r->name_to_resolve); + gpr_free(r->default_port); + grpc_channel_args_destroy(exec_ctx, r->channel_args); + gpr_free(r); +} + +static grpc_resolver *dns_ares_create(grpc_exec_ctx *exec_ctx, + grpc_resolver_args *args, + const char *default_port) { + /* Get name from args. */ + const char *path = args->uri->path; + if (path[0] == '/') ++path; + /* Create resolver. */ + ares_dns_resolver *r = + (ares_dns_resolver *)gpr_zalloc(sizeof(ares_dns_resolver)); + grpc_resolver_init(&r->base, &dns_ares_resolver_vtable, args->combiner); + if (0 != strcmp(args->uri->authority, "")) { + r->dns_server = gpr_strdup(args->uri->authority); + } + r->name_to_resolve = gpr_strdup(path); + r->default_port = gpr_strdup(default_port); + r->channel_args = grpc_channel_args_copy(args->args); + const grpc_arg *arg = grpc_channel_args_find( + r->channel_args, GRPC_ARG_SERVICE_CONFIG_DISABLE_RESOLUTION); + r->request_service_config = !grpc_channel_arg_get_integer( + arg, (grpc_integer_options){false, false, true}); + r->interested_parties = grpc_pollset_set_create(); + if (args->pollset_set != NULL) { + grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, + args->pollset_set); + } + gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS, + GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, + GRPC_DNS_RECONNECT_JITTER, + GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, + GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + GRPC_CLOSURE_INIT(&r->dns_ares_on_retry_timer_locked, + dns_ares_on_retry_timer_locked, r, + grpc_combiner_scheduler(r->base.combiner)); + GRPC_CLOSURE_INIT(&r->dns_ares_on_resolved_locked, + dns_ares_on_resolved_locked, r, + grpc_combiner_scheduler(r->base.combiner)); + return &r->base; +} + +/* + * FACTORY + */ + +static void dns_ares_factory_ref(grpc_resolver_factory *factory) {} + +static void dns_ares_factory_unref(grpc_resolver_factory *factory) {} + +static grpc_resolver *dns_factory_create_resolver( + grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, + grpc_resolver_args *args) { + return dns_ares_create(exec_ctx, args, "https"); +} + +static char *dns_ares_factory_get_default_host_name( + grpc_resolver_factory *factory, grpc_uri *uri) { + const char *path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static const grpc_resolver_factory_vtable dns_ares_factory_vtable = { + dns_ares_factory_ref, dns_ares_factory_unref, dns_factory_create_resolver, + dns_ares_factory_get_default_host_name, "dns"}; +static grpc_resolver_factory dns_resolver_factory = {&dns_ares_factory_vtable}; + +static grpc_resolver_factory *dns_ares_resolver_factory_create() { + return &dns_resolver_factory; +} + +extern "C" void grpc_resolver_dns_ares_init(void) { + char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + /* TODO(zyc): Turn on c-ares based resolver by default after the address + sorter and the CNAME support are added. */ + if (resolver != NULL && gpr_stricmp(resolver, "ares") == 0) { + grpc_error *error = grpc_ares_init(); + if (error != GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR("ares_library_init() failed", error); + return; + } + grpc_resolve_address = grpc_resolve_address_ares; + grpc_register_resolver_type(dns_ares_resolver_factory_create()); + } + gpr_free(resolver); +} + +extern "C" void grpc_resolver_dns_ares_shutdown(void) { + char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + if (resolver != NULL && gpr_stricmp(resolver, "ares") == 0) { + grpc_ares_cleanup(); + } + gpr_free(resolver); +} + +#else /* GRPC_ARES == 1 && !defined(GRPC_UV) */ + +extern "C" void grpc_resolver_dns_ares_init(void) {} + +extern "C" void grpc_resolver_dns_ares_shutdown(void) {} + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c deleted file mode 100644 index c30cc93b6f..0000000000 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include -#include "src/core/lib/iomgr/port.h" -#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) - -#include -#include - -#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" - -#include -#include -#include -#include -#include -#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/string.h" - -typedef struct fd_node { - /** the owner of this fd node */ - grpc_ares_ev_driver *ev_driver; - /** a closure wrapping on_readable_cb, which should be invoked when the - grpc_fd in this node becomes readable. */ - grpc_closure read_closure; - /** a closure wrapping on_writable_cb, which should be invoked when the - grpc_fd in this node becomes writable. */ - grpc_closure write_closure; - /** next fd node in the list */ - struct fd_node *next; - - /** mutex guarding the rest of the state */ - gpr_mu mu; - /** the grpc_fd owned by this fd node */ - grpc_fd *fd; - /** if the readable closure has been registered */ - bool readable_registered; - /** if the writable closure has been registered */ - bool writable_registered; - /** if the fd is being shut down */ - bool shutting_down; -} fd_node; - -struct grpc_ares_ev_driver { - /** the ares_channel owned by this event driver */ - ares_channel channel; - /** pollset set for driving the IO events of the channel */ - grpc_pollset_set *pollset_set; - /** refcount of the event driver */ - gpr_refcount refs; - - /** mutex guarding the rest of the state */ - gpr_mu mu; - /** a list of grpc_fd that this event driver is currently using. */ - fd_node *fds; - /** is this event driver currently working? */ - bool working; - /** is this event driver being shut down */ - bool shutting_down; -}; - -static void grpc_ares_notify_on_event_locked(grpc_exec_ctx *exec_ctx, - grpc_ares_ev_driver *ev_driver); - -static grpc_ares_ev_driver *grpc_ares_ev_driver_ref( - grpc_ares_ev_driver *ev_driver) { - gpr_log(GPR_DEBUG, "Ref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); - gpr_ref(&ev_driver->refs); - return ev_driver; -} - -static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver *ev_driver) { - gpr_log(GPR_DEBUG, "Unref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); - if (gpr_unref(&ev_driver->refs)) { - gpr_log(GPR_DEBUG, "destroy ev_driver %" PRIuPTR, (uintptr_t)ev_driver); - GPR_ASSERT(ev_driver->fds == NULL); - gpr_mu_destroy(&ev_driver->mu); - ares_destroy(ev_driver->channel); - gpr_free(ev_driver); - } -} - -static void fd_node_destroy(grpc_exec_ctx *exec_ctx, fd_node *fdn) { - gpr_log(GPR_DEBUG, "delete fd: %d", grpc_fd_wrapped_fd(fdn->fd)); - GPR_ASSERT(!fdn->readable_registered); - GPR_ASSERT(!fdn->writable_registered); - gpr_mu_destroy(&fdn->mu); - /* c-ares library has closed the fd inside grpc_fd. This fd may be picked up - immediately by another thread, and should not be closed by the following - grpc_fd_orphan. */ - grpc_fd_orphan(exec_ctx, fdn->fd, NULL, NULL, true /* already_closed */, - "c-ares query finished"); - gpr_free(fdn); -} - -static void fd_node_shutdown(grpc_exec_ctx *exec_ctx, fd_node *fdn) { - gpr_mu_lock(&fdn->mu); - fdn->shutting_down = true; - if (!fdn->readable_registered && !fdn->writable_registered) { - gpr_mu_unlock(&fdn->mu); - fd_node_destroy(exec_ctx, fdn); - } else { - grpc_fd_shutdown(exec_ctx, fdn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "c-ares fd shutdown")); - gpr_mu_unlock(&fdn->mu); - } -} - -grpc_error *grpc_ares_ev_driver_create(grpc_ares_ev_driver **ev_driver, - grpc_pollset_set *pollset_set) { - *ev_driver = (grpc_ares_ev_driver *)gpr_malloc(sizeof(grpc_ares_ev_driver)); - int status = ares_init(&(*ev_driver)->channel); - gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create"); - if (status != ARES_SUCCESS) { - char *err_msg; - gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s", - ares_strerror(status)); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); - gpr_free(err_msg); - gpr_free(*ev_driver); - return err; - } - gpr_mu_init(&(*ev_driver)->mu); - gpr_ref_init(&(*ev_driver)->refs, 1); - (*ev_driver)->pollset_set = pollset_set; - (*ev_driver)->fds = NULL; - (*ev_driver)->working = false; - (*ev_driver)->shutting_down = false; - return GRPC_ERROR_NONE; -} - -void grpc_ares_ev_driver_destroy(grpc_ares_ev_driver *ev_driver) { - // It's not safe to shut down remaining fds here directly, becauses - // ares_host_callback does not provide an exec_ctx. We mark the event driver - // as being shut down. If the event driver is working, - // grpc_ares_notify_on_event_locked will shut down the fds; if it's not - // working, there are no fds to shut down. - gpr_mu_lock(&ev_driver->mu); - ev_driver->shutting_down = true; - gpr_mu_unlock(&ev_driver->mu); - grpc_ares_ev_driver_unref(ev_driver); -} - -void grpc_ares_ev_driver_shutdown(grpc_exec_ctx *exec_ctx, - grpc_ares_ev_driver *ev_driver) { - gpr_mu_lock(&ev_driver->mu); - ev_driver->shutting_down = true; - fd_node *fn = ev_driver->fds; - while (fn != NULL) { - grpc_fd_shutdown(exec_ctx, fn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "grpc_ares_ev_driver_shutdown")); - fn = fn->next; - } - gpr_mu_unlock(&ev_driver->mu); -} - -// Search fd in the fd_node list head. This is an O(n) search, the max possible -// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests. -static fd_node *pop_fd_node(fd_node **head, int fd) { - fd_node dummy_head; - dummy_head.next = *head; - fd_node *node = &dummy_head; - while (node->next != NULL) { - if (grpc_fd_wrapped_fd(node->next->fd) == fd) { - fd_node *ret = node->next; - node->next = node->next->next; - *head = dummy_head.next; - return ret; - } - node = node->next; - } - return NULL; -} - -/* Check if \a fd is still readable */ -static bool grpc_ares_is_fd_still_readable(grpc_ares_ev_driver *ev_driver, - int fd) { - size_t bytes_available = 0; - return ioctl(fd, FIONREAD, &bytes_available) == 0 && bytes_available > 0; -} - -static void on_readable_cb(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - fd_node *fdn = (fd_node *)arg; - grpc_ares_ev_driver *ev_driver = fdn->ev_driver; - gpr_mu_lock(&fdn->mu); - const int fd = grpc_fd_wrapped_fd(fdn->fd); - fdn->readable_registered = false; - if (fdn->shutting_down && !fdn->writable_registered) { - gpr_mu_unlock(&fdn->mu); - fd_node_destroy(exec_ctx, fdn); - grpc_ares_ev_driver_unref(ev_driver); - return; - } - gpr_mu_unlock(&fdn->mu); - - gpr_log(GPR_DEBUG, "readable on %d", fd); - if (error == GRPC_ERROR_NONE) { - do { - ares_process_fd(ev_driver->channel, fd, ARES_SOCKET_BAD); - } while (grpc_ares_is_fd_still_readable(ev_driver, fd)); - } else { - // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or - // timed out. The pending lookups made on this ev_driver will be cancelled - // by the following ares_cancel() and the on_done callbacks will be invoked - // with a status of ARES_ECANCELLED. The remaining file descriptors in this - // ev_driver will be cleaned up in the follwing - // grpc_ares_notify_on_event_locked(). - ares_cancel(ev_driver->channel); - } - gpr_mu_lock(&ev_driver->mu); - grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); - gpr_mu_unlock(&ev_driver->mu); - grpc_ares_ev_driver_unref(ev_driver); -} - -static void on_writable_cb(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - fd_node *fdn = (fd_node *)arg; - grpc_ares_ev_driver *ev_driver = fdn->ev_driver; - gpr_mu_lock(&fdn->mu); - const int fd = grpc_fd_wrapped_fd(fdn->fd); - fdn->writable_registered = false; - if (fdn->shutting_down && !fdn->readable_registered) { - gpr_mu_unlock(&fdn->mu); - fd_node_destroy(exec_ctx, fdn); - grpc_ares_ev_driver_unref(ev_driver); - return; - } - gpr_mu_unlock(&fdn->mu); - - gpr_log(GPR_DEBUG, "writable on %d", fd); - if (error == GRPC_ERROR_NONE) { - ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, fd); - } else { - // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or - // timed out. The pending lookups made on this ev_driver will be cancelled - // by the following ares_cancel() and the on_done callbacks will be invoked - // with a status of ARES_ECANCELLED. The remaining file descriptors in this - // ev_driver will be cleaned up in the follwing - // grpc_ares_notify_on_event_locked(). - ares_cancel(ev_driver->channel); - } - gpr_mu_lock(&ev_driver->mu); - grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); - gpr_mu_unlock(&ev_driver->mu); - grpc_ares_ev_driver_unref(ev_driver); -} - -ares_channel *grpc_ares_ev_driver_get_channel(grpc_ares_ev_driver *ev_driver) { - return &ev_driver->channel; -} - -// Get the file descriptors used by the ev_driver's ares channel, register -// driver_closure with these filedescriptors. -static void grpc_ares_notify_on_event_locked(grpc_exec_ctx *exec_ctx, - grpc_ares_ev_driver *ev_driver) { - fd_node *new_list = NULL; - if (!ev_driver->shutting_down) { - ares_socket_t socks[ARES_GETSOCK_MAXNUM]; - int socks_bitmask = - ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); - for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { - if (ARES_GETSOCK_READABLE(socks_bitmask, i) || - ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { - fd_node *fdn = pop_fd_node(&ev_driver->fds, socks[i]); - // Create a new fd_node if sock[i] is not in the fd_node list. - if (fdn == NULL) { - char *fd_name; - gpr_asprintf(&fd_name, "ares_ev_driver-%" PRIuPTR, i); - fdn = (fd_node *)gpr_malloc(sizeof(fd_node)); - gpr_log(GPR_DEBUG, "new fd: %d", socks[i]); - fdn->fd = grpc_fd_create(socks[i], fd_name); - fdn->ev_driver = ev_driver; - fdn->readable_registered = false; - fdn->writable_registered = false; - fdn->shutting_down = false; - gpr_mu_init(&fdn->mu); - GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_cb, fdn, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable_cb, fdn, - grpc_schedule_on_exec_ctx); - grpc_pollset_set_add_fd(exec_ctx, ev_driver->pollset_set, fdn->fd); - gpr_free(fd_name); - } - fdn->next = new_list; - new_list = fdn; - gpr_mu_lock(&fdn->mu); - // Register read_closure if the socket is readable and read_closure has - // not been registered with this socket. - if (ARES_GETSOCK_READABLE(socks_bitmask, i) && - !fdn->readable_registered) { - grpc_ares_ev_driver_ref(ev_driver); - gpr_log(GPR_DEBUG, "notify read on: %d", grpc_fd_wrapped_fd(fdn->fd)); - grpc_fd_notify_on_read(exec_ctx, fdn->fd, &fdn->read_closure); - fdn->readable_registered = true; - } - // Register write_closure if the socket is writable and write_closure - // has not been registered with this socket. - if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && - !fdn->writable_registered) { - gpr_log(GPR_DEBUG, "notify write on: %d", - grpc_fd_wrapped_fd(fdn->fd)); - grpc_ares_ev_driver_ref(ev_driver); - grpc_fd_notify_on_write(exec_ctx, fdn->fd, &fdn->write_closure); - fdn->writable_registered = true; - } - gpr_mu_unlock(&fdn->mu); - } - } - } - // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and - // are therefore no longer in use, so they can be shut down and removed from - // the list. - while (ev_driver->fds != NULL) { - fd_node *cur = ev_driver->fds; - ev_driver->fds = ev_driver->fds->next; - fd_node_shutdown(exec_ctx, cur); - } - ev_driver->fds = new_list; - // If the ev driver has no working fd, all the tasks are done. - if (new_list == NULL) { - ev_driver->working = false; - gpr_log(GPR_DEBUG, "ev driver stop working"); - } -} - -void grpc_ares_ev_driver_start(grpc_exec_ctx *exec_ctx, - grpc_ares_ev_driver *ev_driver) { - gpr_mu_lock(&ev_driver->mu); - if (!ev_driver->working) { - ev_driver->working = true; - grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); - } - gpr_mu_unlock(&ev_driver->mu); -} - -#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc new file mode 100644 index 0000000000..c30cc93b6f --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc @@ -0,0 +1,356 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include +#include "src/core/lib/iomgr/port.h" +#if GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) + +#include +#include + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" + +#include +#include +#include +#include +#include +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/string.h" + +typedef struct fd_node { + /** the owner of this fd node */ + grpc_ares_ev_driver *ev_driver; + /** a closure wrapping on_readable_cb, which should be invoked when the + grpc_fd in this node becomes readable. */ + grpc_closure read_closure; + /** a closure wrapping on_writable_cb, which should be invoked when the + grpc_fd in this node becomes writable. */ + grpc_closure write_closure; + /** next fd node in the list */ + struct fd_node *next; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** the grpc_fd owned by this fd node */ + grpc_fd *fd; + /** if the readable closure has been registered */ + bool readable_registered; + /** if the writable closure has been registered */ + bool writable_registered; + /** if the fd is being shut down */ + bool shutting_down; +} fd_node; + +struct grpc_ares_ev_driver { + /** the ares_channel owned by this event driver */ + ares_channel channel; + /** pollset set for driving the IO events of the channel */ + grpc_pollset_set *pollset_set; + /** refcount of the event driver */ + gpr_refcount refs; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** a list of grpc_fd that this event driver is currently using. */ + fd_node *fds; + /** is this event driver currently working? */ + bool working; + /** is this event driver being shut down */ + bool shutting_down; +}; + +static void grpc_ares_notify_on_event_locked(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver); + +static grpc_ares_ev_driver *grpc_ares_ev_driver_ref( + grpc_ares_ev_driver *ev_driver) { + gpr_log(GPR_DEBUG, "Ref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + gpr_ref(&ev_driver->refs); + return ev_driver; +} + +static void grpc_ares_ev_driver_unref(grpc_ares_ev_driver *ev_driver) { + gpr_log(GPR_DEBUG, "Unref ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + if (gpr_unref(&ev_driver->refs)) { + gpr_log(GPR_DEBUG, "destroy ev_driver %" PRIuPTR, (uintptr_t)ev_driver); + GPR_ASSERT(ev_driver->fds == NULL); + gpr_mu_destroy(&ev_driver->mu); + ares_destroy(ev_driver->channel); + gpr_free(ev_driver); + } +} + +static void fd_node_destroy(grpc_exec_ctx *exec_ctx, fd_node *fdn) { + gpr_log(GPR_DEBUG, "delete fd: %d", grpc_fd_wrapped_fd(fdn->fd)); + GPR_ASSERT(!fdn->readable_registered); + GPR_ASSERT(!fdn->writable_registered); + gpr_mu_destroy(&fdn->mu); + /* c-ares library has closed the fd inside grpc_fd. This fd may be picked up + immediately by another thread, and should not be closed by the following + grpc_fd_orphan. */ + grpc_fd_orphan(exec_ctx, fdn->fd, NULL, NULL, true /* already_closed */, + "c-ares query finished"); + gpr_free(fdn); +} + +static void fd_node_shutdown(grpc_exec_ctx *exec_ctx, fd_node *fdn) { + gpr_mu_lock(&fdn->mu); + fdn->shutting_down = true; + if (!fdn->readable_registered && !fdn->writable_registered) { + gpr_mu_unlock(&fdn->mu); + fd_node_destroy(exec_ctx, fdn); + } else { + grpc_fd_shutdown(exec_ctx, fdn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "c-ares fd shutdown")); + gpr_mu_unlock(&fdn->mu); + } +} + +grpc_error *grpc_ares_ev_driver_create(grpc_ares_ev_driver **ev_driver, + grpc_pollset_set *pollset_set) { + *ev_driver = (grpc_ares_ev_driver *)gpr_malloc(sizeof(grpc_ares_ev_driver)); + int status = ares_init(&(*ev_driver)->channel); + gpr_log(GPR_DEBUG, "grpc_ares_ev_driver_create"); + if (status != ARES_SUCCESS) { + char *err_msg; + gpr_asprintf(&err_msg, "Failed to init ares channel. C-ares error: %s", + ares_strerror(status)); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); + gpr_free(err_msg); + gpr_free(*ev_driver); + return err; + } + gpr_mu_init(&(*ev_driver)->mu); + gpr_ref_init(&(*ev_driver)->refs, 1); + (*ev_driver)->pollset_set = pollset_set; + (*ev_driver)->fds = NULL; + (*ev_driver)->working = false; + (*ev_driver)->shutting_down = false; + return GRPC_ERROR_NONE; +} + +void grpc_ares_ev_driver_destroy(grpc_ares_ev_driver *ev_driver) { + // It's not safe to shut down remaining fds here directly, becauses + // ares_host_callback does not provide an exec_ctx. We mark the event driver + // as being shut down. If the event driver is working, + // grpc_ares_notify_on_event_locked will shut down the fds; if it's not + // working, there are no fds to shut down. + gpr_mu_lock(&ev_driver->mu); + ev_driver->shutting_down = true; + gpr_mu_unlock(&ev_driver->mu); + grpc_ares_ev_driver_unref(ev_driver); +} + +void grpc_ares_ev_driver_shutdown(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver) { + gpr_mu_lock(&ev_driver->mu); + ev_driver->shutting_down = true; + fd_node *fn = ev_driver->fds; + while (fn != NULL) { + grpc_fd_shutdown(exec_ctx, fn->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "grpc_ares_ev_driver_shutdown")); + fn = fn->next; + } + gpr_mu_unlock(&ev_driver->mu); +} + +// Search fd in the fd_node list head. This is an O(n) search, the max possible +// value of n is ARES_GETSOCK_MAXNUM (16). n is typically 1 - 2 in our tests. +static fd_node *pop_fd_node(fd_node **head, int fd) { + fd_node dummy_head; + dummy_head.next = *head; + fd_node *node = &dummy_head; + while (node->next != NULL) { + if (grpc_fd_wrapped_fd(node->next->fd) == fd) { + fd_node *ret = node->next; + node->next = node->next->next; + *head = dummy_head.next; + return ret; + } + node = node->next; + } + return NULL; +} + +/* Check if \a fd is still readable */ +static bool grpc_ares_is_fd_still_readable(grpc_ares_ev_driver *ev_driver, + int fd) { + size_t bytes_available = 0; + return ioctl(fd, FIONREAD, &bytes_available) == 0 && bytes_available > 0; +} + +static void on_readable_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + fd_node *fdn = (fd_node *)arg; + grpc_ares_ev_driver *ev_driver = fdn->ev_driver; + gpr_mu_lock(&fdn->mu); + const int fd = grpc_fd_wrapped_fd(fdn->fd); + fdn->readable_registered = false; + if (fdn->shutting_down && !fdn->writable_registered) { + gpr_mu_unlock(&fdn->mu); + fd_node_destroy(exec_ctx, fdn); + grpc_ares_ev_driver_unref(ev_driver); + return; + } + gpr_mu_unlock(&fdn->mu); + + gpr_log(GPR_DEBUG, "readable on %d", fd); + if (error == GRPC_ERROR_NONE) { + do { + ares_process_fd(ev_driver->channel, fd, ARES_SOCKET_BAD); + } while (grpc_ares_is_fd_still_readable(ev_driver, fd)); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + gpr_mu_lock(&ev_driver->mu); + grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); + gpr_mu_unlock(&ev_driver->mu); + grpc_ares_ev_driver_unref(ev_driver); +} + +static void on_writable_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + fd_node *fdn = (fd_node *)arg; + grpc_ares_ev_driver *ev_driver = fdn->ev_driver; + gpr_mu_lock(&fdn->mu); + const int fd = grpc_fd_wrapped_fd(fdn->fd); + fdn->writable_registered = false; + if (fdn->shutting_down && !fdn->readable_registered) { + gpr_mu_unlock(&fdn->mu); + fd_node_destroy(exec_ctx, fdn); + grpc_ares_ev_driver_unref(ev_driver); + return; + } + gpr_mu_unlock(&fdn->mu); + + gpr_log(GPR_DEBUG, "writable on %d", fd); + if (error == GRPC_ERROR_NONE) { + ares_process_fd(ev_driver->channel, ARES_SOCKET_BAD, fd); + } else { + // If error is not GRPC_ERROR_NONE, it means the fd has been shutdown or + // timed out. The pending lookups made on this ev_driver will be cancelled + // by the following ares_cancel() and the on_done callbacks will be invoked + // with a status of ARES_ECANCELLED. The remaining file descriptors in this + // ev_driver will be cleaned up in the follwing + // grpc_ares_notify_on_event_locked(). + ares_cancel(ev_driver->channel); + } + gpr_mu_lock(&ev_driver->mu); + grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); + gpr_mu_unlock(&ev_driver->mu); + grpc_ares_ev_driver_unref(ev_driver); +} + +ares_channel *grpc_ares_ev_driver_get_channel(grpc_ares_ev_driver *ev_driver) { + return &ev_driver->channel; +} + +// Get the file descriptors used by the ev_driver's ares channel, register +// driver_closure with these filedescriptors. +static void grpc_ares_notify_on_event_locked(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver) { + fd_node *new_list = NULL; + if (!ev_driver->shutting_down) { + ares_socket_t socks[ARES_GETSOCK_MAXNUM]; + int socks_bitmask = + ares_getsock(ev_driver->channel, socks, ARES_GETSOCK_MAXNUM); + for (size_t i = 0; i < ARES_GETSOCK_MAXNUM; i++) { + if (ARES_GETSOCK_READABLE(socks_bitmask, i) || + ARES_GETSOCK_WRITABLE(socks_bitmask, i)) { + fd_node *fdn = pop_fd_node(&ev_driver->fds, socks[i]); + // Create a new fd_node if sock[i] is not in the fd_node list. + if (fdn == NULL) { + char *fd_name; + gpr_asprintf(&fd_name, "ares_ev_driver-%" PRIuPTR, i); + fdn = (fd_node *)gpr_malloc(sizeof(fd_node)); + gpr_log(GPR_DEBUG, "new fd: %d", socks[i]); + fdn->fd = grpc_fd_create(socks[i], fd_name); + fdn->ev_driver = ev_driver; + fdn->readable_registered = false; + fdn->writable_registered = false; + fdn->shutting_down = false; + gpr_mu_init(&fdn->mu); + GRPC_CLOSURE_INIT(&fdn->read_closure, on_readable_cb, fdn, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&fdn->write_closure, on_writable_cb, fdn, + grpc_schedule_on_exec_ctx); + grpc_pollset_set_add_fd(exec_ctx, ev_driver->pollset_set, fdn->fd); + gpr_free(fd_name); + } + fdn->next = new_list; + new_list = fdn; + gpr_mu_lock(&fdn->mu); + // Register read_closure if the socket is readable and read_closure has + // not been registered with this socket. + if (ARES_GETSOCK_READABLE(socks_bitmask, i) && + !fdn->readable_registered) { + grpc_ares_ev_driver_ref(ev_driver); + gpr_log(GPR_DEBUG, "notify read on: %d", grpc_fd_wrapped_fd(fdn->fd)); + grpc_fd_notify_on_read(exec_ctx, fdn->fd, &fdn->read_closure); + fdn->readable_registered = true; + } + // Register write_closure if the socket is writable and write_closure + // has not been registered with this socket. + if (ARES_GETSOCK_WRITABLE(socks_bitmask, i) && + !fdn->writable_registered) { + gpr_log(GPR_DEBUG, "notify write on: %d", + grpc_fd_wrapped_fd(fdn->fd)); + grpc_ares_ev_driver_ref(ev_driver); + grpc_fd_notify_on_write(exec_ctx, fdn->fd, &fdn->write_closure); + fdn->writable_registered = true; + } + gpr_mu_unlock(&fdn->mu); + } + } + } + // Any remaining fds in ev_driver->fds were not returned by ares_getsock() and + // are therefore no longer in use, so they can be shut down and removed from + // the list. + while (ev_driver->fds != NULL) { + fd_node *cur = ev_driver->fds; + ev_driver->fds = ev_driver->fds->next; + fd_node_shutdown(exec_ctx, cur); + } + ev_driver->fds = new_list; + // If the ev driver has no working fd, all the tasks are done. + if (new_list == NULL) { + ev_driver->working = false; + gpr_log(GPR_DEBUG, "ev driver stop working"); + } +} + +void grpc_ares_ev_driver_start(grpc_exec_ctx *exec_ctx, + grpc_ares_ev_driver *ev_driver) { + gpr_mu_lock(&ev_driver->mu); + if (!ev_driver->working) { + ev_driver->working = true; + grpc_ares_notify_on_event_locked(exec_ctx, ev_driver); + } + gpr_mu_unlock(&ev_driver->mu); +} + +#endif /* GRPC_ARES == 1 && defined(GRPC_POSIX_SOCKET) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c deleted file mode 100644 index 04379975e1..0000000000 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c +++ /dev/null @@ -1,550 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#if GRPC_ARES == 1 && !defined(GRPC_UV) - -#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/parse_address.h" -#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/nameser.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/string.h" - -static gpr_once g_basic_init = GPR_ONCE_INIT; -static gpr_mu g_init_mu; - -struct grpc_ares_request { - /** indicates the DNS server to use, if specified */ - struct ares_addr_port_node dns_server_addr; - /** following members are set in grpc_resolve_address_ares_impl */ - /** closure to call when the request completes */ - grpc_closure *on_done; - /** the pointer to receive the resolved addresses */ - grpc_lb_addresses **lb_addrs_out; - /** the pointer to receive the service config in JSON */ - char **service_config_json_out; - /** the evernt driver used by this request */ - grpc_ares_ev_driver *ev_driver; - /** number of ongoing queries */ - gpr_refcount pending_queries; - - /** mutex guarding the rest of the state */ - gpr_mu mu; - /** is there at least one successful query, set in on_done_cb */ - bool success; - /** the errors explaining the request failure, set in on_done_cb */ - grpc_error *error; -}; - -typedef struct grpc_ares_hostbyname_request { - /** following members are set in create_hostbyname_request */ - /** the top-level request instance */ - grpc_ares_request *parent_request; - /** host to resolve, parsed from the name to resolve */ - char *host; - /** port to fill in sockaddr_in, parsed from the name to resolve */ - uint16_t port; - /** is it a grpclb address */ - bool is_balancer; -} grpc_ares_hostbyname_request; - -static void do_basic_init(void) { gpr_mu_init(&g_init_mu); } - -static uint16_t strhtons(const char *port) { - if (strcmp(port, "http") == 0) { - return htons(80); - } else if (strcmp(port, "https") == 0) { - return htons(443); - } - return htons((unsigned short)atoi(port)); -} - -static void grpc_ares_request_ref(grpc_ares_request *r) { - gpr_ref(&r->pending_queries); -} - -static void grpc_ares_request_unref(grpc_exec_ctx *exec_ctx, - grpc_ares_request *r) { - /* If there are no pending queries, invoke on_done callback and destroy the - request */ - if (gpr_unref(&r->pending_queries)) { - /* TODO(zyc): Sort results with RFC6724 before invoking on_done. */ - if (exec_ctx == NULL) { - /* A new exec_ctx is created here, as the c-ares interface does not - provide one in ares_host_callback. It's safe to schedule on_done with - the newly created exec_ctx, since the caller has been warned not to - acquire locks in on_done. ares_dns_resolver is using combiner to - protect resources needed by on_done. */ - grpc_exec_ctx new_exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_CLOSURE_SCHED(&new_exec_ctx, r->on_done, r->error); - grpc_exec_ctx_finish(&new_exec_ctx); - } else { - GRPC_CLOSURE_SCHED(exec_ctx, r->on_done, r->error); - } - gpr_mu_destroy(&r->mu); - grpc_ares_ev_driver_destroy(r->ev_driver); - gpr_free(r); - } -} - -static grpc_ares_hostbyname_request *create_hostbyname_request( - grpc_ares_request *parent_request, char *host, uint16_t port, - bool is_balancer) { - grpc_ares_hostbyname_request *hr = (grpc_ares_hostbyname_request *)gpr_zalloc( - sizeof(grpc_ares_hostbyname_request)); - hr->parent_request = parent_request; - hr->host = gpr_strdup(host); - hr->port = port; - hr->is_balancer = is_balancer; - grpc_ares_request_ref(parent_request); - return hr; -} - -static void destroy_hostbyname_request(grpc_exec_ctx *exec_ctx, - grpc_ares_hostbyname_request *hr) { - grpc_ares_request_unref(exec_ctx, hr->parent_request); - gpr_free(hr->host); - gpr_free(hr); -} - -static void on_hostbyname_done_cb(void *arg, int status, int timeouts, - struct hostent *hostent) { - grpc_ares_hostbyname_request *hr = (grpc_ares_hostbyname_request *)arg; - grpc_ares_request *r = hr->parent_request; - gpr_mu_lock(&r->mu); - if (status == ARES_SUCCESS) { - GRPC_ERROR_UNREF(r->error); - r->error = GRPC_ERROR_NONE; - r->success = true; - grpc_lb_addresses **lb_addresses = r->lb_addrs_out; - if (*lb_addresses == NULL) { - *lb_addresses = grpc_lb_addresses_create(0, NULL); - } - size_t prev_naddr = (*lb_addresses)->num_addresses; - size_t i; - for (i = 0; hostent->h_addr_list[i] != NULL; i++) { - } - (*lb_addresses)->num_addresses += i; - (*lb_addresses)->addresses = (grpc_lb_address *)gpr_realloc( - (*lb_addresses)->addresses, - sizeof(grpc_lb_address) * (*lb_addresses)->num_addresses); - for (i = prev_naddr; i < (*lb_addresses)->num_addresses; i++) { - switch (hostent->h_addrtype) { - case AF_INET6: { - size_t addr_len = sizeof(struct sockaddr_in6); - struct sockaddr_in6 addr; - memset(&addr, 0, addr_len); - memcpy(&addr.sin6_addr, hostent->h_addr_list[i - prev_naddr], - sizeof(struct in6_addr)); - addr.sin6_family = (sa_family_t)hostent->h_addrtype; - addr.sin6_port = hr->port; - grpc_lb_addresses_set_address( - *lb_addresses, i, &addr, addr_len, - hr->is_balancer /* is_balancer */, - hr->is_balancer ? hr->host : NULL /* balancer_name */, - NULL /* user_data */); - char output[INET6_ADDRSTRLEN]; - ares_inet_ntop(AF_INET6, &addr.sin6_addr, output, INET6_ADDRSTRLEN); - gpr_log(GPR_DEBUG, - "c-ares resolver gets a AF_INET6 result: \n" - " addr: %s\n port: %d\n sin6_scope_id: %d\n", - output, ntohs(hr->port), addr.sin6_scope_id); - break; - } - case AF_INET: { - size_t addr_len = sizeof(struct sockaddr_in); - struct sockaddr_in addr; - memset(&addr, 0, addr_len); - memcpy(&addr.sin_addr, hostent->h_addr_list[i - prev_naddr], - sizeof(struct in_addr)); - addr.sin_family = (sa_family_t)hostent->h_addrtype; - addr.sin_port = hr->port; - grpc_lb_addresses_set_address( - *lb_addresses, i, &addr, addr_len, - hr->is_balancer /* is_balancer */, - hr->is_balancer ? hr->host : NULL /* balancer_name */, - NULL /* user_data */); - char output[INET_ADDRSTRLEN]; - ares_inet_ntop(AF_INET, &addr.sin_addr, output, INET_ADDRSTRLEN); - gpr_log(GPR_DEBUG, - "c-ares resolver gets a AF_INET result: \n" - " addr: %s\n port: %d\n", - output, ntohs(hr->port)); - break; - } - } - } - } else if (!r->success) { - char *error_msg; - gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", - ares_strerror(status)); - grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); - gpr_free(error_msg); - if (r->error == GRPC_ERROR_NONE) { - r->error = error; - } else { - r->error = grpc_error_add_child(error, r->error); - } - } - gpr_mu_unlock(&r->mu); - destroy_hostbyname_request(NULL, hr); -} - -static void on_srv_query_done_cb(void *arg, int status, int timeouts, - unsigned char *abuf, int alen) { - grpc_ares_request *r = (grpc_ares_request *)arg; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_log(GPR_DEBUG, "on_query_srv_done_cb"); - if (status == ARES_SUCCESS) { - gpr_log(GPR_DEBUG, "on_query_srv_done_cb ARES_SUCCESS"); - struct ares_srv_reply *reply; - const int parse_status = ares_parse_srv_reply(abuf, alen, &reply); - if (parse_status == ARES_SUCCESS) { - ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); - for (struct ares_srv_reply *srv_it = reply; srv_it != NULL; - srv_it = srv_it->next) { - if (grpc_ipv6_loopback_available()) { - grpc_ares_hostbyname_request *hr = create_hostbyname_request( - r, srv_it->host, htons(srv_it->port), true /* is_balancer */); - ares_gethostbyname(*channel, hr->host, AF_INET6, - on_hostbyname_done_cb, hr); - } - grpc_ares_hostbyname_request *hr = create_hostbyname_request( - r, srv_it->host, htons(srv_it->port), true /* is_balancer */); - ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_cb, - hr); - grpc_ares_ev_driver_start(&exec_ctx, r->ev_driver); - } - } - if (reply != NULL) { - ares_free_data(reply); - } - } else if (!r->success) { - char *error_msg; - gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", - ares_strerror(status)); - grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); - gpr_free(error_msg); - if (r->error == GRPC_ERROR_NONE) { - r->error = error; - } else { - r->error = grpc_error_add_child(error, r->error); - } - } - grpc_ares_request_unref(&exec_ctx, r); - grpc_exec_ctx_finish(&exec_ctx); -} - -static const char g_service_config_attribute_prefix[] = "grpc_config="; - -static void on_txt_done_cb(void *arg, int status, int timeouts, - unsigned char *buf, int len) { - gpr_log(GPR_DEBUG, "on_txt_done_cb"); - char *error_msg; - grpc_ares_request *r = (grpc_ares_request *)arg; - const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1; - struct ares_txt_ext *result = NULL; - struct ares_txt_ext *reply = NULL; - grpc_error *error = GRPC_ERROR_NONE; - gpr_mu_lock(&r->mu); - if (status != ARES_SUCCESS) goto fail; - status = ares_parse_txt_reply_ext(buf, len, &reply); - if (status != ARES_SUCCESS) goto fail; - // Find service config in TXT record. - for (result = reply; result != NULL; result = result->next) { - if (result->record_start && - memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) == - 0) { - break; - } - } - // Found a service config record. - if (result != NULL) { - size_t service_config_len = result->length - prefix_len; - *r->service_config_json_out = (char *)gpr_malloc(service_config_len + 1); - memcpy(*r->service_config_json_out, result->txt + prefix_len, - service_config_len); - for (result = result->next; result != NULL && !result->record_start; - result = result->next) { - *r->service_config_json_out = (char *)gpr_realloc( - *r->service_config_json_out, service_config_len + result->length + 1); - memcpy(*r->service_config_json_out + service_config_len, result->txt, - result->length); - service_config_len += result->length; - } - (*r->service_config_json_out)[service_config_len] = '\0'; - gpr_log(GPR_INFO, "found service config: %s", *r->service_config_json_out); - } - // Clean up. - ares_free_data(reply); - goto done; -fail: - gpr_asprintf(&error_msg, "C-ares TXT lookup status is not ARES_SUCCESS: %s", - ares_strerror(status)); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); - gpr_free(error_msg); - if (r->error == GRPC_ERROR_NONE) { - r->error = error; - } else { - r->error = grpc_error_add_child(error, r->error); - } -done: - gpr_mu_unlock(&r->mu); - grpc_ares_request_unref(NULL, r); -} - -static grpc_ares_request *grpc_dns_lookup_ares_impl( - grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, - const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, - char **service_config_json) { - grpc_error *error = GRPC_ERROR_NONE; - grpc_ares_hostbyname_request *hr = NULL; - grpc_ares_request *r = NULL; - ares_channel *channel = NULL; - /* TODO(zyc): Enable tracing after #9603 is checked in */ - /* if (grpc_dns_trace) { - gpr_log(GPR_DEBUG, "resolve_address (blocking): name=%s, default_port=%s", - name, default_port); - } */ - - /* parse name, splitting it into host and port parts */ - char *host; - char *port; - gpr_split_host_port(name, &host, &port); - if (host == NULL) { - error = grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); - goto error_cleanup; - } else if (port == NULL) { - if (default_port == NULL) { - error = grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); - goto error_cleanup; - } - port = gpr_strdup(default_port); - } - - grpc_ares_ev_driver *ev_driver; - error = grpc_ares_ev_driver_create(&ev_driver, interested_parties); - if (error != GRPC_ERROR_NONE) goto error_cleanup; - - r = (grpc_ares_request *)gpr_zalloc(sizeof(grpc_ares_request)); - gpr_mu_init(&r->mu); - r->ev_driver = ev_driver; - r->on_done = on_done; - r->lb_addrs_out = addrs; - r->service_config_json_out = service_config_json; - r->success = false; - r->error = GRPC_ERROR_NONE; - channel = grpc_ares_ev_driver_get_channel(r->ev_driver); - - // If dns_server is specified, use it. - if (dns_server != NULL) { - gpr_log(GPR_INFO, "Using DNS server %s", dns_server); - grpc_resolved_address addr; - if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) { - r->dns_server_addr.family = AF_INET; - struct sockaddr_in *in = (struct sockaddr_in *)addr.addr; - memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr, - sizeof(struct in_addr)); - r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); - r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); - } else if (grpc_parse_ipv6_hostport(dns_server, &addr, - false /* log_errors */)) { - r->dns_server_addr.family = AF_INET6; - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr.addr; - memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr, - sizeof(struct in6_addr)); - r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); - r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); - } else { - error = grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("cannot parse authority"), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); - gpr_free(r); - goto error_cleanup; - } - int status = ares_set_servers_ports(*channel, &r->dns_server_addr); - if (status != ARES_SUCCESS) { - char *error_msg; - gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", - ares_strerror(status)); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); - gpr_free(error_msg); - gpr_free(r); - goto error_cleanup; - } - } - gpr_ref_init(&r->pending_queries, 1); - if (grpc_ipv6_loopback_available()) { - hr = create_hostbyname_request(r, host, strhtons(port), - false /* is_balancer */); - ares_gethostbyname(*channel, hr->host, AF_INET6, on_hostbyname_done_cb, hr); - } - hr = create_hostbyname_request(r, host, strhtons(port), - false /* is_balancer */); - ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_cb, hr); - if (check_grpclb) { - /* Query the SRV record */ - grpc_ares_request_ref(r); - char *service_name; - gpr_asprintf(&service_name, "_grpclb._tcp.%s", host); - ares_query(*channel, service_name, ns_c_in, ns_t_srv, on_srv_query_done_cb, - r); - gpr_free(service_name); - } - if (service_config_json != NULL) { - grpc_ares_request_ref(r); - ares_search(*channel, hr->host, ns_c_in, ns_t_txt, on_txt_done_cb, r); - } - /* TODO(zyc): Handle CNAME records here. */ - grpc_ares_ev_driver_start(exec_ctx, r->ev_driver); - grpc_ares_request_unref(exec_ctx, r); - gpr_free(host); - gpr_free(port); - return r; - -error_cleanup: - GRPC_CLOSURE_SCHED(exec_ctx, on_done, error); - gpr_free(host); - gpr_free(port); - return NULL; -} - -grpc_ares_request *(*grpc_dns_lookup_ares)( - grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, - const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, - char **service_config_json) = grpc_dns_lookup_ares_impl; - -void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) { - if (grpc_dns_lookup_ares == grpc_dns_lookup_ares_impl) { - grpc_ares_ev_driver_shutdown(exec_ctx, r->ev_driver); - } -} - -grpc_error *grpc_ares_init(void) { - gpr_once_init(&g_basic_init, do_basic_init); - gpr_mu_lock(&g_init_mu); - int status = ares_library_init(ARES_LIB_INIT_ALL); - gpr_mu_unlock(&g_init_mu); - - if (status != ARES_SUCCESS) { - char *error_msg; - gpr_asprintf(&error_msg, "ares_library_init failed: %s", - ares_strerror(status)); - grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); - gpr_free(error_msg); - return error; - } - return GRPC_ERROR_NONE; -} - -void grpc_ares_cleanup(void) { - gpr_mu_lock(&g_init_mu); - ares_library_cleanup(); - gpr_mu_unlock(&g_init_mu); -} - -/* - * grpc_resolve_address_ares related structs and functions - */ - -typedef struct grpc_resolve_address_ares_request { - /** the pointer to receive the resolved addresses */ - grpc_resolved_addresses **addrs_out; - /** currently resolving lb addresses */ - grpc_lb_addresses *lb_addrs; - /** closure to call when the resolve_address_ares request completes */ - grpc_closure *on_resolve_address_done; - /** a closure wrapping on_dns_lookup_done_cb, which should be invoked when the - grpc_dns_lookup_ares operation is done. */ - grpc_closure on_dns_lookup_done; -} grpc_resolve_address_ares_request; - -static void on_dns_lookup_done_cb(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_resolve_address_ares_request *r = - (grpc_resolve_address_ares_request *)arg; - grpc_resolved_addresses **resolved_addresses = r->addrs_out; - if (r->lb_addrs == NULL || r->lb_addrs->num_addresses == 0) { - *resolved_addresses = NULL; - } else { - *resolved_addresses = - (grpc_resolved_addresses *)gpr_zalloc(sizeof(grpc_resolved_addresses)); - (*resolved_addresses)->naddrs = r->lb_addrs->num_addresses; - (*resolved_addresses)->addrs = (grpc_resolved_address *)gpr_zalloc( - sizeof(grpc_resolved_address) * (*resolved_addresses)->naddrs); - for (size_t i = 0; i < (*resolved_addresses)->naddrs; i++) { - GPR_ASSERT(!r->lb_addrs->addresses[i].is_balancer); - memcpy(&(*resolved_addresses)->addrs[i], - &r->lb_addrs->addresses[i].address, sizeof(grpc_resolved_address)); - } - } - GRPC_CLOSURE_SCHED(exec_ctx, r->on_resolve_address_done, - GRPC_ERROR_REF(error)); - grpc_lb_addresses_destroy(exec_ctx, r->lb_addrs); - gpr_free(r); -} - -static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, - const char *name, - const char *default_port, - grpc_pollset_set *interested_parties, - grpc_closure *on_done, - grpc_resolved_addresses **addrs) { - grpc_resolve_address_ares_request *r = - (grpc_resolve_address_ares_request *)gpr_zalloc( - sizeof(grpc_resolve_address_ares_request)); - r->addrs_out = addrs; - r->on_resolve_address_done = on_done; - GRPC_CLOSURE_INIT(&r->on_dns_lookup_done, on_dns_lookup_done_cb, r, - grpc_schedule_on_exec_ctx); - grpc_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port, - interested_parties, &r->on_dns_lookup_done, &r->lb_addrs, - false /* check_grpclb */, - NULL /* service_config_json */); -} - -void (*grpc_resolve_address_ares)( - grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, - grpc_pollset_set *interested_parties, grpc_closure *on_done, - grpc_resolved_addresses **addrs) = grpc_resolve_address_ares_impl; - -#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc new file mode 100644 index 0000000000..04379975e1 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc @@ -0,0 +1,550 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#if GRPC_ARES == 1 && !defined(GRPC_UV) + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/nameser.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/string.h" + +static gpr_once g_basic_init = GPR_ONCE_INIT; +static gpr_mu g_init_mu; + +struct grpc_ares_request { + /** indicates the DNS server to use, if specified */ + struct ares_addr_port_node dns_server_addr; + /** following members are set in grpc_resolve_address_ares_impl */ + /** closure to call when the request completes */ + grpc_closure *on_done; + /** the pointer to receive the resolved addresses */ + grpc_lb_addresses **lb_addrs_out; + /** the pointer to receive the service config in JSON */ + char **service_config_json_out; + /** the evernt driver used by this request */ + grpc_ares_ev_driver *ev_driver; + /** number of ongoing queries */ + gpr_refcount pending_queries; + + /** mutex guarding the rest of the state */ + gpr_mu mu; + /** is there at least one successful query, set in on_done_cb */ + bool success; + /** the errors explaining the request failure, set in on_done_cb */ + grpc_error *error; +}; + +typedef struct grpc_ares_hostbyname_request { + /** following members are set in create_hostbyname_request */ + /** the top-level request instance */ + grpc_ares_request *parent_request; + /** host to resolve, parsed from the name to resolve */ + char *host; + /** port to fill in sockaddr_in, parsed from the name to resolve */ + uint16_t port; + /** is it a grpclb address */ + bool is_balancer; +} grpc_ares_hostbyname_request; + +static void do_basic_init(void) { gpr_mu_init(&g_init_mu); } + +static uint16_t strhtons(const char *port) { + if (strcmp(port, "http") == 0) { + return htons(80); + } else if (strcmp(port, "https") == 0) { + return htons(443); + } + return htons((unsigned short)atoi(port)); +} + +static void grpc_ares_request_ref(grpc_ares_request *r) { + gpr_ref(&r->pending_queries); +} + +static void grpc_ares_request_unref(grpc_exec_ctx *exec_ctx, + grpc_ares_request *r) { + /* If there are no pending queries, invoke on_done callback and destroy the + request */ + if (gpr_unref(&r->pending_queries)) { + /* TODO(zyc): Sort results with RFC6724 before invoking on_done. */ + if (exec_ctx == NULL) { + /* A new exec_ctx is created here, as the c-ares interface does not + provide one in ares_host_callback. It's safe to schedule on_done with + the newly created exec_ctx, since the caller has been warned not to + acquire locks in on_done. ares_dns_resolver is using combiner to + protect resources needed by on_done. */ + grpc_exec_ctx new_exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_CLOSURE_SCHED(&new_exec_ctx, r->on_done, r->error); + grpc_exec_ctx_finish(&new_exec_ctx); + } else { + GRPC_CLOSURE_SCHED(exec_ctx, r->on_done, r->error); + } + gpr_mu_destroy(&r->mu); + grpc_ares_ev_driver_destroy(r->ev_driver); + gpr_free(r); + } +} + +static grpc_ares_hostbyname_request *create_hostbyname_request( + grpc_ares_request *parent_request, char *host, uint16_t port, + bool is_balancer) { + grpc_ares_hostbyname_request *hr = (grpc_ares_hostbyname_request *)gpr_zalloc( + sizeof(grpc_ares_hostbyname_request)); + hr->parent_request = parent_request; + hr->host = gpr_strdup(host); + hr->port = port; + hr->is_balancer = is_balancer; + grpc_ares_request_ref(parent_request); + return hr; +} + +static void destroy_hostbyname_request(grpc_exec_ctx *exec_ctx, + grpc_ares_hostbyname_request *hr) { + grpc_ares_request_unref(exec_ctx, hr->parent_request); + gpr_free(hr->host); + gpr_free(hr); +} + +static void on_hostbyname_done_cb(void *arg, int status, int timeouts, + struct hostent *hostent) { + grpc_ares_hostbyname_request *hr = (grpc_ares_hostbyname_request *)arg; + grpc_ares_request *r = hr->parent_request; + gpr_mu_lock(&r->mu); + if (status == ARES_SUCCESS) { + GRPC_ERROR_UNREF(r->error); + r->error = GRPC_ERROR_NONE; + r->success = true; + grpc_lb_addresses **lb_addresses = r->lb_addrs_out; + if (*lb_addresses == NULL) { + *lb_addresses = grpc_lb_addresses_create(0, NULL); + } + size_t prev_naddr = (*lb_addresses)->num_addresses; + size_t i; + for (i = 0; hostent->h_addr_list[i] != NULL; i++) { + } + (*lb_addresses)->num_addresses += i; + (*lb_addresses)->addresses = (grpc_lb_address *)gpr_realloc( + (*lb_addresses)->addresses, + sizeof(grpc_lb_address) * (*lb_addresses)->num_addresses); + for (i = prev_naddr; i < (*lb_addresses)->num_addresses; i++) { + switch (hostent->h_addrtype) { + case AF_INET6: { + size_t addr_len = sizeof(struct sockaddr_in6); + struct sockaddr_in6 addr; + memset(&addr, 0, addr_len); + memcpy(&addr.sin6_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in6_addr)); + addr.sin6_family = (sa_family_t)hostent->h_addrtype; + addr.sin6_port = hr->port; + grpc_lb_addresses_set_address( + *lb_addresses, i, &addr, addr_len, + hr->is_balancer /* is_balancer */, + hr->is_balancer ? hr->host : NULL /* balancer_name */, + NULL /* user_data */); + char output[INET6_ADDRSTRLEN]; + ares_inet_ntop(AF_INET6, &addr.sin6_addr, output, INET6_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET6 result: \n" + " addr: %s\n port: %d\n sin6_scope_id: %d\n", + output, ntohs(hr->port), addr.sin6_scope_id); + break; + } + case AF_INET: { + size_t addr_len = sizeof(struct sockaddr_in); + struct sockaddr_in addr; + memset(&addr, 0, addr_len); + memcpy(&addr.sin_addr, hostent->h_addr_list[i - prev_naddr], + sizeof(struct in_addr)); + addr.sin_family = (sa_family_t)hostent->h_addrtype; + addr.sin_port = hr->port; + grpc_lb_addresses_set_address( + *lb_addresses, i, &addr, addr_len, + hr->is_balancer /* is_balancer */, + hr->is_balancer ? hr->host : NULL /* balancer_name */, + NULL /* user_data */); + char output[INET_ADDRSTRLEN]; + ares_inet_ntop(AF_INET, &addr.sin_addr, output, INET_ADDRSTRLEN); + gpr_log(GPR_DEBUG, + "c-ares resolver gets a AF_INET result: \n" + " addr: %s\n port: %d\n", + output, ntohs(hr->port)); + break; + } + } + } + } else if (!r->success) { + char *error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } + } + gpr_mu_unlock(&r->mu); + destroy_hostbyname_request(NULL, hr); +} + +static void on_srv_query_done_cb(void *arg, int status, int timeouts, + unsigned char *abuf, int alen) { + grpc_ares_request *r = (grpc_ares_request *)arg; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_log(GPR_DEBUG, "on_query_srv_done_cb"); + if (status == ARES_SUCCESS) { + gpr_log(GPR_DEBUG, "on_query_srv_done_cb ARES_SUCCESS"); + struct ares_srv_reply *reply; + const int parse_status = ares_parse_srv_reply(abuf, alen, &reply); + if (parse_status == ARES_SUCCESS) { + ares_channel *channel = grpc_ares_ev_driver_get_channel(r->ev_driver); + for (struct ares_srv_reply *srv_it = reply; srv_it != NULL; + srv_it = srv_it->next) { + if (grpc_ipv6_loopback_available()) { + grpc_ares_hostbyname_request *hr = create_hostbyname_request( + r, srv_it->host, htons(srv_it->port), true /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET6, + on_hostbyname_done_cb, hr); + } + grpc_ares_hostbyname_request *hr = create_hostbyname_request( + r, srv_it->host, htons(srv_it->port), true /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_cb, + hr); + grpc_ares_ev_driver_start(&exec_ctx, r->ev_driver); + } + } + if (reply != NULL) { + ares_free_data(reply); + } + } else if (!r->success) { + char *error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } + } + grpc_ares_request_unref(&exec_ctx, r); + grpc_exec_ctx_finish(&exec_ctx); +} + +static const char g_service_config_attribute_prefix[] = "grpc_config="; + +static void on_txt_done_cb(void *arg, int status, int timeouts, + unsigned char *buf, int len) { + gpr_log(GPR_DEBUG, "on_txt_done_cb"); + char *error_msg; + grpc_ares_request *r = (grpc_ares_request *)arg; + const size_t prefix_len = sizeof(g_service_config_attribute_prefix) - 1; + struct ares_txt_ext *result = NULL; + struct ares_txt_ext *reply = NULL; + grpc_error *error = GRPC_ERROR_NONE; + gpr_mu_lock(&r->mu); + if (status != ARES_SUCCESS) goto fail; + status = ares_parse_txt_reply_ext(buf, len, &reply); + if (status != ARES_SUCCESS) goto fail; + // Find service config in TXT record. + for (result = reply; result != NULL; result = result->next) { + if (result->record_start && + memcmp(result->txt, g_service_config_attribute_prefix, prefix_len) == + 0) { + break; + } + } + // Found a service config record. + if (result != NULL) { + size_t service_config_len = result->length - prefix_len; + *r->service_config_json_out = (char *)gpr_malloc(service_config_len + 1); + memcpy(*r->service_config_json_out, result->txt + prefix_len, + service_config_len); + for (result = result->next; result != NULL && !result->record_start; + result = result->next) { + *r->service_config_json_out = (char *)gpr_realloc( + *r->service_config_json_out, service_config_len + result->length + 1); + memcpy(*r->service_config_json_out + service_config_len, result->txt, + result->length); + service_config_len += result->length; + } + (*r->service_config_json_out)[service_config_len] = '\0'; + gpr_log(GPR_INFO, "found service config: %s", *r->service_config_json_out); + } + // Clean up. + ares_free_data(reply); + goto done; +fail: + gpr_asprintf(&error_msg, "C-ares TXT lookup status is not ARES_SUCCESS: %s", + ares_strerror(status)); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + if (r->error == GRPC_ERROR_NONE) { + r->error = error; + } else { + r->error = grpc_error_add_child(error, r->error); + } +done: + gpr_mu_unlock(&r->mu); + grpc_ares_request_unref(NULL, r); +} + +static grpc_ares_request *grpc_dns_lookup_ares_impl( + grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, + const char *default_port, grpc_pollset_set *interested_parties, + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) { + grpc_error *error = GRPC_ERROR_NONE; + grpc_ares_hostbyname_request *hr = NULL; + grpc_ares_request *r = NULL; + ares_channel *channel = NULL; + /* TODO(zyc): Enable tracing after #9603 is checked in */ + /* if (grpc_dns_trace) { + gpr_log(GPR_DEBUG, "resolve_address (blocking): name=%s, default_port=%s", + name, default_port); + } */ + + /* parse name, splitting it into host and port parts */ + char *host; + char *port; + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto error_cleanup; + } else if (port == NULL) { + if (default_port == NULL) { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto error_cleanup; + } + port = gpr_strdup(default_port); + } + + grpc_ares_ev_driver *ev_driver; + error = grpc_ares_ev_driver_create(&ev_driver, interested_parties); + if (error != GRPC_ERROR_NONE) goto error_cleanup; + + r = (grpc_ares_request *)gpr_zalloc(sizeof(grpc_ares_request)); + gpr_mu_init(&r->mu); + r->ev_driver = ev_driver; + r->on_done = on_done; + r->lb_addrs_out = addrs; + r->service_config_json_out = service_config_json; + r->success = false; + r->error = GRPC_ERROR_NONE; + channel = grpc_ares_ev_driver_get_channel(r->ev_driver); + + // If dns_server is specified, use it. + if (dns_server != NULL) { + gpr_log(GPR_INFO, "Using DNS server %s", dns_server); + grpc_resolved_address addr; + if (grpc_parse_ipv4_hostport(dns_server, &addr, false /* log_errors */)) { + r->dns_server_addr.family = AF_INET; + struct sockaddr_in *in = (struct sockaddr_in *)addr.addr; + memcpy(&r->dns_server_addr.addr.addr4, &in->sin_addr, + sizeof(struct in_addr)); + r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); + r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); + } else if (grpc_parse_ipv6_hostport(dns_server, &addr, + false /* log_errors */)) { + r->dns_server_addr.family = AF_INET6; + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr.addr; + memcpy(&r->dns_server_addr.addr.addr6, &in6->sin6_addr, + sizeof(struct in6_addr)); + r->dns_server_addr.tcp_port = grpc_sockaddr_get_port(&addr); + r->dns_server_addr.udp_port = grpc_sockaddr_get_port(&addr); + } else { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("cannot parse authority"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + gpr_free(r); + goto error_cleanup; + } + int status = ares_set_servers_ports(*channel, &r->dns_server_addr); + if (status != ARES_SUCCESS) { + char *error_msg; + gpr_asprintf(&error_msg, "C-ares status is not ARES_SUCCESS: %s", + ares_strerror(status)); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + gpr_free(r); + goto error_cleanup; + } + } + gpr_ref_init(&r->pending_queries, 1); + if (grpc_ipv6_loopback_available()) { + hr = create_hostbyname_request(r, host, strhtons(port), + false /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET6, on_hostbyname_done_cb, hr); + } + hr = create_hostbyname_request(r, host, strhtons(port), + false /* is_balancer */); + ares_gethostbyname(*channel, hr->host, AF_INET, on_hostbyname_done_cb, hr); + if (check_grpclb) { + /* Query the SRV record */ + grpc_ares_request_ref(r); + char *service_name; + gpr_asprintf(&service_name, "_grpclb._tcp.%s", host); + ares_query(*channel, service_name, ns_c_in, ns_t_srv, on_srv_query_done_cb, + r); + gpr_free(service_name); + } + if (service_config_json != NULL) { + grpc_ares_request_ref(r); + ares_search(*channel, hr->host, ns_c_in, ns_t_txt, on_txt_done_cb, r); + } + /* TODO(zyc): Handle CNAME records here. */ + grpc_ares_ev_driver_start(exec_ctx, r->ev_driver); + grpc_ares_request_unref(exec_ctx, r); + gpr_free(host); + gpr_free(port); + return r; + +error_cleanup: + GRPC_CLOSURE_SCHED(exec_ctx, on_done, error); + gpr_free(host); + gpr_free(port); + return NULL; +} + +grpc_ares_request *(*grpc_dns_lookup_ares)( + grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, + const char *default_port, grpc_pollset_set *interested_parties, + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) = grpc_dns_lookup_ares_impl; + +void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) { + if (grpc_dns_lookup_ares == grpc_dns_lookup_ares_impl) { + grpc_ares_ev_driver_shutdown(exec_ctx, r->ev_driver); + } +} + +grpc_error *grpc_ares_init(void) { + gpr_once_init(&g_basic_init, do_basic_init); + gpr_mu_lock(&g_init_mu); + int status = ares_library_init(ARES_LIB_INIT_ALL); + gpr_mu_unlock(&g_init_mu); + + if (status != ARES_SUCCESS) { + char *error_msg; + gpr_asprintf(&error_msg, "ares_library_init failed: %s", + ares_strerror(status)); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg); + gpr_free(error_msg); + return error; + } + return GRPC_ERROR_NONE; +} + +void grpc_ares_cleanup(void) { + gpr_mu_lock(&g_init_mu); + ares_library_cleanup(); + gpr_mu_unlock(&g_init_mu); +} + +/* + * grpc_resolve_address_ares related structs and functions + */ + +typedef struct grpc_resolve_address_ares_request { + /** the pointer to receive the resolved addresses */ + grpc_resolved_addresses **addrs_out; + /** currently resolving lb addresses */ + grpc_lb_addresses *lb_addrs; + /** closure to call when the resolve_address_ares request completes */ + grpc_closure *on_resolve_address_done; + /** a closure wrapping on_dns_lookup_done_cb, which should be invoked when the + grpc_dns_lookup_ares operation is done. */ + grpc_closure on_dns_lookup_done; +} grpc_resolve_address_ares_request; + +static void on_dns_lookup_done_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_resolve_address_ares_request *r = + (grpc_resolve_address_ares_request *)arg; + grpc_resolved_addresses **resolved_addresses = r->addrs_out; + if (r->lb_addrs == NULL || r->lb_addrs->num_addresses == 0) { + *resolved_addresses = NULL; + } else { + *resolved_addresses = + (grpc_resolved_addresses *)gpr_zalloc(sizeof(grpc_resolved_addresses)); + (*resolved_addresses)->naddrs = r->lb_addrs->num_addresses; + (*resolved_addresses)->addrs = (grpc_resolved_address *)gpr_zalloc( + sizeof(grpc_resolved_address) * (*resolved_addresses)->naddrs); + for (size_t i = 0; i < (*resolved_addresses)->naddrs; i++) { + GPR_ASSERT(!r->lb_addrs->addresses[i].is_balancer); + memcpy(&(*resolved_addresses)->addrs[i], + &r->lb_addrs->addresses[i].address, sizeof(grpc_resolved_address)); + } + } + GRPC_CLOSURE_SCHED(exec_ctx, r->on_resolve_address_done, + GRPC_ERROR_REF(error)); + grpc_lb_addresses_destroy(exec_ctx, r->lb_addrs); + gpr_free(r); +} + +static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, + const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) { + grpc_resolve_address_ares_request *r = + (grpc_resolve_address_ares_request *)gpr_zalloc( + sizeof(grpc_resolve_address_ares_request)); + r->addrs_out = addrs; + r->on_resolve_address_done = on_done; + GRPC_CLOSURE_INIT(&r->on_dns_lookup_done, on_dns_lookup_done_cb, r, + grpc_schedule_on_exec_ctx); + grpc_dns_lookup_ares(exec_ctx, NULL /* dns_server */, name, default_port, + interested_parties, &r->on_dns_lookup_done, &r->lb_addrs, + false /* check_grpclb */, + NULL /* service_config_json */); +} + +void (*grpc_resolve_address_ares)( + grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, + grpc_pollset_set *interested_parties, grpc_closure *on_done, + grpc_resolved_addresses **addrs) = grpc_resolve_address_ares_impl; + +#endif /* GRPC_ARES == 1 && !defined(GRPC_UV) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c deleted file mode 100644 index f2587c4520..0000000000 --- a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2016-2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#if GRPC_ARES != 1 || defined(GRPC_UV) - -#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" - -struct grpc_ares_request { - char val; -}; - -static grpc_ares_request *grpc_dns_lookup_ares_impl( - grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, - const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, - char **service_config_json) { - return NULL; -} - -grpc_ares_request *(*grpc_dns_lookup_ares)( - grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, - const char *default_port, grpc_pollset_set *interested_parties, - grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, - char **service_config_json) = grpc_dns_lookup_ares_impl; - -void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {} - -grpc_error *grpc_ares_init(void) { return GRPC_ERROR_NONE; } - -void grpc_ares_cleanup(void) {} - -static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, - const char *name, - const char *default_port, - grpc_pollset_set *interested_parties, - grpc_closure *on_done, - grpc_resolved_addresses **addrs) {} - -void (*grpc_resolve_address_ares)( - grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, - grpc_pollset_set *interested_parties, grpc_closure *on_done, - grpc_resolved_addresses **addrs) = grpc_resolve_address_ares_impl; - -#endif /* GRPC_ARES != 1 || defined(GRPC_UV) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc new file mode 100644 index 0000000000..f2587c4520 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2016-2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#if GRPC_ARES != 1 || defined(GRPC_UV) + +#include "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h" + +struct grpc_ares_request { + char val; +}; + +static grpc_ares_request *grpc_dns_lookup_ares_impl( + grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, + const char *default_port, grpc_pollset_set *interested_parties, + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) { + return NULL; +} + +grpc_ares_request *(*grpc_dns_lookup_ares)( + grpc_exec_ctx *exec_ctx, const char *dns_server, const char *name, + const char *default_port, grpc_pollset_set *interested_parties, + grpc_closure *on_done, grpc_lb_addresses **addrs, bool check_grpclb, + char **service_config_json) = grpc_dns_lookup_ares_impl; + +void grpc_cancel_ares_request(grpc_exec_ctx *exec_ctx, grpc_ares_request *r) {} + +grpc_error *grpc_ares_init(void) { return GRPC_ERROR_NONE; } + +void grpc_ares_cleanup(void) {} + +static void grpc_resolve_address_ares_impl(grpc_exec_ctx *exec_ctx, + const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) {} + +void (*grpc_resolve_address_ares)( + grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, + grpc_pollset_set *interested_parties, grpc_closure *on_done, + grpc_resolved_addresses **addrs) = grpc_resolve_address_ares_impl; + +#endif /* GRPC_ARES != 1 || defined(GRPC_UV) */ diff --git a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c deleted file mode 100644 index 5ea75f0554..0000000000 --- a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -#include "src/core/ext/filters/client_channel/lb_policy_registry.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/support/backoff.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" - -#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 -#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 -#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 -#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 -#define GRPC_DNS_RECONNECT_JITTER 0.2 - -typedef struct { - /** base class: must be first */ - grpc_resolver base; - /** name to resolve */ - char *name_to_resolve; - /** default port to use */ - char *default_port; - /** channel args. */ - grpc_channel_args *channel_args; - /** pollset_set to drive the name resolution process */ - grpc_pollset_set *interested_parties; - - /** are we currently resolving? */ - bool resolving; - /** which version of the result have we published? */ - int published_version; - /** which version of the result is current? */ - int resolved_version; - /** pending next completion, or NULL */ - grpc_closure *next_completion; - /** target result address for next completion */ - grpc_channel_args **target_result; - /** current (fully resolved) result */ - grpc_channel_args *resolved_result; - /** retry timer */ - bool have_retry_timer; - grpc_timer retry_timer; - grpc_closure on_retry; - /** retry backoff state */ - gpr_backoff backoff_state; - - /** currently resolving addresses */ - grpc_resolved_addresses *addresses; -} dns_resolver; - -static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); - -static void dns_start_resolving_locked(grpc_exec_ctx *exec_ctx, - dns_resolver *r); -static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - dns_resolver *r); - -static void dns_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void dns_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *r); -static void dns_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_channel_args **target_result, - grpc_closure *on_complete); - -static const grpc_resolver_vtable dns_resolver_vtable = { - dns_destroy, dns_shutdown_locked, dns_channel_saw_error_locked, - dns_next_locked}; - -static void dns_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - dns_resolver *r = (dns_resolver *)resolver; - if (r->have_retry_timer) { - grpc_timer_cancel(exec_ctx, &r->retry_timer); - } - if (r->next_completion != NULL) { - *r->target_result = NULL; - GRPC_CLOSURE_SCHED( - exec_ctx, r->next_completion, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); - r->next_completion = NULL; - } -} - -static void dns_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - dns_resolver *r = (dns_resolver *)resolver; - if (!r->resolving) { - gpr_backoff_reset(&r->backoff_state); - dns_start_resolving_locked(exec_ctx, r); - } -} - -static void dns_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, - grpc_channel_args **target_result, - grpc_closure *on_complete) { - dns_resolver *r = (dns_resolver *)resolver; - GPR_ASSERT(!r->next_completion); - r->next_completion = on_complete; - r->target_result = target_result; - if (r->resolved_version == 0 && !r->resolving) { - gpr_backoff_reset(&r->backoff_state); - dns_start_resolving_locked(exec_ctx, r); - } else { - dns_maybe_finish_next_locked(exec_ctx, r); - } -} - -static void dns_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - dns_resolver *r = (dns_resolver *)arg; - - r->have_retry_timer = false; - if (error == GRPC_ERROR_NONE) { - if (!r->resolving) { - dns_start_resolving_locked(exec_ctx, r); - } - } - - GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); -} - -static void dns_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - dns_resolver *r = (dns_resolver *)arg; - grpc_channel_args *result = NULL; - GPR_ASSERT(r->resolving); - r->resolving = false; - if (r->addresses != NULL) { - grpc_lb_addresses *addresses = grpc_lb_addresses_create( - r->addresses->naddrs, NULL /* user_data_vtable */); - for (size_t i = 0; i < r->addresses->naddrs; ++i) { - grpc_lb_addresses_set_address( - addresses, i, &r->addresses->addrs[i].addr, - r->addresses->addrs[i].len, false /* is_balancer */, - NULL /* balancer_name */, NULL /* user_data */); - } - grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses); - result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); - grpc_resolved_addresses_destroy(r->addresses); - grpc_lb_addresses_destroy(exec_ctx, addresses); - } else { - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); - gpr_timespec timeout = gpr_time_sub(next_try, now); - gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", - grpc_error_string(error)); - GPR_ASSERT(!r->have_retry_timer); - r->have_retry_timer = true; - GRPC_RESOLVER_REF(&r->base, "retry-timer"); - if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { - gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec, - timeout.tv_nsec); - } else { - gpr_log(GPR_DEBUG, "retrying immediately"); - } - GRPC_CLOSURE_INIT(&r->on_retry, dns_on_retry_timer_locked, r, - grpc_combiner_scheduler(r->base.combiner)); - grpc_timer_init(exec_ctx, &r->retry_timer, next_try, &r->on_retry, now); - } - if (r->resolved_result != NULL) { - grpc_channel_args_destroy(exec_ctx, r->resolved_result); - } - r->resolved_result = result; - r->resolved_version++; - dns_maybe_finish_next_locked(exec_ctx, r); - - GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving"); -} - -static void dns_start_resolving_locked(grpc_exec_ctx *exec_ctx, - dns_resolver *r) { - GRPC_RESOLVER_REF(&r->base, "dns-resolving"); - GPR_ASSERT(!r->resolving); - r->resolving = true; - r->addresses = NULL; - grpc_resolve_address( - exec_ctx, r->name_to_resolve, r->default_port, r->interested_parties, - GRPC_CLOSURE_CREATE(dns_on_resolved_locked, r, - grpc_combiner_scheduler(r->base.combiner)), - &r->addresses); -} - -static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - dns_resolver *r) { - if (r->next_completion != NULL && - r->resolved_version != r->published_version) { - *r->target_result = r->resolved_result == NULL - ? NULL - : grpc_channel_args_copy(r->resolved_result); - GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); - r->next_completion = NULL; - r->published_version = r->resolved_version; - } -} - -static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { - dns_resolver *r = (dns_resolver *)gr; - if (r->resolved_result != NULL) { - grpc_channel_args_destroy(exec_ctx, r->resolved_result); - } - grpc_pollset_set_destroy(exec_ctx, r->interested_parties); - gpr_free(r->name_to_resolve); - gpr_free(r->default_port); - grpc_channel_args_destroy(exec_ctx, r->channel_args); - gpr_free(r); -} - -static grpc_resolver *dns_create(grpc_exec_ctx *exec_ctx, - grpc_resolver_args *args, - const char *default_port) { - if (0 != strcmp(args->uri->authority, "")) { - gpr_log(GPR_ERROR, "authority based dns uri's not supported"); - return NULL; - } - // Get name from args. - char *path = args->uri->path; - if (path[0] == '/') ++path; - // Create resolver. - dns_resolver *r = (dns_resolver *)gpr_zalloc(sizeof(dns_resolver)); - grpc_resolver_init(&r->base, &dns_resolver_vtable, args->combiner); - r->name_to_resolve = gpr_strdup(path); - r->default_port = gpr_strdup(default_port); - r->channel_args = grpc_channel_args_copy(args->args); - r->interested_parties = grpc_pollset_set_create(); - if (args->pollset_set != NULL) { - grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, - args->pollset_set); - } - gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS, - GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, - GRPC_DNS_RECONNECT_JITTER, - GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, - GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); - return &r->base; -} - -/* - * FACTORY - */ - -static void dns_factory_ref(grpc_resolver_factory *factory) {} - -static void dns_factory_unref(grpc_resolver_factory *factory) {} - -static grpc_resolver *dns_factory_create_resolver( - grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, - grpc_resolver_args *args) { - return dns_create(exec_ctx, args, "https"); -} - -static char *dns_factory_get_default_host_name(grpc_resolver_factory *factory, - grpc_uri *uri) { - const char *path = uri->path; - if (path[0] == '/') ++path; - return gpr_strdup(path); -} - -static const grpc_resolver_factory_vtable dns_factory_vtable = { - dns_factory_ref, dns_factory_unref, dns_factory_create_resolver, - dns_factory_get_default_host_name, "dns"}; -static grpc_resolver_factory dns_resolver_factory = {&dns_factory_vtable}; - -static grpc_resolver_factory *dns_resolver_factory_create() { - return &dns_resolver_factory; -} - -void grpc_resolver_dns_native_init(void) { - char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); - if (resolver != NULL && gpr_stricmp(resolver, "native") == 0) { - gpr_log(GPR_DEBUG, "Using native dns resolver"); - grpc_register_resolver_type(dns_resolver_factory_create()); - } else { - grpc_resolver_factory *existing_factory = - grpc_resolver_factory_lookup("dns"); - if (existing_factory == NULL) { - gpr_log(GPR_DEBUG, "Using native dns resolver"); - grpc_register_resolver_type(dns_resolver_factory_create()); - } else { - grpc_resolver_factory_unref(existing_factory); - } - } - gpr_free(resolver); -} - -void grpc_resolver_dns_native_shutdown(void) {} diff --git a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc new file mode 100644 index 0000000000..1baf80b720 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc @@ -0,0 +1,310 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/lb_policy_registry.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/support/backoff.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +#define GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS 1 +#define GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_DNS_RECONNECT_JITTER 0.2 + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** name to resolve */ + char *name_to_resolve; + /** default port to use */ + char *default_port; + /** channel args. */ + grpc_channel_args *channel_args; + /** pollset_set to drive the name resolution process */ + grpc_pollset_set *interested_parties; + + /** are we currently resolving? */ + bool resolving; + /** which version of the result have we published? */ + int published_version; + /** which version of the result is current? */ + int resolved_version; + /** pending next completion, or NULL */ + grpc_closure *next_completion; + /** target result address for next completion */ + grpc_channel_args **target_result; + /** current (fully resolved) result */ + grpc_channel_args *resolved_result; + /** retry timer */ + bool have_retry_timer; + grpc_timer retry_timer; + grpc_closure on_retry; + /** retry backoff state */ + gpr_backoff backoff_state; + + /** currently resolving addresses */ + grpc_resolved_addresses *addresses; +} dns_resolver; + +static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); + +static void dns_start_resolving_locked(grpc_exec_ctx *exec_ctx, + dns_resolver *r); +static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + dns_resolver *r); + +static void dns_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void dns_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *r); +static void dns_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_channel_args **target_result, + grpc_closure *on_complete); + +static const grpc_resolver_vtable dns_resolver_vtable = { + dns_destroy, dns_shutdown_locked, dns_channel_saw_error_locked, + dns_next_locked}; + +static void dns_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + dns_resolver *r = (dns_resolver *)resolver; + if (r->have_retry_timer) { + grpc_timer_cancel(exec_ctx, &r->retry_timer); + } + if (r->next_completion != NULL) { + *r->target_result = NULL; + GRPC_CLOSURE_SCHED( + exec_ctx, r->next_completion, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); + r->next_completion = NULL; + } +} + +static void dns_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + dns_resolver *r = (dns_resolver *)resolver; + if (!r->resolving) { + gpr_backoff_reset(&r->backoff_state); + dns_start_resolving_locked(exec_ctx, r); + } +} + +static void dns_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *resolver, + grpc_channel_args **target_result, + grpc_closure *on_complete) { + dns_resolver *r = (dns_resolver *)resolver; + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_result = target_result; + if (r->resolved_version == 0 && !r->resolving) { + gpr_backoff_reset(&r->backoff_state); + dns_start_resolving_locked(exec_ctx, r); + } else { + dns_maybe_finish_next_locked(exec_ctx, r); + } +} + +static void dns_on_retry_timer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + dns_resolver *r = (dns_resolver *)arg; + + r->have_retry_timer = false; + if (error == GRPC_ERROR_NONE) { + if (!r->resolving) { + dns_start_resolving_locked(exec_ctx, r); + } + } + + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "retry-timer"); +} + +static void dns_on_resolved_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + dns_resolver *r = (dns_resolver *)arg; + grpc_channel_args *result = NULL; + GPR_ASSERT(r->resolving); + r->resolving = false; + if (r->addresses != NULL) { + grpc_lb_addresses *addresses = grpc_lb_addresses_create( + r->addresses->naddrs, NULL /* user_data_vtable */); + for (size_t i = 0; i < r->addresses->naddrs; ++i) { + grpc_lb_addresses_set_address( + addresses, i, &r->addresses->addrs[i].addr, + r->addresses->addrs[i].len, false /* is_balancer */, + NULL /* balancer_name */, NULL /* user_data */); + } + grpc_arg new_arg = grpc_lb_addresses_create_channel_arg(addresses); + result = grpc_channel_args_copy_and_add(r->channel_args, &new_arg, 1); + grpc_resolved_addresses_destroy(r->addresses); + grpc_lb_addresses_destroy(exec_ctx, addresses); + } else { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec next_try = gpr_backoff_step(&r->backoff_state, now); + gpr_timespec timeout = gpr_time_sub(next_try, now); + gpr_log(GPR_INFO, "dns resolution failed (will retry): %s", + grpc_error_string(error)); + GPR_ASSERT(!r->have_retry_timer); + r->have_retry_timer = true; + GRPC_RESOLVER_REF(&r->base, "retry-timer"); + if (gpr_time_cmp(timeout, gpr_time_0(timeout.clock_type)) > 0) { + gpr_log(GPR_DEBUG, "retrying in %" PRId64 ".%09d seconds", timeout.tv_sec, + timeout.tv_nsec); + } else { + gpr_log(GPR_DEBUG, "retrying immediately"); + } + GRPC_CLOSURE_INIT(&r->on_retry, dns_on_retry_timer_locked, r, + grpc_combiner_scheduler(r->base.combiner)); + grpc_timer_init(exec_ctx, &r->retry_timer, next_try, &r->on_retry, now); + } + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(exec_ctx, r->resolved_result); + } + r->resolved_result = result; + r->resolved_version++; + dns_maybe_finish_next_locked(exec_ctx, r); + + GRPC_RESOLVER_UNREF(exec_ctx, &r->base, "dns-resolving"); +} + +static void dns_start_resolving_locked(grpc_exec_ctx *exec_ctx, + dns_resolver *r) { + GRPC_RESOLVER_REF(&r->base, "dns-resolving"); + GPR_ASSERT(!r->resolving); + r->resolving = true; + r->addresses = NULL; + grpc_resolve_address( + exec_ctx, r->name_to_resolve, r->default_port, r->interested_parties, + GRPC_CLOSURE_CREATE(dns_on_resolved_locked, r, + grpc_combiner_scheduler(r->base.combiner)), + &r->addresses); +} + +static void dns_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + dns_resolver *r) { + if (r->next_completion != NULL && + r->resolved_version != r->published_version) { + *r->target_result = r->resolved_result == NULL + ? NULL + : grpc_channel_args_copy(r->resolved_result); + GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); + r->next_completion = NULL; + r->published_version = r->resolved_version; + } +} + +static void dns_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { + dns_resolver *r = (dns_resolver *)gr; + if (r->resolved_result != NULL) { + grpc_channel_args_destroy(exec_ctx, r->resolved_result); + } + grpc_pollset_set_destroy(exec_ctx, r->interested_parties); + gpr_free(r->name_to_resolve); + gpr_free(r->default_port); + grpc_channel_args_destroy(exec_ctx, r->channel_args); + gpr_free(r); +} + +static grpc_resolver *dns_create(grpc_exec_ctx *exec_ctx, + grpc_resolver_args *args, + const char *default_port) { + if (0 != strcmp(args->uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based dns uri's not supported"); + return NULL; + } + // Get name from args. + char *path = args->uri->path; + if (path[0] == '/') ++path; + // Create resolver. + dns_resolver *r = (dns_resolver *)gpr_zalloc(sizeof(dns_resolver)); + grpc_resolver_init(&r->base, &dns_resolver_vtable, args->combiner); + r->name_to_resolve = gpr_strdup(path); + r->default_port = gpr_strdup(default_port); + r->channel_args = grpc_channel_args_copy(args->args); + r->interested_parties = grpc_pollset_set_create(); + if (args->pollset_set != NULL) { + grpc_pollset_set_add_pollset_set(exec_ctx, r->interested_parties, + args->pollset_set); + } + gpr_backoff_init(&r->backoff_state, GRPC_DNS_INITIAL_CONNECT_BACKOFF_SECONDS, + GRPC_DNS_RECONNECT_BACKOFF_MULTIPLIER, + GRPC_DNS_RECONNECT_JITTER, + GRPC_DNS_MIN_CONNECT_TIMEOUT_SECONDS * 1000, + GRPC_DNS_RECONNECT_MAX_BACKOFF_SECONDS * 1000); + return &r->base; +} + +/* + * FACTORY + */ + +static void dns_factory_ref(grpc_resolver_factory *factory) {} + +static void dns_factory_unref(grpc_resolver_factory *factory) {} + +static grpc_resolver *dns_factory_create_resolver( + grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, + grpc_resolver_args *args) { + return dns_create(exec_ctx, args, "https"); +} + +static char *dns_factory_get_default_host_name(grpc_resolver_factory *factory, + grpc_uri *uri) { + const char *path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static const grpc_resolver_factory_vtable dns_factory_vtable = { + dns_factory_ref, dns_factory_unref, dns_factory_create_resolver, + dns_factory_get_default_host_name, "dns"}; +static grpc_resolver_factory dns_resolver_factory = {&dns_factory_vtable}; + +static grpc_resolver_factory *dns_resolver_factory_create() { + return &dns_resolver_factory; +} + +extern "C" void grpc_resolver_dns_native_init(void) { + char *resolver = gpr_getenv("GRPC_DNS_RESOLVER"); + if (resolver != NULL && gpr_stricmp(resolver, "native") == 0) { + gpr_log(GPR_DEBUG, "Using native dns resolver"); + grpc_register_resolver_type(dns_resolver_factory_create()); + } else { + grpc_resolver_factory *existing_factory = + grpc_resolver_factory_lookup("dns"); + if (existing_factory == NULL) { + gpr_log(GPR_DEBUG, "Using native dns resolver"); + grpc_register_resolver_type(dns_resolver_factory_create()); + } else { + grpc_resolver_factory_unref(existing_factory); + } + } + gpr_free(resolver); +} + +extern "C" void grpc_resolver_dns_native_shutdown(void) {} diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c deleted file mode 100644 index 69ea440ae6..0000000000 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c +++ /dev/null @@ -1,265 +0,0 @@ -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// This is similar to the sockaddr resolver, except that it supports a -// bunch of query args that are useful for dependency injection in tests. - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/lb_policy_factory.h" -#include "src/core/ext/filters/client_channel/parse_address.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" - -// -// fake_resolver -// - -typedef struct { - // base class -- must be first - grpc_resolver base; - - // passed-in parameters - grpc_channel_args* channel_args; - - // If not NULL, the next set of resolution results to be returned to - // grpc_resolver_next_locked()'s closure. - grpc_channel_args* next_results; - - // Results to use for the pretended re-resolution in - // fake_resolver_channel_saw_error_locked(). - grpc_channel_args* results_upon_error; - - // pending next completion, or NULL - grpc_closure* next_completion; - // target result address for next completion - grpc_channel_args** target_result; -} fake_resolver; - -static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) { - fake_resolver* r = (fake_resolver*)gr; - grpc_channel_args_destroy(exec_ctx, r->next_results); - grpc_channel_args_destroy(exec_ctx, r->results_upon_error); - grpc_channel_args_destroy(exec_ctx, r->channel_args); - gpr_free(r); -} - -static void fake_resolver_shutdown_locked(grpc_exec_ctx* exec_ctx, - grpc_resolver* resolver) { - fake_resolver* r = (fake_resolver*)resolver; - if (r->next_completion != NULL) { - *r->target_result = NULL; - GRPC_CLOSURE_SCHED( - exec_ctx, r->next_completion, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); - r->next_completion = NULL; - } -} - -static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx, - fake_resolver* r) { - if (r->next_completion != NULL && r->next_results != NULL) { - *r->target_result = - grpc_channel_args_union(r->next_results, r->channel_args); - grpc_channel_args_destroy(exec_ctx, r->next_results); - r->next_results = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); - r->next_completion = NULL; - } -} - -static void fake_resolver_channel_saw_error_locked(grpc_exec_ctx* exec_ctx, - grpc_resolver* resolver) { - fake_resolver* r = (fake_resolver*)resolver; - if (r->next_results == NULL && r->results_upon_error != NULL) { - // Pretend we re-resolved. - r->next_results = grpc_channel_args_copy(r->results_upon_error); - } - fake_resolver_maybe_finish_next_locked(exec_ctx, r); -} - -static void fake_resolver_next_locked(grpc_exec_ctx* exec_ctx, - grpc_resolver* resolver, - grpc_channel_args** target_result, - grpc_closure* on_complete) { - fake_resolver* r = (fake_resolver*)resolver; - GPR_ASSERT(!r->next_completion); - r->next_completion = on_complete; - r->target_result = target_result; - fake_resolver_maybe_finish_next_locked(exec_ctx, r); -} - -static const grpc_resolver_vtable fake_resolver_vtable = { - fake_resolver_destroy, fake_resolver_shutdown_locked, - fake_resolver_channel_saw_error_locked, fake_resolver_next_locked}; - -struct grpc_fake_resolver_response_generator { - fake_resolver* resolver; // Set by the fake_resolver constructor to itself. - gpr_refcount refcount; -}; - -grpc_fake_resolver_response_generator* -grpc_fake_resolver_response_generator_create() { - grpc_fake_resolver_response_generator* generator = - (grpc_fake_resolver_response_generator*)gpr_zalloc(sizeof(*generator)); - gpr_ref_init(&generator->refcount, 1); - return generator; -} - -grpc_fake_resolver_response_generator* -grpc_fake_resolver_response_generator_ref( - grpc_fake_resolver_response_generator* generator) { - gpr_ref(&generator->refcount); - return generator; -} - -void grpc_fake_resolver_response_generator_unref( - grpc_fake_resolver_response_generator* generator) { - if (gpr_unref(&generator->refcount)) { - gpr_free(generator); - } -} - -typedef struct set_response_closure_arg { - grpc_closure set_response_closure; - grpc_fake_resolver_response_generator* generator; - grpc_channel_args* next_response; -} set_response_closure_arg; - -static void set_response_closure_fn(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - set_response_closure_arg* closure_arg = (set_response_closure_arg*)arg; - grpc_fake_resolver_response_generator* generator = closure_arg->generator; - fake_resolver* r = generator->resolver; - if (r->next_results != NULL) { - grpc_channel_args_destroy(exec_ctx, r->next_results); - } - r->next_results = closure_arg->next_response; - if (r->results_upon_error != NULL) { - grpc_channel_args_destroy(exec_ctx, r->results_upon_error); - } - r->results_upon_error = grpc_channel_args_copy(closure_arg->next_response); - gpr_free(closure_arg); - fake_resolver_maybe_finish_next_locked(exec_ctx, r); -} - -void grpc_fake_resolver_response_generator_set_response( - grpc_exec_ctx* exec_ctx, grpc_fake_resolver_response_generator* generator, - grpc_channel_args* next_response) { - GPR_ASSERT(generator->resolver != NULL); - set_response_closure_arg* closure_arg = - (set_response_closure_arg*)gpr_zalloc(sizeof(*closure_arg)); - closure_arg->generator = generator; - closure_arg->next_response = grpc_channel_args_copy(next_response); - GRPC_CLOSURE_SCHED(exec_ctx, - GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, - set_response_closure_fn, closure_arg, - grpc_combiner_scheduler( - generator->resolver->base.combiner)), - GRPC_ERROR_NONE); -} - -static void* response_generator_arg_copy(void* p) { - return grpc_fake_resolver_response_generator_ref( - (grpc_fake_resolver_response_generator*)p); -} - -static void response_generator_arg_destroy(grpc_exec_ctx* exec_ctx, void* p) { - grpc_fake_resolver_response_generator_unref( - (grpc_fake_resolver_response_generator*)p); -} - -static int response_generator_cmp(void* a, void* b) { return GPR_ICMP(a, b); } - -static const grpc_arg_pointer_vtable response_generator_arg_vtable = { - response_generator_arg_copy, response_generator_arg_destroy, - response_generator_cmp}; - -grpc_arg grpc_fake_resolver_response_generator_arg( - grpc_fake_resolver_response_generator* generator) { - grpc_arg arg; - arg.type = GRPC_ARG_POINTER; - arg.key = (char*)GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR; - arg.value.pointer.p = generator; - arg.value.pointer.vtable = &response_generator_arg_vtable; - return arg; -} - -grpc_fake_resolver_response_generator* -grpc_fake_resolver_get_response_generator(const grpc_channel_args* args) { - const grpc_arg* arg = - grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR); - if (arg == NULL || arg->type != GRPC_ARG_POINTER) return NULL; - return (grpc_fake_resolver_response_generator*)arg->value.pointer.p; -} - -// -// fake_resolver_factory -// - -static void fake_resolver_factory_ref(grpc_resolver_factory* factory) {} - -static void fake_resolver_factory_unref(grpc_resolver_factory* factory) {} - -static grpc_resolver* fake_resolver_create(grpc_exec_ctx* exec_ctx, - grpc_resolver_factory* factory, - grpc_resolver_args* args) { - fake_resolver* r = (fake_resolver*)gpr_zalloc(sizeof(*r)); - r->channel_args = grpc_channel_args_copy(args->args); - grpc_resolver_init(&r->base, &fake_resolver_vtable, args->combiner); - grpc_fake_resolver_response_generator* response_generator = - grpc_fake_resolver_get_response_generator(args->args); - if (response_generator != NULL) response_generator->resolver = r; - return &r->base; -} - -static char* fake_resolver_get_default_authority(grpc_resolver_factory* factory, - grpc_uri* uri) { - const char* path = uri->path; - if (path[0] == '/') ++path; - return gpr_strdup(path); -} - -static const grpc_resolver_factory_vtable fake_resolver_factory_vtable = { - fake_resolver_factory_ref, fake_resolver_factory_unref, - fake_resolver_create, fake_resolver_get_default_authority, "fake"}; - -static grpc_resolver_factory fake_resolver_factory = { - &fake_resolver_factory_vtable}; - -void grpc_resolver_fake_init(void) { - grpc_register_resolver_type(&fake_resolver_factory); -} - -void grpc_resolver_fake_shutdown(void) {} diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc new file mode 100644 index 0000000000..ed5b1011fb --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc @@ -0,0 +1,265 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// This is similar to the sockaddr resolver, except that it supports a +// bunch of query args that are useful for dependency injection in tests. + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +#include "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" + +// +// fake_resolver +// + +typedef struct { + // base class -- must be first + grpc_resolver base; + + // passed-in parameters + grpc_channel_args* channel_args; + + // If not NULL, the next set of resolution results to be returned to + // grpc_resolver_next_locked()'s closure. + grpc_channel_args* next_results; + + // Results to use for the pretended re-resolution in + // fake_resolver_channel_saw_error_locked(). + grpc_channel_args* results_upon_error; + + // pending next completion, or NULL + grpc_closure* next_completion; + // target result address for next completion + grpc_channel_args** target_result; +} fake_resolver; + +static void fake_resolver_destroy(grpc_exec_ctx* exec_ctx, grpc_resolver* gr) { + fake_resolver* r = (fake_resolver*)gr; + grpc_channel_args_destroy(exec_ctx, r->next_results); + grpc_channel_args_destroy(exec_ctx, r->results_upon_error); + grpc_channel_args_destroy(exec_ctx, r->channel_args); + gpr_free(r); +} + +static void fake_resolver_shutdown_locked(grpc_exec_ctx* exec_ctx, + grpc_resolver* resolver) { + fake_resolver* r = (fake_resolver*)resolver; + if (r->next_completion != NULL) { + *r->target_result = NULL; + GRPC_CLOSURE_SCHED( + exec_ctx, r->next_completion, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); + r->next_completion = NULL; + } +} + +static void fake_resolver_maybe_finish_next_locked(grpc_exec_ctx* exec_ctx, + fake_resolver* r) { + if (r->next_completion != NULL && r->next_results != NULL) { + *r->target_result = + grpc_channel_args_union(r->next_results, r->channel_args); + grpc_channel_args_destroy(exec_ctx, r->next_results); + r->next_results = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); + r->next_completion = NULL; + } +} + +static void fake_resolver_channel_saw_error_locked(grpc_exec_ctx* exec_ctx, + grpc_resolver* resolver) { + fake_resolver* r = (fake_resolver*)resolver; + if (r->next_results == NULL && r->results_upon_error != NULL) { + // Pretend we re-resolved. + r->next_results = grpc_channel_args_copy(r->results_upon_error); + } + fake_resolver_maybe_finish_next_locked(exec_ctx, r); +} + +static void fake_resolver_next_locked(grpc_exec_ctx* exec_ctx, + grpc_resolver* resolver, + grpc_channel_args** target_result, + grpc_closure* on_complete) { + fake_resolver* r = (fake_resolver*)resolver; + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_result = target_result; + fake_resolver_maybe_finish_next_locked(exec_ctx, r); +} + +static const grpc_resolver_vtable fake_resolver_vtable = { + fake_resolver_destroy, fake_resolver_shutdown_locked, + fake_resolver_channel_saw_error_locked, fake_resolver_next_locked}; + +struct grpc_fake_resolver_response_generator { + fake_resolver* resolver; // Set by the fake_resolver constructor to itself. + gpr_refcount refcount; +}; + +grpc_fake_resolver_response_generator* +grpc_fake_resolver_response_generator_create() { + grpc_fake_resolver_response_generator* generator = + (grpc_fake_resolver_response_generator*)gpr_zalloc(sizeof(*generator)); + gpr_ref_init(&generator->refcount, 1); + return generator; +} + +grpc_fake_resolver_response_generator* +grpc_fake_resolver_response_generator_ref( + grpc_fake_resolver_response_generator* generator) { + gpr_ref(&generator->refcount); + return generator; +} + +void grpc_fake_resolver_response_generator_unref( + grpc_fake_resolver_response_generator* generator) { + if (gpr_unref(&generator->refcount)) { + gpr_free(generator); + } +} + +typedef struct set_response_closure_arg { + grpc_closure set_response_closure; + grpc_fake_resolver_response_generator* generator; + grpc_channel_args* next_response; +} set_response_closure_arg; + +static void set_response_closure_fn(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + set_response_closure_arg* closure_arg = (set_response_closure_arg*)arg; + grpc_fake_resolver_response_generator* generator = closure_arg->generator; + fake_resolver* r = generator->resolver; + if (r->next_results != NULL) { + grpc_channel_args_destroy(exec_ctx, r->next_results); + } + r->next_results = closure_arg->next_response; + if (r->results_upon_error != NULL) { + grpc_channel_args_destroy(exec_ctx, r->results_upon_error); + } + r->results_upon_error = grpc_channel_args_copy(closure_arg->next_response); + gpr_free(closure_arg); + fake_resolver_maybe_finish_next_locked(exec_ctx, r); +} + +void grpc_fake_resolver_response_generator_set_response( + grpc_exec_ctx* exec_ctx, grpc_fake_resolver_response_generator* generator, + grpc_channel_args* next_response) { + GPR_ASSERT(generator->resolver != NULL); + set_response_closure_arg* closure_arg = + (set_response_closure_arg*)gpr_zalloc(sizeof(*closure_arg)); + closure_arg->generator = generator; + closure_arg->next_response = grpc_channel_args_copy(next_response); + GRPC_CLOSURE_SCHED(exec_ctx, + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, + set_response_closure_fn, closure_arg, + grpc_combiner_scheduler( + generator->resolver->base.combiner)), + GRPC_ERROR_NONE); +} + +static void* response_generator_arg_copy(void* p) { + return grpc_fake_resolver_response_generator_ref( + (grpc_fake_resolver_response_generator*)p); +} + +static void response_generator_arg_destroy(grpc_exec_ctx* exec_ctx, void* p) { + grpc_fake_resolver_response_generator_unref( + (grpc_fake_resolver_response_generator*)p); +} + +static int response_generator_cmp(void* a, void* b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable response_generator_arg_vtable = { + response_generator_arg_copy, response_generator_arg_destroy, + response_generator_cmp}; + +grpc_arg grpc_fake_resolver_response_generator_arg( + grpc_fake_resolver_response_generator* generator) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = (char*)GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR; + arg.value.pointer.p = generator; + arg.value.pointer.vtable = &response_generator_arg_vtable; + return arg; +} + +grpc_fake_resolver_response_generator* +grpc_fake_resolver_get_response_generator(const grpc_channel_args* args) { + const grpc_arg* arg = + grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR); + if (arg == NULL || arg->type != GRPC_ARG_POINTER) return NULL; + return (grpc_fake_resolver_response_generator*)arg->value.pointer.p; +} + +// +// fake_resolver_factory +// + +static void fake_resolver_factory_ref(grpc_resolver_factory* factory) {} + +static void fake_resolver_factory_unref(grpc_resolver_factory* factory) {} + +static grpc_resolver* fake_resolver_create(grpc_exec_ctx* exec_ctx, + grpc_resolver_factory* factory, + grpc_resolver_args* args) { + fake_resolver* r = (fake_resolver*)gpr_zalloc(sizeof(*r)); + r->channel_args = grpc_channel_args_copy(args->args); + grpc_resolver_init(&r->base, &fake_resolver_vtable, args->combiner); + grpc_fake_resolver_response_generator* response_generator = + grpc_fake_resolver_get_response_generator(args->args); + if (response_generator != NULL) response_generator->resolver = r; + return &r->base; +} + +static char* fake_resolver_get_default_authority(grpc_resolver_factory* factory, + grpc_uri* uri) { + const char* path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static const grpc_resolver_factory_vtable fake_resolver_factory_vtable = { + fake_resolver_factory_ref, fake_resolver_factory_unref, + fake_resolver_create, fake_resolver_get_default_authority, "fake"}; + +static grpc_resolver_factory fake_resolver_factory = { + &fake_resolver_factory_vtable}; + +extern "C" void grpc_resolver_fake_init(void) { + grpc_register_resolver_type(&fake_resolver_factory); +} + +extern "C" void grpc_resolver_fake_shutdown(void) {} diff --git a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c deleted file mode 100644 index 7ceb8f40a1..0000000000 --- a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * - * Copyright 2015-2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/ext/filters/client_channel/lb_policy_factory.h" -#include "src/core/ext/filters/client_channel/parse_address.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -typedef struct { - /** base class: must be first */ - grpc_resolver base; - /** the addresses that we've 'resolved' */ - grpc_lb_addresses *addresses; - /** channel args */ - grpc_channel_args *channel_args; - /** have we published? */ - bool published; - /** pending next completion, or NULL */ - grpc_closure *next_completion; - /** target result address for next completion */ - grpc_channel_args **target_result; -} sockaddr_resolver; - -static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); - -static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - sockaddr_resolver *r); - -static void sockaddr_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); -static void sockaddr_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *r); -static void sockaddr_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, - grpc_channel_args **target_result, - grpc_closure *on_complete); - -static const grpc_resolver_vtable sockaddr_resolver_vtable = { - sockaddr_destroy, sockaddr_shutdown_locked, - sockaddr_channel_saw_error_locked, sockaddr_next_locked}; - -static void sockaddr_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - sockaddr_resolver *r = (sockaddr_resolver *)resolver; - if (r->next_completion != NULL) { - *r->target_result = NULL; - GRPC_CLOSURE_SCHED( - exec_ctx, r->next_completion, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); - r->next_completion = NULL; - } -} - -static void sockaddr_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver) { - sockaddr_resolver *r = (sockaddr_resolver *)resolver; - r->published = false; - sockaddr_maybe_finish_next_locked(exec_ctx, r); -} - -static void sockaddr_next_locked(grpc_exec_ctx *exec_ctx, - grpc_resolver *resolver, - grpc_channel_args **target_result, - grpc_closure *on_complete) { - sockaddr_resolver *r = (sockaddr_resolver *)resolver; - GPR_ASSERT(!r->next_completion); - r->next_completion = on_complete; - r->target_result = target_result; - sockaddr_maybe_finish_next_locked(exec_ctx, r); -} - -static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, - sockaddr_resolver *r) { - if (r->next_completion != NULL && !r->published) { - r->published = true; - grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses); - *r->target_result = - grpc_channel_args_copy_and_add(r->channel_args, &arg, 1); - GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); - r->next_completion = NULL; - } -} - -static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { - sockaddr_resolver *r = (sockaddr_resolver *)gr; - grpc_lb_addresses_destroy(exec_ctx, r->addresses); - grpc_channel_args_destroy(exec_ctx, r->channel_args); - gpr_free(r); -} - -static char *ip_get_default_authority(grpc_uri *uri) { - const char *path = uri->path; - if (path[0] == '/') ++path; - return gpr_strdup(path); -} - -static char *ipv4_get_default_authority(grpc_resolver_factory *factory, - grpc_uri *uri) { - return ip_get_default_authority(uri); -} - -static char *ipv6_get_default_authority(grpc_resolver_factory *factory, - grpc_uri *uri) { - return ip_get_default_authority(uri); -} - -#ifdef GRPC_HAVE_UNIX_SOCKET -char *unix_get_default_authority(grpc_resolver_factory *factory, - grpc_uri *uri) { - return gpr_strdup("localhost"); -} -#endif - -static void do_nothing(void *ignored) {} - -static grpc_resolver *sockaddr_create(grpc_exec_ctx *exec_ctx, - grpc_resolver_args *args, - bool parse(const grpc_uri *uri, - grpc_resolved_address *dst)) { - if (0 != strcmp(args->uri->authority, "")) { - gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme", - args->uri->scheme); - return NULL; - } - /* Construct addresses. */ - grpc_slice path_slice = - grpc_slice_new(args->uri->path, strlen(args->uri->path), do_nothing); - grpc_slice_buffer path_parts; - grpc_slice_buffer_init(&path_parts); - grpc_slice_split(path_slice, ",", &path_parts); - grpc_lb_addresses *addresses = - grpc_lb_addresses_create(path_parts.count, NULL /* user_data_vtable */); - bool errors_found = false; - for (size_t i = 0; i < addresses->num_addresses; i++) { - grpc_uri ith_uri = *args->uri; - char *part_str = grpc_slice_to_c_string(path_parts.slices[i]); - ith_uri.path = part_str; - if (!parse(&ith_uri, &addresses->addresses[i].address)) { - errors_found = true; /* GPR_TRUE */ - } - gpr_free(part_str); - if (errors_found) break; - } - grpc_slice_buffer_destroy_internal(exec_ctx, &path_parts); - grpc_slice_unref_internal(exec_ctx, path_slice); - if (errors_found) { - grpc_lb_addresses_destroy(exec_ctx, addresses); - return NULL; - } - /* Instantiate resolver. */ - sockaddr_resolver *r = - (sockaddr_resolver *)gpr_zalloc(sizeof(sockaddr_resolver)); - r->addresses = addresses; - r->channel_args = grpc_channel_args_copy(args->args); - grpc_resolver_init(&r->base, &sockaddr_resolver_vtable, args->combiner); - return &r->base; -} - -/* - * FACTORY - */ - -static void sockaddr_factory_ref(grpc_resolver_factory *factory) {} - -static void sockaddr_factory_unref(grpc_resolver_factory *factory) {} - -#define DECL_FACTORY(name) \ - static grpc_resolver *name##_factory_create_resolver( \ - grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, \ - grpc_resolver_args *args) { \ - return sockaddr_create(exec_ctx, args, grpc_parse_##name); \ - } \ - static const grpc_resolver_factory_vtable name##_factory_vtable = { \ - sockaddr_factory_ref, sockaddr_factory_unref, \ - name##_factory_create_resolver, name##_get_default_authority, #name}; \ - static grpc_resolver_factory name##_resolver_factory = { \ - &name##_factory_vtable} - -#ifdef GRPC_HAVE_UNIX_SOCKET -DECL_FACTORY(unix); -#endif -DECL_FACTORY(ipv4); -DECL_FACTORY(ipv6); - -void grpc_resolver_sockaddr_init(void) { - grpc_register_resolver_type(&ipv4_resolver_factory); - grpc_register_resolver_type(&ipv6_resolver_factory); -#ifdef GRPC_HAVE_UNIX_SOCKET - grpc_register_resolver_type(&unix_resolver_factory); -#endif -} - -void grpc_resolver_sockaddr_shutdown(void) {} diff --git a/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc new file mode 100644 index 0000000000..dda9542325 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc @@ -0,0 +1,222 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/client_channel/lb_policy_factory.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +typedef struct { + /** base class: must be first */ + grpc_resolver base; + /** the addresses that we've 'resolved' */ + grpc_lb_addresses *addresses; + /** channel args */ + grpc_channel_args *channel_args; + /** have we published? */ + bool published; + /** pending next completion, or NULL */ + grpc_closure *next_completion; + /** target result address for next completion */ + grpc_channel_args **target_result; +} sockaddr_resolver; + +static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *r); + +static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + sockaddr_resolver *r); + +static void sockaddr_shutdown_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r); +static void sockaddr_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *r); +static void sockaddr_next_locked(grpc_exec_ctx *exec_ctx, grpc_resolver *r, + grpc_channel_args **target_result, + grpc_closure *on_complete); + +static const grpc_resolver_vtable sockaddr_resolver_vtable = { + sockaddr_destroy, sockaddr_shutdown_locked, + sockaddr_channel_saw_error_locked, sockaddr_next_locked}; + +static void sockaddr_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + sockaddr_resolver *r = (sockaddr_resolver *)resolver; + if (r->next_completion != NULL) { + *r->target_result = NULL; + GRPC_CLOSURE_SCHED( + exec_ctx, r->next_completion, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Resolver Shutdown")); + r->next_completion = NULL; + } +} + +static void sockaddr_channel_saw_error_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver) { + sockaddr_resolver *r = (sockaddr_resolver *)resolver; + r->published = false; + sockaddr_maybe_finish_next_locked(exec_ctx, r); +} + +static void sockaddr_next_locked(grpc_exec_ctx *exec_ctx, + grpc_resolver *resolver, + grpc_channel_args **target_result, + grpc_closure *on_complete) { + sockaddr_resolver *r = (sockaddr_resolver *)resolver; + GPR_ASSERT(!r->next_completion); + r->next_completion = on_complete; + r->target_result = target_result; + sockaddr_maybe_finish_next_locked(exec_ctx, r); +} + +static void sockaddr_maybe_finish_next_locked(grpc_exec_ctx *exec_ctx, + sockaddr_resolver *r) { + if (r->next_completion != NULL && !r->published) { + r->published = true; + grpc_arg arg = grpc_lb_addresses_create_channel_arg(r->addresses); + *r->target_result = + grpc_channel_args_copy_and_add(r->channel_args, &arg, 1); + GRPC_CLOSURE_SCHED(exec_ctx, r->next_completion, GRPC_ERROR_NONE); + r->next_completion = NULL; + } +} + +static void sockaddr_destroy(grpc_exec_ctx *exec_ctx, grpc_resolver *gr) { + sockaddr_resolver *r = (sockaddr_resolver *)gr; + grpc_lb_addresses_destroy(exec_ctx, r->addresses); + grpc_channel_args_destroy(exec_ctx, r->channel_args); + gpr_free(r); +} + +static char *ip_get_default_authority(grpc_uri *uri) { + const char *path = uri->path; + if (path[0] == '/') ++path; + return gpr_strdup(path); +} + +static char *ipv4_get_default_authority(grpc_resolver_factory *factory, + grpc_uri *uri) { + return ip_get_default_authority(uri); +} + +static char *ipv6_get_default_authority(grpc_resolver_factory *factory, + grpc_uri *uri) { + return ip_get_default_authority(uri); +} + +#ifdef GRPC_HAVE_UNIX_SOCKET +char *unix_get_default_authority(grpc_resolver_factory *factory, + grpc_uri *uri) { + return gpr_strdup("localhost"); +} +#endif + +static void do_nothing(void *ignored) {} + +static grpc_resolver *sockaddr_create(grpc_exec_ctx *exec_ctx, + grpc_resolver_args *args, + bool parse(const grpc_uri *uri, + grpc_resolved_address *dst)) { + if (0 != strcmp(args->uri->authority, "")) { + gpr_log(GPR_ERROR, "authority based uri's not supported by the %s scheme", + args->uri->scheme); + return NULL; + } + /* Construct addresses. */ + grpc_slice path_slice = + grpc_slice_new(args->uri->path, strlen(args->uri->path), do_nothing); + grpc_slice_buffer path_parts; + grpc_slice_buffer_init(&path_parts); + grpc_slice_split(path_slice, ",", &path_parts); + grpc_lb_addresses *addresses = + grpc_lb_addresses_create(path_parts.count, NULL /* user_data_vtable */); + bool errors_found = false; + for (size_t i = 0; i < addresses->num_addresses; i++) { + grpc_uri ith_uri = *args->uri; + char *part_str = grpc_slice_to_c_string(path_parts.slices[i]); + ith_uri.path = part_str; + if (!parse(&ith_uri, &addresses->addresses[i].address)) { + errors_found = true; /* GPR_TRUE */ + } + gpr_free(part_str); + if (errors_found) break; + } + grpc_slice_buffer_destroy_internal(exec_ctx, &path_parts); + grpc_slice_unref_internal(exec_ctx, path_slice); + if (errors_found) { + grpc_lb_addresses_destroy(exec_ctx, addresses); + return NULL; + } + /* Instantiate resolver. */ + sockaddr_resolver *r = + (sockaddr_resolver *)gpr_zalloc(sizeof(sockaddr_resolver)); + r->addresses = addresses; + r->channel_args = grpc_channel_args_copy(args->args); + grpc_resolver_init(&r->base, &sockaddr_resolver_vtable, args->combiner); + return &r->base; +} + +/* + * FACTORY + */ + +static void sockaddr_factory_ref(grpc_resolver_factory *factory) {} + +static void sockaddr_factory_unref(grpc_resolver_factory *factory) {} + +#define DECL_FACTORY(name) \ + static grpc_resolver *name##_factory_create_resolver( \ + grpc_exec_ctx *exec_ctx, grpc_resolver_factory *factory, \ + grpc_resolver_args *args) { \ + return sockaddr_create(exec_ctx, args, grpc_parse_##name); \ + } \ + static const grpc_resolver_factory_vtable name##_factory_vtable = { \ + sockaddr_factory_ref, sockaddr_factory_unref, \ + name##_factory_create_resolver, name##_get_default_authority, #name}; \ + static grpc_resolver_factory name##_resolver_factory = { \ + &name##_factory_vtable} + +#ifdef GRPC_HAVE_UNIX_SOCKET +DECL_FACTORY(unix); +#endif +DECL_FACTORY(ipv4); +DECL_FACTORY(ipv6); + +extern "C" void grpc_resolver_sockaddr_init(void) { + grpc_register_resolver_type(&ipv4_resolver_factory); + grpc_register_resolver_type(&ipv6_resolver_factory); +#ifdef GRPC_HAVE_UNIX_SOCKET + grpc_register_resolver_type(&unix_resolver_factory); +#endif +} + +extern "C" void grpc_resolver_sockaddr_shutdown(void) {} diff --git a/src/core/ext/filters/client_channel/resolver_factory.c b/src/core/ext/filters/client_channel/resolver_factory.c deleted file mode 100644 index 6f0a7c1e36..0000000000 --- a/src/core/ext/filters/client_channel/resolver_factory.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/resolver_factory.h" - -void grpc_resolver_factory_ref(grpc_resolver_factory* factory) { - factory->vtable->ref(factory); -} - -void grpc_resolver_factory_unref(grpc_resolver_factory* factory) { - factory->vtable->unref(factory); -} - -/** Create a resolver instance for a name */ -grpc_resolver* grpc_resolver_factory_create_resolver( - grpc_exec_ctx* exec_ctx, grpc_resolver_factory* factory, - grpc_resolver_args* args) { - if (factory == NULL) return NULL; - return factory->vtable->create_resolver(exec_ctx, factory, args); -} - -char* grpc_resolver_factory_get_default_authority( - grpc_resolver_factory* factory, grpc_uri* uri) { - if (factory == NULL) return NULL; - return factory->vtable->get_default_authority(factory, uri); -} diff --git a/src/core/ext/filters/client_channel/resolver_factory.cc b/src/core/ext/filters/client_channel/resolver_factory.cc new file mode 100644 index 0000000000..6f0a7c1e36 --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver_factory.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/resolver_factory.h" + +void grpc_resolver_factory_ref(grpc_resolver_factory* factory) { + factory->vtable->ref(factory); +} + +void grpc_resolver_factory_unref(grpc_resolver_factory* factory) { + factory->vtable->unref(factory); +} + +/** Create a resolver instance for a name */ +grpc_resolver* grpc_resolver_factory_create_resolver( + grpc_exec_ctx* exec_ctx, grpc_resolver_factory* factory, + grpc_resolver_args* args) { + if (factory == NULL) return NULL; + return factory->vtable->create_resolver(exec_ctx, factory, args); +} + +char* grpc_resolver_factory_get_default_authority( + grpc_resolver_factory* factory, grpc_uri* uri) { + if (factory == NULL) return NULL; + return factory->vtable->get_default_authority(factory, uri); +} diff --git a/src/core/ext/filters/client_channel/resolver_registry.c b/src/core/ext/filters/client_channel/resolver_registry.c deleted file mode 100644 index 1a0fb0bc3c..0000000000 --- a/src/core/ext/filters/client_channel/resolver_registry.c +++ /dev/null @@ -1,159 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/resolver_registry.h" - -#include - -#include -#include -#include - -#define MAX_RESOLVERS 10 -#define DEFAULT_RESOLVER_PREFIX_MAX_LENGTH 32 - -static grpc_resolver_factory *g_all_of_the_resolvers[MAX_RESOLVERS]; -static int g_number_of_resolvers = 0; - -static char g_default_resolver_prefix[DEFAULT_RESOLVER_PREFIX_MAX_LENGTH] = - "dns:///"; - -void grpc_resolver_registry_init() {} - -void grpc_resolver_registry_shutdown(void) { - for (int i = 0; i < g_number_of_resolvers; i++) { - grpc_resolver_factory_unref(g_all_of_the_resolvers[i]); - } - // FIXME(ctiller): this should live in grpc_resolver_registry_init, - // however that would have the client_channel plugin call this AFTER we start - // registering resolvers from third party plugins, and so they'd never show - // up. - // We likely need some kind of dependency system for plugins.... what form - // that takes is TBD. - g_number_of_resolvers = 0; -} - -void grpc_resolver_registry_set_default_prefix( - const char *default_resolver_prefix) { - const size_t len = strlen(default_resolver_prefix); - GPR_ASSERT(len < DEFAULT_RESOLVER_PREFIX_MAX_LENGTH && - "default resolver prefix too long"); - GPR_ASSERT(len > 0 && "default resolver prefix can't be empty"); - // By the previous assert, default_resolver_prefix is safe to be copied with a - // plain strcpy. - strcpy(g_default_resolver_prefix, default_resolver_prefix); -} - -void grpc_register_resolver_type(grpc_resolver_factory *factory) { - int i; - for (i = 0; i < g_number_of_resolvers; i++) { - GPR_ASSERT(0 != strcmp(factory->vtable->scheme, - g_all_of_the_resolvers[i]->vtable->scheme)); - } - GPR_ASSERT(g_number_of_resolvers != MAX_RESOLVERS); - grpc_resolver_factory_ref(factory); - g_all_of_the_resolvers[g_number_of_resolvers++] = factory; -} - -static grpc_resolver_factory *lookup_factory(const char *name) { - int i; - - for (i = 0; i < g_number_of_resolvers; i++) { - if (0 == strcmp(name, g_all_of_the_resolvers[i]->vtable->scheme)) { - return g_all_of_the_resolvers[i]; - } - } - return NULL; -} - -grpc_resolver_factory *grpc_resolver_factory_lookup(const char *name) { - grpc_resolver_factory *f = lookup_factory(name); - if (f) grpc_resolver_factory_ref(f); - return f; -} - -static grpc_resolver_factory *lookup_factory_by_uri(grpc_uri *uri) { - if (!uri) return NULL; - return lookup_factory(uri->scheme); -} - -static grpc_resolver_factory *resolve_factory(grpc_exec_ctx *exec_ctx, - const char *target, - grpc_uri **uri, - char **canonical_target) { - grpc_resolver_factory *factory = NULL; - - GPR_ASSERT(uri != NULL); - *uri = grpc_uri_parse(exec_ctx, target, 1); - factory = lookup_factory_by_uri(*uri); - if (factory == NULL) { - grpc_uri_destroy(*uri); - gpr_asprintf(canonical_target, "%s%s", g_default_resolver_prefix, target); - *uri = grpc_uri_parse(exec_ctx, *canonical_target, 1); - factory = lookup_factory_by_uri(*uri); - if (factory == NULL) { - grpc_uri_destroy(grpc_uri_parse(exec_ctx, target, 0)); - grpc_uri_destroy(grpc_uri_parse(exec_ctx, *canonical_target, 0)); - gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target, - *canonical_target); - } - } - return factory; -} - -grpc_resolver *grpc_resolver_create(grpc_exec_ctx *exec_ctx, const char *target, - const grpc_channel_args *args, - grpc_pollset_set *pollset_set, - grpc_combiner *combiner) { - grpc_uri *uri = NULL; - char *canonical_target = NULL; - grpc_resolver_factory *factory = - resolve_factory(exec_ctx, target, &uri, &canonical_target); - grpc_resolver *resolver; - grpc_resolver_args resolver_args; - memset(&resolver_args, 0, sizeof(resolver_args)); - resolver_args.uri = uri; - resolver_args.args = args; - resolver_args.pollset_set = pollset_set; - resolver_args.combiner = combiner; - resolver = - grpc_resolver_factory_create_resolver(exec_ctx, factory, &resolver_args); - grpc_uri_destroy(uri); - gpr_free(canonical_target); - return resolver; -} - -char *grpc_get_default_authority(grpc_exec_ctx *exec_ctx, const char *target) { - grpc_uri *uri = NULL; - char *canonical_target = NULL; - grpc_resolver_factory *factory = - resolve_factory(exec_ctx, target, &uri, &canonical_target); - char *authority = grpc_resolver_factory_get_default_authority(factory, uri); - grpc_uri_destroy(uri); - gpr_free(canonical_target); - return authority; -} - -char *grpc_resolver_factory_add_default_prefix_if_needed( - grpc_exec_ctx *exec_ctx, const char *target) { - grpc_uri *uri = NULL; - char *canonical_target = NULL; - resolve_factory(exec_ctx, target, &uri, &canonical_target); - grpc_uri_destroy(uri); - return canonical_target == NULL ? gpr_strdup(target) : canonical_target; -} diff --git a/src/core/ext/filters/client_channel/resolver_registry.cc b/src/core/ext/filters/client_channel/resolver_registry.cc new file mode 100644 index 0000000000..1a0fb0bc3c --- /dev/null +++ b/src/core/ext/filters/client_channel/resolver_registry.cc @@ -0,0 +1,159 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/resolver_registry.h" + +#include + +#include +#include +#include + +#define MAX_RESOLVERS 10 +#define DEFAULT_RESOLVER_PREFIX_MAX_LENGTH 32 + +static grpc_resolver_factory *g_all_of_the_resolvers[MAX_RESOLVERS]; +static int g_number_of_resolvers = 0; + +static char g_default_resolver_prefix[DEFAULT_RESOLVER_PREFIX_MAX_LENGTH] = + "dns:///"; + +void grpc_resolver_registry_init() {} + +void grpc_resolver_registry_shutdown(void) { + for (int i = 0; i < g_number_of_resolvers; i++) { + grpc_resolver_factory_unref(g_all_of_the_resolvers[i]); + } + // FIXME(ctiller): this should live in grpc_resolver_registry_init, + // however that would have the client_channel plugin call this AFTER we start + // registering resolvers from third party plugins, and so they'd never show + // up. + // We likely need some kind of dependency system for plugins.... what form + // that takes is TBD. + g_number_of_resolvers = 0; +} + +void grpc_resolver_registry_set_default_prefix( + const char *default_resolver_prefix) { + const size_t len = strlen(default_resolver_prefix); + GPR_ASSERT(len < DEFAULT_RESOLVER_PREFIX_MAX_LENGTH && + "default resolver prefix too long"); + GPR_ASSERT(len > 0 && "default resolver prefix can't be empty"); + // By the previous assert, default_resolver_prefix is safe to be copied with a + // plain strcpy. + strcpy(g_default_resolver_prefix, default_resolver_prefix); +} + +void grpc_register_resolver_type(grpc_resolver_factory *factory) { + int i; + for (i = 0; i < g_number_of_resolvers; i++) { + GPR_ASSERT(0 != strcmp(factory->vtable->scheme, + g_all_of_the_resolvers[i]->vtable->scheme)); + } + GPR_ASSERT(g_number_of_resolvers != MAX_RESOLVERS); + grpc_resolver_factory_ref(factory); + g_all_of_the_resolvers[g_number_of_resolvers++] = factory; +} + +static grpc_resolver_factory *lookup_factory(const char *name) { + int i; + + for (i = 0; i < g_number_of_resolvers; i++) { + if (0 == strcmp(name, g_all_of_the_resolvers[i]->vtable->scheme)) { + return g_all_of_the_resolvers[i]; + } + } + return NULL; +} + +grpc_resolver_factory *grpc_resolver_factory_lookup(const char *name) { + grpc_resolver_factory *f = lookup_factory(name); + if (f) grpc_resolver_factory_ref(f); + return f; +} + +static grpc_resolver_factory *lookup_factory_by_uri(grpc_uri *uri) { + if (!uri) return NULL; + return lookup_factory(uri->scheme); +} + +static grpc_resolver_factory *resolve_factory(grpc_exec_ctx *exec_ctx, + const char *target, + grpc_uri **uri, + char **canonical_target) { + grpc_resolver_factory *factory = NULL; + + GPR_ASSERT(uri != NULL); + *uri = grpc_uri_parse(exec_ctx, target, 1); + factory = lookup_factory_by_uri(*uri); + if (factory == NULL) { + grpc_uri_destroy(*uri); + gpr_asprintf(canonical_target, "%s%s", g_default_resolver_prefix, target); + *uri = grpc_uri_parse(exec_ctx, *canonical_target, 1); + factory = lookup_factory_by_uri(*uri); + if (factory == NULL) { + grpc_uri_destroy(grpc_uri_parse(exec_ctx, target, 0)); + grpc_uri_destroy(grpc_uri_parse(exec_ctx, *canonical_target, 0)); + gpr_log(GPR_ERROR, "don't know how to resolve '%s' or '%s'", target, + *canonical_target); + } + } + return factory; +} + +grpc_resolver *grpc_resolver_create(grpc_exec_ctx *exec_ctx, const char *target, + const grpc_channel_args *args, + grpc_pollset_set *pollset_set, + grpc_combiner *combiner) { + grpc_uri *uri = NULL; + char *canonical_target = NULL; + grpc_resolver_factory *factory = + resolve_factory(exec_ctx, target, &uri, &canonical_target); + grpc_resolver *resolver; + grpc_resolver_args resolver_args; + memset(&resolver_args, 0, sizeof(resolver_args)); + resolver_args.uri = uri; + resolver_args.args = args; + resolver_args.pollset_set = pollset_set; + resolver_args.combiner = combiner; + resolver = + grpc_resolver_factory_create_resolver(exec_ctx, factory, &resolver_args); + grpc_uri_destroy(uri); + gpr_free(canonical_target); + return resolver; +} + +char *grpc_get_default_authority(grpc_exec_ctx *exec_ctx, const char *target) { + grpc_uri *uri = NULL; + char *canonical_target = NULL; + grpc_resolver_factory *factory = + resolve_factory(exec_ctx, target, &uri, &canonical_target); + char *authority = grpc_resolver_factory_get_default_authority(factory, uri); + grpc_uri_destroy(uri); + gpr_free(canonical_target); + return authority; +} + +char *grpc_resolver_factory_add_default_prefix_if_needed( + grpc_exec_ctx *exec_ctx, const char *target) { + grpc_uri *uri = NULL; + char *canonical_target = NULL; + resolve_factory(exec_ctx, target, &uri, &canonical_target); + grpc_uri_destroy(uri); + return canonical_target == NULL ? gpr_strdup(target) : canonical_target; +} diff --git a/src/core/ext/filters/client_channel/retry_throttle.c b/src/core/ext/filters/client_channel/retry_throttle.c deleted file mode 100644 index 09dcade089..0000000000 --- a/src/core/ext/filters/client_channel/retry_throttle.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/retry_throttle.h" - -#include -#include - -#include -#include -#include -#include -#include - -// -// server_retry_throttle_data -// - -struct grpc_server_retry_throttle_data { - gpr_refcount refs; - int max_milli_tokens; - int milli_token_ratio; - gpr_atm milli_tokens; - // A pointer to the replacement for this grpc_server_retry_throttle_data - // entry. If non-NULL, then this entry is stale and must not be used. - // We hold a reference to the replacement. - gpr_atm replacement; -}; - -static void get_replacement_throttle_data_if_needed( - grpc_server_retry_throttle_data** throttle_data) { - while (true) { - grpc_server_retry_throttle_data* new_throttle_data = - (grpc_server_retry_throttle_data*)gpr_atm_acq_load( - &(*throttle_data)->replacement); - if (new_throttle_data == NULL) return; - *throttle_data = new_throttle_data; - } -} - -bool grpc_server_retry_throttle_data_record_failure( - grpc_server_retry_throttle_data* throttle_data) { - // First, check if we are stale and need to be replaced. - get_replacement_throttle_data_if_needed(&throttle_data); - // We decrement milli_tokens by 1000 (1 token) for each failure. - const int new_value = (int)gpr_atm_no_barrier_clamped_add( - &throttle_data->milli_tokens, (gpr_atm)-1000, (gpr_atm)0, - (gpr_atm)throttle_data->max_milli_tokens); - // Retries are allowed as long as the new value is above the threshold - // (max_milli_tokens / 2). - return new_value > throttle_data->max_milli_tokens / 2; -} - -void grpc_server_retry_throttle_data_record_success( - grpc_server_retry_throttle_data* throttle_data) { - // First, check if we are stale and need to be replaced. - get_replacement_throttle_data_if_needed(&throttle_data); - // We increment milli_tokens by milli_token_ratio for each success. - gpr_atm_no_barrier_clamped_add( - &throttle_data->milli_tokens, (gpr_atm)throttle_data->milli_token_ratio, - (gpr_atm)0, (gpr_atm)throttle_data->max_milli_tokens); -} - -grpc_server_retry_throttle_data* grpc_server_retry_throttle_data_ref( - grpc_server_retry_throttle_data* throttle_data) { - gpr_ref(&throttle_data->refs); - return throttle_data; -} - -void grpc_server_retry_throttle_data_unref( - grpc_server_retry_throttle_data* throttle_data) { - if (gpr_unref(&throttle_data->refs)) { - grpc_server_retry_throttle_data* replacement = - (grpc_server_retry_throttle_data*)gpr_atm_acq_load( - &throttle_data->replacement); - if (replacement != NULL) { - grpc_server_retry_throttle_data_unref(replacement); - } - gpr_free(throttle_data); - } -} - -static grpc_server_retry_throttle_data* grpc_server_retry_throttle_data_create( - int max_milli_tokens, int milli_token_ratio, - grpc_server_retry_throttle_data* old_throttle_data) { - grpc_server_retry_throttle_data* throttle_data = - (grpc_server_retry_throttle_data*)gpr_malloc(sizeof(*throttle_data)); - memset(throttle_data, 0, sizeof(*throttle_data)); - gpr_ref_init(&throttle_data->refs, 1); - throttle_data->max_milli_tokens = max_milli_tokens; - throttle_data->milli_token_ratio = milli_token_ratio; - int initial_milli_tokens = max_milli_tokens; - // If there was a pre-existing entry for this server name, initialize - // the token count by scaling proportionately to the old data. This - // ensures that if we're already throttling retries on the old scale, - // we will start out doing the same thing on the new one. - if (old_throttle_data != NULL) { - double token_fraction = - (int)gpr_atm_acq_load(&old_throttle_data->milli_tokens) / - (double)old_throttle_data->max_milli_tokens; - initial_milli_tokens = (int)(token_fraction * max_milli_tokens); - } - gpr_atm_rel_store(&throttle_data->milli_tokens, - (gpr_atm)initial_milli_tokens); - // If there was a pre-existing entry, mark it as stale and give it a - // pointer to the new entry, which is its replacement. - if (old_throttle_data != NULL) { - grpc_server_retry_throttle_data_ref(throttle_data); - gpr_atm_rel_store(&old_throttle_data->replacement, (gpr_atm)throttle_data); - } - return throttle_data; -} - -// -// avl vtable for string -> server_retry_throttle_data map -// - -static void* copy_server_name(void* key, void* unused) { - return gpr_strdup((const char*)key); -} - -static long compare_server_name(void* key1, void* key2, void* unused) { - return strcmp((const char*)key1, (const char*)key2); -} - -static void destroy_server_retry_throttle_data(void* value, void* unused) { - grpc_server_retry_throttle_data* throttle_data = - (grpc_server_retry_throttle_data*)value; - grpc_server_retry_throttle_data_unref(throttle_data); -} - -static void* copy_server_retry_throttle_data(void* value, void* unused) { - grpc_server_retry_throttle_data* throttle_data = - (grpc_server_retry_throttle_data*)value; - return grpc_server_retry_throttle_data_ref(throttle_data); -} - -static void destroy_server_name(void* key, void* unused) { gpr_free(key); } - -static const gpr_avl_vtable avl_vtable = { - destroy_server_name, copy_server_name, compare_server_name, - destroy_server_retry_throttle_data, copy_server_retry_throttle_data}; - -// -// server_retry_throttle_map -// - -static gpr_mu g_mu; -static gpr_avl g_avl; - -void grpc_retry_throttle_map_init() { - gpr_mu_init(&g_mu); - g_avl = gpr_avl_create(&avl_vtable); -} - -void grpc_retry_throttle_map_shutdown() { - gpr_mu_destroy(&g_mu); - gpr_avl_unref(g_avl, NULL); -} - -grpc_server_retry_throttle_data* grpc_retry_throttle_map_get_data_for_server( - const char* server_name, int max_milli_tokens, int milli_token_ratio) { - gpr_mu_lock(&g_mu); - grpc_server_retry_throttle_data* throttle_data = - (grpc_server_retry_throttle_data*)gpr_avl_get(g_avl, (char*)server_name, - NULL); - if (throttle_data == NULL) { - // Entry not found. Create a new one. - throttle_data = grpc_server_retry_throttle_data_create( - max_milli_tokens, milli_token_ratio, NULL); - g_avl = gpr_avl_add(g_avl, (char*)server_name, throttle_data, NULL); - } else { - if (throttle_data->max_milli_tokens != max_milli_tokens || - throttle_data->milli_token_ratio != milli_token_ratio) { - // Entry found but with old parameters. Create a new one based on - // the original one. - throttle_data = grpc_server_retry_throttle_data_create( - max_milli_tokens, milli_token_ratio, throttle_data); - g_avl = gpr_avl_add(g_avl, (char*)server_name, throttle_data, NULL); - } else { - // Entry found. Increase refcount. - grpc_server_retry_throttle_data_ref(throttle_data); - } - } - gpr_mu_unlock(&g_mu); - return throttle_data; -} diff --git a/src/core/ext/filters/client_channel/retry_throttle.cc b/src/core/ext/filters/client_channel/retry_throttle.cc new file mode 100644 index 0000000000..09dcade089 --- /dev/null +++ b/src/core/ext/filters/client_channel/retry_throttle.cc @@ -0,0 +1,202 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/retry_throttle.h" + +#include +#include + +#include +#include +#include +#include +#include + +// +// server_retry_throttle_data +// + +struct grpc_server_retry_throttle_data { + gpr_refcount refs; + int max_milli_tokens; + int milli_token_ratio; + gpr_atm milli_tokens; + // A pointer to the replacement for this grpc_server_retry_throttle_data + // entry. If non-NULL, then this entry is stale and must not be used. + // We hold a reference to the replacement. + gpr_atm replacement; +}; + +static void get_replacement_throttle_data_if_needed( + grpc_server_retry_throttle_data** throttle_data) { + while (true) { + grpc_server_retry_throttle_data* new_throttle_data = + (grpc_server_retry_throttle_data*)gpr_atm_acq_load( + &(*throttle_data)->replacement); + if (new_throttle_data == NULL) return; + *throttle_data = new_throttle_data; + } +} + +bool grpc_server_retry_throttle_data_record_failure( + grpc_server_retry_throttle_data* throttle_data) { + // First, check if we are stale and need to be replaced. + get_replacement_throttle_data_if_needed(&throttle_data); + // We decrement milli_tokens by 1000 (1 token) for each failure. + const int new_value = (int)gpr_atm_no_barrier_clamped_add( + &throttle_data->milli_tokens, (gpr_atm)-1000, (gpr_atm)0, + (gpr_atm)throttle_data->max_milli_tokens); + // Retries are allowed as long as the new value is above the threshold + // (max_milli_tokens / 2). + return new_value > throttle_data->max_milli_tokens / 2; +} + +void grpc_server_retry_throttle_data_record_success( + grpc_server_retry_throttle_data* throttle_data) { + // First, check if we are stale and need to be replaced. + get_replacement_throttle_data_if_needed(&throttle_data); + // We increment milli_tokens by milli_token_ratio for each success. + gpr_atm_no_barrier_clamped_add( + &throttle_data->milli_tokens, (gpr_atm)throttle_data->milli_token_ratio, + (gpr_atm)0, (gpr_atm)throttle_data->max_milli_tokens); +} + +grpc_server_retry_throttle_data* grpc_server_retry_throttle_data_ref( + grpc_server_retry_throttle_data* throttle_data) { + gpr_ref(&throttle_data->refs); + return throttle_data; +} + +void grpc_server_retry_throttle_data_unref( + grpc_server_retry_throttle_data* throttle_data) { + if (gpr_unref(&throttle_data->refs)) { + grpc_server_retry_throttle_data* replacement = + (grpc_server_retry_throttle_data*)gpr_atm_acq_load( + &throttle_data->replacement); + if (replacement != NULL) { + grpc_server_retry_throttle_data_unref(replacement); + } + gpr_free(throttle_data); + } +} + +static grpc_server_retry_throttle_data* grpc_server_retry_throttle_data_create( + int max_milli_tokens, int milli_token_ratio, + grpc_server_retry_throttle_data* old_throttle_data) { + grpc_server_retry_throttle_data* throttle_data = + (grpc_server_retry_throttle_data*)gpr_malloc(sizeof(*throttle_data)); + memset(throttle_data, 0, sizeof(*throttle_data)); + gpr_ref_init(&throttle_data->refs, 1); + throttle_data->max_milli_tokens = max_milli_tokens; + throttle_data->milli_token_ratio = milli_token_ratio; + int initial_milli_tokens = max_milli_tokens; + // If there was a pre-existing entry for this server name, initialize + // the token count by scaling proportionately to the old data. This + // ensures that if we're already throttling retries on the old scale, + // we will start out doing the same thing on the new one. + if (old_throttle_data != NULL) { + double token_fraction = + (int)gpr_atm_acq_load(&old_throttle_data->milli_tokens) / + (double)old_throttle_data->max_milli_tokens; + initial_milli_tokens = (int)(token_fraction * max_milli_tokens); + } + gpr_atm_rel_store(&throttle_data->milli_tokens, + (gpr_atm)initial_milli_tokens); + // If there was a pre-existing entry, mark it as stale and give it a + // pointer to the new entry, which is its replacement. + if (old_throttle_data != NULL) { + grpc_server_retry_throttle_data_ref(throttle_data); + gpr_atm_rel_store(&old_throttle_data->replacement, (gpr_atm)throttle_data); + } + return throttle_data; +} + +// +// avl vtable for string -> server_retry_throttle_data map +// + +static void* copy_server_name(void* key, void* unused) { + return gpr_strdup((const char*)key); +} + +static long compare_server_name(void* key1, void* key2, void* unused) { + return strcmp((const char*)key1, (const char*)key2); +} + +static void destroy_server_retry_throttle_data(void* value, void* unused) { + grpc_server_retry_throttle_data* throttle_data = + (grpc_server_retry_throttle_data*)value; + grpc_server_retry_throttle_data_unref(throttle_data); +} + +static void* copy_server_retry_throttle_data(void* value, void* unused) { + grpc_server_retry_throttle_data* throttle_data = + (grpc_server_retry_throttle_data*)value; + return grpc_server_retry_throttle_data_ref(throttle_data); +} + +static void destroy_server_name(void* key, void* unused) { gpr_free(key); } + +static const gpr_avl_vtable avl_vtable = { + destroy_server_name, copy_server_name, compare_server_name, + destroy_server_retry_throttle_data, copy_server_retry_throttle_data}; + +// +// server_retry_throttle_map +// + +static gpr_mu g_mu; +static gpr_avl g_avl; + +void grpc_retry_throttle_map_init() { + gpr_mu_init(&g_mu); + g_avl = gpr_avl_create(&avl_vtable); +} + +void grpc_retry_throttle_map_shutdown() { + gpr_mu_destroy(&g_mu); + gpr_avl_unref(g_avl, NULL); +} + +grpc_server_retry_throttle_data* grpc_retry_throttle_map_get_data_for_server( + const char* server_name, int max_milli_tokens, int milli_token_ratio) { + gpr_mu_lock(&g_mu); + grpc_server_retry_throttle_data* throttle_data = + (grpc_server_retry_throttle_data*)gpr_avl_get(g_avl, (char*)server_name, + NULL); + if (throttle_data == NULL) { + // Entry not found. Create a new one. + throttle_data = grpc_server_retry_throttle_data_create( + max_milli_tokens, milli_token_ratio, NULL); + g_avl = gpr_avl_add(g_avl, (char*)server_name, throttle_data, NULL); + } else { + if (throttle_data->max_milli_tokens != max_milli_tokens || + throttle_data->milli_token_ratio != milli_token_ratio) { + // Entry found but with old parameters. Create a new one based on + // the original one. + throttle_data = grpc_server_retry_throttle_data_create( + max_milli_tokens, milli_token_ratio, throttle_data); + g_avl = gpr_avl_add(g_avl, (char*)server_name, throttle_data, NULL); + } else { + // Entry found. Increase refcount. + grpc_server_retry_throttle_data_ref(throttle_data); + } + } + gpr_mu_unlock(&g_mu); + return throttle_data; +} diff --git a/src/core/ext/filters/client_channel/subchannel.c b/src/core/ext/filters/client_channel/subchannel.c deleted file mode 100644 index 40a51c72d6..0000000000 --- a/src/core/ext/filters/client_channel/subchannel.c +++ /dev/null @@ -1,816 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/subchannel.h" - -#include -#include - -#include -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/parse_address.h" -#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" -#include "src/core/ext/filters/client_channel/subchannel_index.h" -#include "src/core/ext/filters/client_channel/uri_parser.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/connected_channel.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/backoff.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/connectivity_state.h" - -#define INTERNAL_REF_BITS 16 -#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1)) - -#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1 -#define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6 -#define GRPC_SUBCHANNEL_RECONNECT_MIN_BACKOFF_SECONDS 20 -#define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120 -#define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2 - -#define GET_CONNECTED_SUBCHANNEL(subchannel, barrier) \ - ((grpc_connected_subchannel *)(gpr_atm_##barrier##_load( \ - &(subchannel)->connected_subchannel))) - -typedef struct { - grpc_closure closure; - grpc_subchannel *subchannel; - grpc_connectivity_state connectivity_state; -} state_watcher; - -typedef struct external_state_watcher { - grpc_subchannel *subchannel; - grpc_pollset_set *pollset_set; - grpc_closure *notify; - grpc_closure closure; - struct external_state_watcher *next; - struct external_state_watcher *prev; -} external_state_watcher; - -struct grpc_subchannel { - grpc_connector *connector; - - /** refcount - - lower INTERNAL_REF_BITS bits are for internal references: - these do not keep the subchannel open. - - upper remaining bits are for public references: these do - keep the subchannel open */ - gpr_atm ref_pair; - - /** non-transport related channel filters */ - const grpc_channel_filter **filters; - size_t num_filters; - /** channel arguments */ - grpc_channel_args *args; - - grpc_subchannel_key *key; - - /** set during connection */ - grpc_connect_out_args connecting_result; - - /** callback for connection finishing */ - grpc_closure connected; - - /** callback for our alarm */ - grpc_closure on_alarm; - - /** pollset_set tracking who's interested in a connection - being setup */ - grpc_pollset_set *pollset_set; - - /** active connection, or null; of type grpc_connected_subchannel */ - gpr_atm connected_subchannel; - - /** mutex protecting remaining elements */ - gpr_mu mu; - - /** have we seen a disconnection? */ - bool disconnected; - /** are we connecting */ - bool connecting; - /** connectivity state tracking */ - grpc_connectivity_state_tracker state_tracker; - - external_state_watcher root_external_state_watcher; - - /** next connect attempt time */ - gpr_timespec next_attempt; - /** backoff state */ - gpr_backoff backoff_state; - /** do we have an active alarm? */ - bool have_alarm; - /** have we started the backoff loop */ - bool backoff_begun; - /** our alarm */ - grpc_timer alarm; -}; - -struct grpc_subchannel_call { - grpc_connected_subchannel *connection; - grpc_closure *schedule_closure_after_destroy; -}; - -#define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) -#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)(con)) -#define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \ - (((grpc_subchannel_call *)(callstack)) - 1) - -static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel, - grpc_error *error); - -#ifndef NDEBUG -#define REF_REASON reason -#define REF_MUTATE_EXTRA_ARGS \ - GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char *purpose -#define REF_MUTATE_PURPOSE(x) , file, line, reason, x -#else -#define REF_REASON "" -#define REF_MUTATE_EXTRA_ARGS -#define REF_MUTATE_PURPOSE(x) -#endif - -/* - * connection implementation - */ - -static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_connected_subchannel *c = (grpc_connected_subchannel *)arg; - grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c)); - gpr_free(c); -} - -grpc_connected_subchannel *grpc_connected_subchannel_ref( - grpc_connected_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CONNECTION(c), REF_REASON); - return c; -} - -void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c), - REF_REASON); -} - -/* - * grpc_subchannel implementation - */ - -static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_subchannel *c = (grpc_subchannel *)arg; - gpr_free((void *)c->filters); - grpc_channel_args_destroy(exec_ctx, c->args); - grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker); - grpc_connector_unref(exec_ctx, c->connector); - grpc_pollset_set_destroy(exec_ctx, c->pollset_set); - grpc_subchannel_key_destroy(exec_ctx, c->key); - gpr_mu_destroy(&c->mu); - gpr_free(c); -} - -static gpr_atm ref_mutate(grpc_subchannel *c, gpr_atm delta, - int barrier REF_MUTATE_EXTRA_ARGS) { - gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) - : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) { - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "SUBCHANNEL: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c, - purpose, old_val, old_val + delta, reason); - } -#endif - return old_val; -} - -grpc_subchannel *grpc_subchannel_ref( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS), - 0 REF_MUTATE_PURPOSE("STRONG_REF")); - GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0); - return c; -} - -grpc_subchannel *grpc_subchannel_weak_ref( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF")); - GPR_ASSERT(old_refs != 0); - return c; -} - -grpc_subchannel *grpc_subchannel_ref_from_weak_ref( - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - if (!c) return NULL; - for (;;) { - gpr_atm old_refs = gpr_atm_acq_load(&c->ref_pair); - if (old_refs >= (1 << INTERNAL_REF_BITS)) { - gpr_atm new_refs = old_refs + (1 << INTERNAL_REF_BITS); - if (gpr_atm_rel_cas(&c->ref_pair, old_refs, new_refs)) { - return c; - } - } else { - return NULL; - } - } -} - -static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { - grpc_connected_subchannel *con; - grpc_subchannel_index_unregister(exec_ctx, c->key, c); - gpr_mu_lock(&c->mu); - GPR_ASSERT(!c->disconnected); - c->disconnected = true; - grpc_connector_shutdown( - exec_ctx, c->connector, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Subchannel disconnected")); - con = GET_CONNECTED_SUBCHANNEL(c, no_barrier); - if (con != NULL) { - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, con, "connection"); - gpr_atm_no_barrier_store(&c->connected_subchannel, (gpr_atm)0xdeadbeef); - } - gpr_mu_unlock(&c->mu); -} - -void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - // add a weak ref and subtract a strong ref (atomically) - old_refs = ref_mutate(c, (gpr_atm)1 - (gpr_atm)(1 << INTERNAL_REF_BITS), - 1 REF_MUTATE_PURPOSE("STRONG_UNREF")); - if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) { - disconnect(exec_ctx, c); - } - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "strong-unref"); -} - -void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - gpr_atm old_refs; - old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF")); - if (old_refs == 1) { - GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(subchannel_destroy, c, - grpc_schedule_on_exec_ctx), - GRPC_ERROR_NONE); - } -} - -grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, - grpc_connector *connector, - const grpc_subchannel_args *args) { - grpc_subchannel_key *key = grpc_subchannel_key_create(args); - grpc_subchannel *c = grpc_subchannel_index_find(exec_ctx, key); - if (c) { - grpc_subchannel_key_destroy(exec_ctx, key); - return c; - } - - GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED(exec_ctx); - c = (grpc_subchannel *)gpr_zalloc(sizeof(*c)); - c->key = key; - gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS); - c->connector = connector; - grpc_connector_ref(c->connector); - c->num_filters = args->filter_count; - if (c->num_filters > 0) { - c->filters = (const grpc_channel_filter **)gpr_malloc( - sizeof(grpc_channel_filter *) * c->num_filters); - memcpy((void *)c->filters, args->filters, - sizeof(grpc_channel_filter *) * c->num_filters); - } else { - c->filters = NULL; - } - c->pollset_set = grpc_pollset_set_create(); - grpc_resolved_address *addr = - (grpc_resolved_address *)gpr_malloc(sizeof(*addr)); - grpc_get_subchannel_address_arg(exec_ctx, args->args, addr); - grpc_resolved_address *new_address = NULL; - grpc_channel_args *new_args = NULL; - if (grpc_proxy_mappers_map_address(exec_ctx, addr, args->args, &new_address, - &new_args)) { - GPR_ASSERT(new_address != NULL); - gpr_free(addr); - addr = new_address; - } - static const char *keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS}; - grpc_arg new_arg = grpc_create_subchannel_address_arg(addr); - gpr_free(addr); - c->args = grpc_channel_args_copy_and_add_and_remove( - new_args != NULL ? new_args : args->args, keys_to_remove, - GPR_ARRAY_SIZE(keys_to_remove), &new_arg, 1); - gpr_free(new_arg.value.string); - if (new_args != NULL) grpc_channel_args_destroy(exec_ctx, new_args); - c->root_external_state_watcher.next = c->root_external_state_watcher.prev = - &c->root_external_state_watcher; - GRPC_CLOSURE_INIT(&c->connected, subchannel_connected, c, - grpc_schedule_on_exec_ctx); - grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, - "subchannel"); - int initial_backoff_ms = - GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000; - int min_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MIN_BACKOFF_SECONDS * 1000; - int max_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; - bool fixed_reconnect_backoff = false; - if (c->args) { - for (size_t i = 0; i < c->args->num_args; i++) { - if (0 == strcmp(c->args->args[i].key, - "grpc.testing.fixed_reconnect_backoff_ms")) { - fixed_reconnect_backoff = true; - initial_backoff_ms = min_backoff_ms = max_backoff_ms = - grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){initial_backoff_ms, 100, INT_MAX}); - } else if (0 == strcmp(c->args->args[i].key, - GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) { - fixed_reconnect_backoff = false; - min_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){min_backoff_ms, 100, INT_MAX}); - } else if (0 == strcmp(c->args->args[i].key, - GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) { - fixed_reconnect_backoff = false; - max_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){max_backoff_ms, 100, INT_MAX}); - } else if (0 == strcmp(c->args->args[i].key, - GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) { - fixed_reconnect_backoff = false; - initial_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){initial_backoff_ms, 100, INT_MAX}); - } - } - } - gpr_backoff_init( - &c->backoff_state, initial_backoff_ms, - fixed_reconnect_backoff ? 1.0 - : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER, - fixed_reconnect_backoff ? 0.0 : GRPC_SUBCHANNEL_RECONNECT_JITTER, - min_backoff_ms, max_backoff_ms); - gpr_mu_init(&c->mu); - - return grpc_subchannel_index_register(exec_ctx, key, c); -} - -static void continue_connect_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c) { - grpc_connect_in_args args; - - args.interested_parties = c->pollset_set; - args.deadline = c->next_attempt; - args.channel_args = c->args; - - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, - GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE, - "state_change"); - grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result, - &c->connected); -} - -grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c, - grpc_error **error) { - grpc_connectivity_state state; - gpr_mu_lock(&c->mu); - state = grpc_connectivity_state_get(&c->state_tracker, error); - gpr_mu_unlock(&c->mu); - return state; -} - -static void on_external_state_watcher_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - external_state_watcher *w = (external_state_watcher *)arg; - grpc_closure *follow_up = w->notify; - if (w->pollset_set != NULL) { - grpc_pollset_set_del_pollset_set(exec_ctx, w->subchannel->pollset_set, - w->pollset_set); - } - gpr_mu_lock(&w->subchannel->mu); - w->next->prev = w->prev; - w->prev->next = w->next; - gpr_mu_unlock(&w->subchannel->mu); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, w->subchannel, "external_state_watcher"); - gpr_free(w); - GRPC_CLOSURE_RUN(exec_ctx, follow_up, GRPC_ERROR_REF(error)); -} - -static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_subchannel *c = (grpc_subchannel *)arg; - gpr_mu_lock(&c->mu); - c->have_alarm = false; - if (c->disconnected) { - error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Disconnected", - &error, 1); - } else { - GRPC_ERROR_REF(error); - } - if (error == GRPC_ERROR_NONE) { - gpr_log(GPR_INFO, "Failed to connect to channel, retrying"); - c->next_attempt = - gpr_backoff_step(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC)); - continue_connect_locked(exec_ctx, c); - gpr_mu_unlock(&c->mu); - } else { - gpr_mu_unlock(&c->mu); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - } - GRPC_ERROR_UNREF(error); -} - -static void maybe_start_connecting_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c) { - if (c->disconnected) { - /* Don't try to connect if we're already disconnected */ - return; - } - - if (c->connecting) { - /* Already connecting: don't restart */ - return; - } - - if (GET_CONNECTED_SUBCHANNEL(c, no_barrier) != NULL) { - /* Already connected: don't restart */ - return; - } - - if (!grpc_connectivity_state_has_watchers(&c->state_tracker)) { - /* Nobody is interested in connecting: so don't just yet */ - return; - } - - c->connecting = true; - GRPC_SUBCHANNEL_WEAK_REF(c, "connecting"); - - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - if (!c->backoff_begun) { - c->backoff_begun = true; - c->next_attempt = gpr_backoff_begin(&c->backoff_state, now); - continue_connect_locked(exec_ctx, c); - } else { - GPR_ASSERT(!c->have_alarm); - c->have_alarm = true; - gpr_timespec time_til_next = gpr_time_sub(c->next_attempt, now); - if (gpr_time_cmp(time_til_next, gpr_time_0(time_til_next.clock_type)) <= - 0) { - gpr_log(GPR_INFO, "Retry immediately"); - } else { - gpr_log(GPR_INFO, "Retry in %" PRId64 ".%09d seconds", - time_til_next.tv_sec, time_til_next.tv_nsec); - } - GRPC_CLOSURE_INIT(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx); - grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, &c->on_alarm, now); - } -} - -void grpc_subchannel_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_subchannel *c, - grpc_pollset_set *interested_parties, grpc_connectivity_state *state, - grpc_closure *notify) { - external_state_watcher *w; - - if (state == NULL) { - gpr_mu_lock(&c->mu); - for (w = c->root_external_state_watcher.next; - w != &c->root_external_state_watcher; w = w->next) { - if (w->notify == notify) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &c->state_tracker, NULL, &w->closure); - } - } - gpr_mu_unlock(&c->mu); - } else { - w = (external_state_watcher *)gpr_malloc(sizeof(*w)); - w->subchannel = c; - w->pollset_set = interested_parties; - w->notify = notify; - GRPC_CLOSURE_INIT(&w->closure, on_external_state_watcher_done, w, - grpc_schedule_on_exec_ctx); - if (interested_parties != NULL) { - grpc_pollset_set_add_pollset_set(exec_ctx, c->pollset_set, - interested_parties); - } - GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher"); - gpr_mu_lock(&c->mu); - w->next = &c->root_external_state_watcher; - w->prev = w->next->prev; - w->next->prev = w->prev->next = w; - grpc_connectivity_state_notify_on_state_change(exec_ctx, &c->state_tracker, - state, &w->closure); - maybe_start_connecting_locked(exec_ctx, c); - gpr_mu_unlock(&c->mu); - } -} - -void grpc_connected_subchannel_process_transport_op( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, - grpc_transport_op *op) { - grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); - grpc_channel_element *top_elem = grpc_channel_stack_element(channel_stack, 0); - top_elem->filter->start_transport_op(exec_ctx, top_elem, op); -} - -static void subchannel_on_child_state_changed(grpc_exec_ctx *exec_ctx, void *p, - grpc_error *error) { - state_watcher *sw = (state_watcher *)p; - grpc_subchannel *c = sw->subchannel; - gpr_mu *mu = &c->mu; - - gpr_mu_lock(mu); - - /* if we failed just leave this closure */ - if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { - /* any errors on a subchannel ==> we're done, create a new one */ - sw->connectivity_state = GRPC_CHANNEL_SHUTDOWN; - } - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, - sw->connectivity_state, GRPC_ERROR_REF(error), - "reflect_child"); - if (sw->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL, - &sw->connectivity_state, &sw->closure); - GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); - sw = NULL; - } - - gpr_mu_unlock(mu); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "state_watcher"); - gpr_free(sw); -} - -static void connected_subchannel_state_op(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *con, - grpc_pollset_set *interested_parties, - grpc_connectivity_state *state, - grpc_closure *closure) { - grpc_transport_op *op = grpc_make_transport_op(NULL); - grpc_channel_element *elem; - op->connectivity_state = state; - op->on_connectivity_state_change = closure; - op->bind_pollset_set = interested_parties; - elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); - elem->filter->start_transport_op(exec_ctx, elem, op); -} - -void grpc_connected_subchannel_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, - grpc_pollset_set *interested_parties, grpc_connectivity_state *state, - grpc_closure *closure) { - connected_subchannel_state_op(exec_ctx, con, interested_parties, state, - closure); -} - -void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx, - grpc_connected_subchannel *con, - grpc_closure *closure) { - grpc_transport_op *op = grpc_make_transport_op(NULL); - grpc_channel_element *elem; - op->send_ping = closure; - elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); - elem->filter->start_transport_op(exec_ctx, elem, op); -} - -static bool publish_transport_locked(grpc_exec_ctx *exec_ctx, - grpc_subchannel *c) { - grpc_connected_subchannel *con; - grpc_channel_stack *stk; - state_watcher *sw_subchannel; - - /* construct channel stack */ - grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create(); - grpc_channel_stack_builder_set_channel_arguments( - exec_ctx, builder, c->connecting_result.channel_args); - grpc_channel_stack_builder_set_transport(builder, - c->connecting_result.transport); - - if (!grpc_channel_init_create_stack(exec_ctx, builder, - GRPC_CLIENT_SUBCHANNEL)) { - grpc_channel_stack_builder_destroy(exec_ctx, builder); - return false; - } - grpc_error *error = grpc_channel_stack_builder_finish( - exec_ctx, builder, 0, 1, connection_destroy, NULL, (void **)&con); - if (error != GRPC_ERROR_NONE) { - grpc_transport_destroy(exec_ctx, c->connecting_result.transport); - gpr_log(GPR_ERROR, "error initializing subchannel stack: %s", - grpc_error_string(error)); - GRPC_ERROR_UNREF(error); - return false; - } - stk = CHANNEL_STACK_FROM_CONNECTION(con); - memset(&c->connecting_result, 0, sizeof(c->connecting_result)); - - /* initialize state watcher */ - sw_subchannel = (state_watcher *)gpr_malloc(sizeof(*sw_subchannel)); - sw_subchannel->subchannel = c; - sw_subchannel->connectivity_state = GRPC_CHANNEL_READY; - GRPC_CLOSURE_INIT(&sw_subchannel->closure, subchannel_on_child_state_changed, - sw_subchannel, grpc_schedule_on_exec_ctx); - - if (c->disconnected) { - gpr_free(sw_subchannel); - grpc_channel_stack_destroy(exec_ctx, stk); - gpr_free(con); - return false; - } - - /* publish */ - /* TODO(ctiller): this full barrier seems to clear up a TSAN failure. - I'd have expected the rel_cas below to be enough, but - seemingly it's not. - Re-evaluate if we really need this. */ - gpr_atm_full_barrier(); - GPR_ASSERT(gpr_atm_rel_cas(&c->connected_subchannel, 0, (gpr_atm)con)); - - /* setup subchannel watching connected subchannel for changes; subchannel - ref for connecting is donated to the state watcher */ - GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - grpc_connected_subchannel_notify_on_state_change( - exec_ctx, con, c->pollset_set, &sw_subchannel->connectivity_state, - &sw_subchannel->closure); - - /* signal completion */ - grpc_connectivity_state_set(exec_ctx, &c->state_tracker, GRPC_CHANNEL_READY, - GRPC_ERROR_NONE, "connected"); - return true; -} - -static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_subchannel *c = (grpc_subchannel *)arg; - grpc_channel_args *delete_channel_args = c->connecting_result.channel_args; - - GRPC_SUBCHANNEL_WEAK_REF(c, "connected"); - gpr_mu_lock(&c->mu); - c->connecting = false; - if (c->connecting_result.transport != NULL && - publish_transport_locked(exec_ctx, c)) { - /* do nothing, transport was published */ - } else if (c->disconnected) { - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - } else { - grpc_connectivity_state_set( - exec_ctx, &c->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, - grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Connect Failed", &error, 1), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), - "connect_failed"); - - const char *errmsg = grpc_error_string(error); - gpr_log(GPR_INFO, "Connect failed: %s", errmsg); - - maybe_start_connecting_locked(exec_ctx, c); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); - } - gpr_mu_unlock(&c->mu); - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connected"); - grpc_channel_args_destroy(exec_ctx, delete_channel_args); -} - -/* - * grpc_subchannel_call implementation - */ - -static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call, - grpc_error *error) { - grpc_subchannel_call *c = (grpc_subchannel_call *)call; - GPR_ASSERT(c->schedule_closure_after_destroy != NULL); - GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0); - grpc_connected_subchannel *connection = c->connection; - grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), NULL, - c->schedule_closure_after_destroy); - GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, connection, "subchannel_call"); - GPR_TIMER_END("grpc_subchannel_call_unref.destroy", 0); -} - -void grpc_subchannel_call_set_cleanup_closure(grpc_subchannel_call *call, - grpc_closure *closure) { - GPR_ASSERT(call->schedule_closure_after_destroy == NULL); - GPR_ASSERT(closure != NULL); - call->schedule_closure_after_destroy = closure; -} - -void grpc_subchannel_call_ref( - grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); -} - -void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call *c - GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { - GRPC_CALL_STACK_UNREF(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); -} - -void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, - grpc_subchannel_call *call, - grpc_transport_stream_op_batch *batch) { - GPR_TIMER_BEGIN("grpc_subchannel_call_process_op", 0); - grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); - grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); - GRPC_CALL_LOG_OP(GPR_INFO, top_elem, batch); - top_elem->filter->start_transport_stream_op_batch(exec_ctx, top_elem, batch); - GPR_TIMER_END("grpc_subchannel_call_process_op", 0); -} - -grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel( - grpc_subchannel *c) { - return GET_CONNECTED_SUBCHANNEL(c, acq); -} - -const grpc_subchannel_key *grpc_subchannel_get_key( - const grpc_subchannel *subchannel) { - return subchannel->key; -} - -grpc_error *grpc_connected_subchannel_create_call( - grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, - const grpc_connected_subchannel_call_args *args, - grpc_subchannel_call **call) { - grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con); - *call = (grpc_subchannel_call *)gpr_arena_alloc( - args->arena, sizeof(grpc_subchannel_call) + chanstk->call_stack_size); - grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call); - (*call)->connection = GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call"); - const grpc_call_element_args call_args = { - .call_stack = callstk, - .server_transport_data = NULL, - .context = args->context, - .path = args->path, - .start_time = args->start_time, - .deadline = args->deadline, - .arena = args->arena, - .call_combiner = args->call_combiner}; - grpc_error *error = grpc_call_stack_init( - exec_ctx, chanstk, 1, subchannel_call_destroy, *call, &call_args); - if (error != GRPC_ERROR_NONE) { - const char *error_string = grpc_error_string(error); - gpr_log(GPR_ERROR, "error: %s", error_string); - return error; - } - grpc_call_stack_set_pollset_or_pollset_set(exec_ctx, callstk, args->pollent); - return GRPC_ERROR_NONE; -} - -grpc_call_stack *grpc_subchannel_call_get_call_stack( - grpc_subchannel_call *subchannel_call) { - return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call); -} - -static void grpc_uri_to_sockaddr(grpc_exec_ctx *exec_ctx, const char *uri_str, - grpc_resolved_address *addr) { - grpc_uri *uri = grpc_uri_parse(exec_ctx, uri_str, 0 /* suppress_errors */); - GPR_ASSERT(uri != NULL); - if (!grpc_parse_uri(uri, addr)) memset(addr, 0, sizeof(*addr)); - grpc_uri_destroy(uri); -} - -void grpc_get_subchannel_address_arg(grpc_exec_ctx *exec_ctx, - const grpc_channel_args *args, - grpc_resolved_address *addr) { - const char *addr_uri_str = grpc_get_subchannel_address_uri_arg(args); - memset(addr, 0, sizeof(*addr)); - if (*addr_uri_str != '\0') { - grpc_uri_to_sockaddr(exec_ctx, addr_uri_str, addr); - } -} - -const char *grpc_get_subchannel_address_uri_arg(const grpc_channel_args *args) { - const grpc_arg *addr_arg = - grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_ADDRESS); - GPR_ASSERT(addr_arg != NULL); // Should have been set by LB policy. - GPR_ASSERT(addr_arg->type == GRPC_ARG_STRING); - return addr_arg->value.string; -} - -grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address *addr) { - return grpc_channel_arg_string_create( - (char *)GRPC_ARG_SUBCHANNEL_ADDRESS, - addr->len > 0 ? grpc_sockaddr_to_uri(addr) : gpr_strdup("")); -} diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc new file mode 100644 index 0000000000..40a51c72d6 --- /dev/null +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -0,0 +1,816 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/subchannel.h" + +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/parse_address.h" +#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h" +#include "src/core/ext/filters/client_channel/subchannel_index.h" +#include "src/core/ext/filters/client_channel/uri_parser.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/backoff.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/connectivity_state.h" + +#define INTERNAL_REF_BITS 16 +#define STRONG_REF_MASK (~(gpr_atm)((1 << INTERNAL_REF_BITS) - 1)) + +#define GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS 1 +#define GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER 1.6 +#define GRPC_SUBCHANNEL_RECONNECT_MIN_BACKOFF_SECONDS 20 +#define GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS 120 +#define GRPC_SUBCHANNEL_RECONNECT_JITTER 0.2 + +#define GET_CONNECTED_SUBCHANNEL(subchannel, barrier) \ + ((grpc_connected_subchannel *)(gpr_atm_##barrier##_load( \ + &(subchannel)->connected_subchannel))) + +typedef struct { + grpc_closure closure; + grpc_subchannel *subchannel; + grpc_connectivity_state connectivity_state; +} state_watcher; + +typedef struct external_state_watcher { + grpc_subchannel *subchannel; + grpc_pollset_set *pollset_set; + grpc_closure *notify; + grpc_closure closure; + struct external_state_watcher *next; + struct external_state_watcher *prev; +} external_state_watcher; + +struct grpc_subchannel { + grpc_connector *connector; + + /** refcount + - lower INTERNAL_REF_BITS bits are for internal references: + these do not keep the subchannel open. + - upper remaining bits are for public references: these do + keep the subchannel open */ + gpr_atm ref_pair; + + /** non-transport related channel filters */ + const grpc_channel_filter **filters; + size_t num_filters; + /** channel arguments */ + grpc_channel_args *args; + + grpc_subchannel_key *key; + + /** set during connection */ + grpc_connect_out_args connecting_result; + + /** callback for connection finishing */ + grpc_closure connected; + + /** callback for our alarm */ + grpc_closure on_alarm; + + /** pollset_set tracking who's interested in a connection + being setup */ + grpc_pollset_set *pollset_set; + + /** active connection, or null; of type grpc_connected_subchannel */ + gpr_atm connected_subchannel; + + /** mutex protecting remaining elements */ + gpr_mu mu; + + /** have we seen a disconnection? */ + bool disconnected; + /** are we connecting */ + bool connecting; + /** connectivity state tracking */ + grpc_connectivity_state_tracker state_tracker; + + external_state_watcher root_external_state_watcher; + + /** next connect attempt time */ + gpr_timespec next_attempt; + /** backoff state */ + gpr_backoff backoff_state; + /** do we have an active alarm? */ + bool have_alarm; + /** have we started the backoff loop */ + bool backoff_begun; + /** our alarm */ + grpc_timer alarm; +}; + +struct grpc_subchannel_call { + grpc_connected_subchannel *connection; + grpc_closure *schedule_closure_after_destroy; +}; + +#define SUBCHANNEL_CALL_TO_CALL_STACK(call) ((grpc_call_stack *)((call) + 1)) +#define CHANNEL_STACK_FROM_CONNECTION(con) ((grpc_channel_stack *)(con)) +#define CALLSTACK_TO_SUBCHANNEL_CALL(callstack) \ + (((grpc_subchannel_call *)(callstack)) - 1) + +static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *subchannel, + grpc_error *error); + +#ifndef NDEBUG +#define REF_REASON reason +#define REF_MUTATE_EXTRA_ARGS \ + GRPC_SUBCHANNEL_REF_EXTRA_ARGS, const char *purpose +#define REF_MUTATE_PURPOSE(x) , file, line, reason, x +#else +#define REF_REASON "" +#define REF_MUTATE_EXTRA_ARGS +#define REF_MUTATE_PURPOSE(x) +#endif + +/* + * connection implementation + */ + +static void connection_destroy(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_connected_subchannel *c = (grpc_connected_subchannel *)arg; + grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c)); + gpr_free(c); +} + +grpc_connected_subchannel *grpc_connected_subchannel_ref( + grpc_connected_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CONNECTION(c), REF_REASON); + return c; +} + +void grpc_connected_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CONNECTION(c), + REF_REASON); +} + +/* + * grpc_subchannel implementation + */ + +static void subchannel_destroy(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_subchannel *c = (grpc_subchannel *)arg; + gpr_free((void *)c->filters); + grpc_channel_args_destroy(exec_ctx, c->args); + grpc_connectivity_state_destroy(exec_ctx, &c->state_tracker); + grpc_connector_unref(exec_ctx, c->connector); + grpc_pollset_set_destroy(exec_ctx, c->pollset_set); + grpc_subchannel_key_destroy(exec_ctx, c->key); + gpr_mu_destroy(&c->mu); + gpr_free(c); +} + +static gpr_atm ref_mutate(grpc_subchannel *c, gpr_atm delta, + int barrier REF_MUTATE_EXTRA_ARGS) { + gpr_atm old_val = barrier ? gpr_atm_full_fetch_add(&c->ref_pair, delta) + : gpr_atm_no_barrier_fetch_add(&c->ref_pair, delta); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) { + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SUBCHANNEL: %p %12s 0x%" PRIxPTR " -> 0x%" PRIxPTR " [%s]", c, + purpose, old_val, old_val + delta, reason); + } +#endif + return old_val; +} + +grpc_subchannel *grpc_subchannel_ref( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, (1 << INTERNAL_REF_BITS), + 0 REF_MUTATE_PURPOSE("STRONG_REF")); + GPR_ASSERT((old_refs & STRONG_REF_MASK) != 0); + return c; +} + +grpc_subchannel *grpc_subchannel_weak_ref( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, 1, 0 REF_MUTATE_PURPOSE("WEAK_REF")); + GPR_ASSERT(old_refs != 0); + return c; +} + +grpc_subchannel *grpc_subchannel_ref_from_weak_ref( + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + if (!c) return NULL; + for (;;) { + gpr_atm old_refs = gpr_atm_acq_load(&c->ref_pair); + if (old_refs >= (1 << INTERNAL_REF_BITS)) { + gpr_atm new_refs = old_refs + (1 << INTERNAL_REF_BITS); + if (gpr_atm_rel_cas(&c->ref_pair, old_refs, new_refs)) { + return c; + } + } else { + return NULL; + } + } +} + +static void disconnect(grpc_exec_ctx *exec_ctx, grpc_subchannel *c) { + grpc_connected_subchannel *con; + grpc_subchannel_index_unregister(exec_ctx, c->key, c); + gpr_mu_lock(&c->mu); + GPR_ASSERT(!c->disconnected); + c->disconnected = true; + grpc_connector_shutdown( + exec_ctx, c->connector, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Subchannel disconnected")); + con = GET_CONNECTED_SUBCHANNEL(c, no_barrier); + if (con != NULL) { + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, con, "connection"); + gpr_atm_no_barrier_store(&c->connected_subchannel, (gpr_atm)0xdeadbeef); + } + gpr_mu_unlock(&c->mu); +} + +void grpc_subchannel_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + // add a weak ref and subtract a strong ref (atomically) + old_refs = ref_mutate(c, (gpr_atm)1 - (gpr_atm)(1 << INTERNAL_REF_BITS), + 1 REF_MUTATE_PURPOSE("STRONG_UNREF")); + if ((old_refs & STRONG_REF_MASK) == (1 << INTERNAL_REF_BITS)) { + disconnect(exec_ctx, c); + } + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "strong-unref"); +} + +void grpc_subchannel_weak_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + gpr_atm old_refs; + old_refs = ref_mutate(c, -(gpr_atm)1, 1 REF_MUTATE_PURPOSE("WEAK_UNREF")); + if (old_refs == 1) { + GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(subchannel_destroy, c, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } +} + +grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, + grpc_connector *connector, + const grpc_subchannel_args *args) { + grpc_subchannel_key *key = grpc_subchannel_key_create(args); + grpc_subchannel *c = grpc_subchannel_index_find(exec_ctx, key); + if (c) { + grpc_subchannel_key_destroy(exec_ctx, key); + return c; + } + + GRPC_STATS_INC_CLIENT_SUBCHANNELS_CREATED(exec_ctx); + c = (grpc_subchannel *)gpr_zalloc(sizeof(*c)); + c->key = key; + gpr_atm_no_barrier_store(&c->ref_pair, 1 << INTERNAL_REF_BITS); + c->connector = connector; + grpc_connector_ref(c->connector); + c->num_filters = args->filter_count; + if (c->num_filters > 0) { + c->filters = (const grpc_channel_filter **)gpr_malloc( + sizeof(grpc_channel_filter *) * c->num_filters); + memcpy((void *)c->filters, args->filters, + sizeof(grpc_channel_filter *) * c->num_filters); + } else { + c->filters = NULL; + } + c->pollset_set = grpc_pollset_set_create(); + grpc_resolved_address *addr = + (grpc_resolved_address *)gpr_malloc(sizeof(*addr)); + grpc_get_subchannel_address_arg(exec_ctx, args->args, addr); + grpc_resolved_address *new_address = NULL; + grpc_channel_args *new_args = NULL; + if (grpc_proxy_mappers_map_address(exec_ctx, addr, args->args, &new_address, + &new_args)) { + GPR_ASSERT(new_address != NULL); + gpr_free(addr); + addr = new_address; + } + static const char *keys_to_remove[] = {GRPC_ARG_SUBCHANNEL_ADDRESS}; + grpc_arg new_arg = grpc_create_subchannel_address_arg(addr); + gpr_free(addr); + c->args = grpc_channel_args_copy_and_add_and_remove( + new_args != NULL ? new_args : args->args, keys_to_remove, + GPR_ARRAY_SIZE(keys_to_remove), &new_arg, 1); + gpr_free(new_arg.value.string); + if (new_args != NULL) grpc_channel_args_destroy(exec_ctx, new_args); + c->root_external_state_watcher.next = c->root_external_state_watcher.prev = + &c->root_external_state_watcher; + GRPC_CLOSURE_INIT(&c->connected, subchannel_connected, c, + grpc_schedule_on_exec_ctx); + grpc_connectivity_state_init(&c->state_tracker, GRPC_CHANNEL_IDLE, + "subchannel"); + int initial_backoff_ms = + GRPC_SUBCHANNEL_INITIAL_CONNECT_BACKOFF_SECONDS * 1000; + int min_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MIN_BACKOFF_SECONDS * 1000; + int max_backoff_ms = GRPC_SUBCHANNEL_RECONNECT_MAX_BACKOFF_SECONDS * 1000; + bool fixed_reconnect_backoff = false; + if (c->args) { + for (size_t i = 0; i < c->args->num_args; i++) { + if (0 == strcmp(c->args->args[i].key, + "grpc.testing.fixed_reconnect_backoff_ms")) { + fixed_reconnect_backoff = true; + initial_backoff_ms = min_backoff_ms = max_backoff_ms = + grpc_channel_arg_get_integer( + &c->args->args[i], + (grpc_integer_options){initial_backoff_ms, 100, INT_MAX}); + } else if (0 == strcmp(c->args->args[i].key, + GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + min_backoff_ms = grpc_channel_arg_get_integer( + &c->args->args[i], + (grpc_integer_options){min_backoff_ms, 100, INT_MAX}); + } else if (0 == strcmp(c->args->args[i].key, + GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + max_backoff_ms = grpc_channel_arg_get_integer( + &c->args->args[i], + (grpc_integer_options){max_backoff_ms, 100, INT_MAX}); + } else if (0 == strcmp(c->args->args[i].key, + GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) { + fixed_reconnect_backoff = false; + initial_backoff_ms = grpc_channel_arg_get_integer( + &c->args->args[i], + (grpc_integer_options){initial_backoff_ms, 100, INT_MAX}); + } + } + } + gpr_backoff_init( + &c->backoff_state, initial_backoff_ms, + fixed_reconnect_backoff ? 1.0 + : GRPC_SUBCHANNEL_RECONNECT_BACKOFF_MULTIPLIER, + fixed_reconnect_backoff ? 0.0 : GRPC_SUBCHANNEL_RECONNECT_JITTER, + min_backoff_ms, max_backoff_ms); + gpr_mu_init(&c->mu); + + return grpc_subchannel_index_register(exec_ctx, key, c); +} + +static void continue_connect_locked(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c) { + grpc_connect_in_args args; + + args.interested_parties = c->pollset_set; + args.deadline = c->next_attempt; + args.channel_args = c->args; + + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, + GRPC_CHANNEL_CONNECTING, GRPC_ERROR_NONE, + "state_change"); + grpc_connector_connect(exec_ctx, c->connector, &args, &c->connecting_result, + &c->connected); +} + +grpc_connectivity_state grpc_subchannel_check_connectivity(grpc_subchannel *c, + grpc_error **error) { + grpc_connectivity_state state; + gpr_mu_lock(&c->mu); + state = grpc_connectivity_state_get(&c->state_tracker, error); + gpr_mu_unlock(&c->mu); + return state; +} + +static void on_external_state_watcher_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + external_state_watcher *w = (external_state_watcher *)arg; + grpc_closure *follow_up = w->notify; + if (w->pollset_set != NULL) { + grpc_pollset_set_del_pollset_set(exec_ctx, w->subchannel->pollset_set, + w->pollset_set); + } + gpr_mu_lock(&w->subchannel->mu); + w->next->prev = w->prev; + w->prev->next = w->next; + gpr_mu_unlock(&w->subchannel->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, w->subchannel, "external_state_watcher"); + gpr_free(w); + GRPC_CLOSURE_RUN(exec_ctx, follow_up, GRPC_ERROR_REF(error)); +} + +static void on_alarm(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_subchannel *c = (grpc_subchannel *)arg; + gpr_mu_lock(&c->mu); + c->have_alarm = false; + if (c->disconnected) { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Disconnected", + &error, 1); + } else { + GRPC_ERROR_REF(error); + } + if (error == GRPC_ERROR_NONE) { + gpr_log(GPR_INFO, "Failed to connect to channel, retrying"); + c->next_attempt = + gpr_backoff_step(&c->backoff_state, gpr_now(GPR_CLOCK_MONOTONIC)); + continue_connect_locked(exec_ctx, c); + gpr_mu_unlock(&c->mu); + } else { + gpr_mu_unlock(&c->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); + } + GRPC_ERROR_UNREF(error); +} + +static void maybe_start_connecting_locked(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c) { + if (c->disconnected) { + /* Don't try to connect if we're already disconnected */ + return; + } + + if (c->connecting) { + /* Already connecting: don't restart */ + return; + } + + if (GET_CONNECTED_SUBCHANNEL(c, no_barrier) != NULL) { + /* Already connected: don't restart */ + return; + } + + if (!grpc_connectivity_state_has_watchers(&c->state_tracker)) { + /* Nobody is interested in connecting: so don't just yet */ + return; + } + + c->connecting = true; + GRPC_SUBCHANNEL_WEAK_REF(c, "connecting"); + + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + if (!c->backoff_begun) { + c->backoff_begun = true; + c->next_attempt = gpr_backoff_begin(&c->backoff_state, now); + continue_connect_locked(exec_ctx, c); + } else { + GPR_ASSERT(!c->have_alarm); + c->have_alarm = true; + gpr_timespec time_til_next = gpr_time_sub(c->next_attempt, now); + if (gpr_time_cmp(time_til_next, gpr_time_0(time_til_next.clock_type)) <= + 0) { + gpr_log(GPR_INFO, "Retry immediately"); + } else { + gpr_log(GPR_INFO, "Retry in %" PRId64 ".%09d seconds", + time_til_next.tv_sec, time_til_next.tv_nsec); + } + GRPC_CLOSURE_INIT(&c->on_alarm, on_alarm, c, grpc_schedule_on_exec_ctx); + grpc_timer_init(exec_ctx, &c->alarm, c->next_attempt, &c->on_alarm, now); + } +} + +void grpc_subchannel_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_subchannel *c, + grpc_pollset_set *interested_parties, grpc_connectivity_state *state, + grpc_closure *notify) { + external_state_watcher *w; + + if (state == NULL) { + gpr_mu_lock(&c->mu); + for (w = c->root_external_state_watcher.next; + w != &c->root_external_state_watcher; w = w->next) { + if (w->notify == notify) { + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &c->state_tracker, NULL, &w->closure); + } + } + gpr_mu_unlock(&c->mu); + } else { + w = (external_state_watcher *)gpr_malloc(sizeof(*w)); + w->subchannel = c; + w->pollset_set = interested_parties; + w->notify = notify; + GRPC_CLOSURE_INIT(&w->closure, on_external_state_watcher_done, w, + grpc_schedule_on_exec_ctx); + if (interested_parties != NULL) { + grpc_pollset_set_add_pollset_set(exec_ctx, c->pollset_set, + interested_parties); + } + GRPC_SUBCHANNEL_WEAK_REF(c, "external_state_watcher"); + gpr_mu_lock(&c->mu); + w->next = &c->root_external_state_watcher; + w->prev = w->next->prev; + w->next->prev = w->prev->next = w; + grpc_connectivity_state_notify_on_state_change(exec_ctx, &c->state_tracker, + state, &w->closure); + maybe_start_connecting_locked(exec_ctx, c); + gpr_mu_unlock(&c->mu); + } +} + +void grpc_connected_subchannel_process_transport_op( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, + grpc_transport_op *op) { + grpc_channel_stack *channel_stack = CHANNEL_STACK_FROM_CONNECTION(con); + grpc_channel_element *top_elem = grpc_channel_stack_element(channel_stack, 0); + top_elem->filter->start_transport_op(exec_ctx, top_elem, op); +} + +static void subchannel_on_child_state_changed(grpc_exec_ctx *exec_ctx, void *p, + grpc_error *error) { + state_watcher *sw = (state_watcher *)p; + grpc_subchannel *c = sw->subchannel; + gpr_mu *mu = &c->mu; + + gpr_mu_lock(mu); + + /* if we failed just leave this closure */ + if (sw->connectivity_state == GRPC_CHANNEL_TRANSIENT_FAILURE) { + /* any errors on a subchannel ==> we're done, create a new one */ + sw->connectivity_state = GRPC_CHANNEL_SHUTDOWN; + } + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, + sw->connectivity_state, GRPC_ERROR_REF(error), + "reflect_child"); + if (sw->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, GET_CONNECTED_SUBCHANNEL(c, no_barrier), NULL, + &sw->connectivity_state, &sw->closure); + GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); + sw = NULL; + } + + gpr_mu_unlock(mu); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "state_watcher"); + gpr_free(sw); +} + +static void connected_subchannel_state_op(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *con, + grpc_pollset_set *interested_parties, + grpc_connectivity_state *state, + grpc_closure *closure) { + grpc_transport_op *op = grpc_make_transport_op(NULL); + grpc_channel_element *elem; + op->connectivity_state = state; + op->on_connectivity_state_change = closure; + op->bind_pollset_set = interested_parties; + elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); + elem->filter->start_transport_op(exec_ctx, elem, op); +} + +void grpc_connected_subchannel_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, + grpc_pollset_set *interested_parties, grpc_connectivity_state *state, + grpc_closure *closure) { + connected_subchannel_state_op(exec_ctx, con, interested_parties, state, + closure); +} + +void grpc_connected_subchannel_ping(grpc_exec_ctx *exec_ctx, + grpc_connected_subchannel *con, + grpc_closure *closure) { + grpc_transport_op *op = grpc_make_transport_op(NULL); + grpc_channel_element *elem; + op->send_ping = closure; + elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CONNECTION(con), 0); + elem->filter->start_transport_op(exec_ctx, elem, op); +} + +static bool publish_transport_locked(grpc_exec_ctx *exec_ctx, + grpc_subchannel *c) { + grpc_connected_subchannel *con; + grpc_channel_stack *stk; + state_watcher *sw_subchannel; + + /* construct channel stack */ + grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create(); + grpc_channel_stack_builder_set_channel_arguments( + exec_ctx, builder, c->connecting_result.channel_args); + grpc_channel_stack_builder_set_transport(builder, + c->connecting_result.transport); + + if (!grpc_channel_init_create_stack(exec_ctx, builder, + GRPC_CLIENT_SUBCHANNEL)) { + grpc_channel_stack_builder_destroy(exec_ctx, builder); + return false; + } + grpc_error *error = grpc_channel_stack_builder_finish( + exec_ctx, builder, 0, 1, connection_destroy, NULL, (void **)&con); + if (error != GRPC_ERROR_NONE) { + grpc_transport_destroy(exec_ctx, c->connecting_result.transport); + gpr_log(GPR_ERROR, "error initializing subchannel stack: %s", + grpc_error_string(error)); + GRPC_ERROR_UNREF(error); + return false; + } + stk = CHANNEL_STACK_FROM_CONNECTION(con); + memset(&c->connecting_result, 0, sizeof(c->connecting_result)); + + /* initialize state watcher */ + sw_subchannel = (state_watcher *)gpr_malloc(sizeof(*sw_subchannel)); + sw_subchannel->subchannel = c; + sw_subchannel->connectivity_state = GRPC_CHANNEL_READY; + GRPC_CLOSURE_INIT(&sw_subchannel->closure, subchannel_on_child_state_changed, + sw_subchannel, grpc_schedule_on_exec_ctx); + + if (c->disconnected) { + gpr_free(sw_subchannel); + grpc_channel_stack_destroy(exec_ctx, stk); + gpr_free(con); + return false; + } + + /* publish */ + /* TODO(ctiller): this full barrier seems to clear up a TSAN failure. + I'd have expected the rel_cas below to be enough, but + seemingly it's not. + Re-evaluate if we really need this. */ + gpr_atm_full_barrier(); + GPR_ASSERT(gpr_atm_rel_cas(&c->connected_subchannel, 0, (gpr_atm)con)); + + /* setup subchannel watching connected subchannel for changes; subchannel + ref for connecting is donated to the state watcher */ + GRPC_SUBCHANNEL_WEAK_REF(c, "state_watcher"); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); + grpc_connected_subchannel_notify_on_state_change( + exec_ctx, con, c->pollset_set, &sw_subchannel->connectivity_state, + &sw_subchannel->closure); + + /* signal completion */ + grpc_connectivity_state_set(exec_ctx, &c->state_tracker, GRPC_CHANNEL_READY, + GRPC_ERROR_NONE, "connected"); + return true; +} + +static void subchannel_connected(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_subchannel *c = (grpc_subchannel *)arg; + grpc_channel_args *delete_channel_args = c->connecting_result.channel_args; + + GRPC_SUBCHANNEL_WEAK_REF(c, "connected"); + gpr_mu_lock(&c->mu); + c->connecting = false; + if (c->connecting_result.transport != NULL && + publish_transport_locked(exec_ctx, c)) { + /* do nothing, transport was published */ + } else if (c->disconnected) { + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); + } else { + grpc_connectivity_state_set( + exec_ctx, &c->state_tracker, GRPC_CHANNEL_TRANSIENT_FAILURE, + grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Connect Failed", &error, 1), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE), + "connect_failed"); + + const char *errmsg = grpc_error_string(error); + gpr_log(GPR_INFO, "Connect failed: %s", errmsg); + + maybe_start_connecting_locked(exec_ctx, c); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connecting"); + } + gpr_mu_unlock(&c->mu); + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, c, "connected"); + grpc_channel_args_destroy(exec_ctx, delete_channel_args); +} + +/* + * grpc_subchannel_call implementation + */ + +static void subchannel_call_destroy(grpc_exec_ctx *exec_ctx, void *call, + grpc_error *error) { + grpc_subchannel_call *c = (grpc_subchannel_call *)call; + GPR_ASSERT(c->schedule_closure_after_destroy != NULL); + GPR_TIMER_BEGIN("grpc_subchannel_call_unref.destroy", 0); + grpc_connected_subchannel *connection = c->connection; + grpc_call_stack_destroy(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), NULL, + c->schedule_closure_after_destroy); + GRPC_CONNECTED_SUBCHANNEL_UNREF(exec_ctx, connection, "subchannel_call"); + GPR_TIMER_END("grpc_subchannel_call_unref.destroy", 0); +} + +void grpc_subchannel_call_set_cleanup_closure(grpc_subchannel_call *call, + grpc_closure *closure) { + GPR_ASSERT(call->schedule_closure_after_destroy == NULL); + GPR_ASSERT(closure != NULL); + call->schedule_closure_after_destroy = closure; +} + +void grpc_subchannel_call_ref( + grpc_subchannel_call *c GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CALL_STACK_REF(SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); +} + +void grpc_subchannel_call_unref(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *c + GRPC_SUBCHANNEL_REF_EXTRA_ARGS) { + GRPC_CALL_STACK_UNREF(exec_ctx, SUBCHANNEL_CALL_TO_CALL_STACK(c), REF_REASON); +} + +void grpc_subchannel_call_process_op(grpc_exec_ctx *exec_ctx, + grpc_subchannel_call *call, + grpc_transport_stream_op_batch *batch) { + GPR_TIMER_BEGIN("grpc_subchannel_call_process_op", 0); + grpc_call_stack *call_stack = SUBCHANNEL_CALL_TO_CALL_STACK(call); + grpc_call_element *top_elem = grpc_call_stack_element(call_stack, 0); + GRPC_CALL_LOG_OP(GPR_INFO, top_elem, batch); + top_elem->filter->start_transport_stream_op_batch(exec_ctx, top_elem, batch); + GPR_TIMER_END("grpc_subchannel_call_process_op", 0); +} + +grpc_connected_subchannel *grpc_subchannel_get_connected_subchannel( + grpc_subchannel *c) { + return GET_CONNECTED_SUBCHANNEL(c, acq); +} + +const grpc_subchannel_key *grpc_subchannel_get_key( + const grpc_subchannel *subchannel) { + return subchannel->key; +} + +grpc_error *grpc_connected_subchannel_create_call( + grpc_exec_ctx *exec_ctx, grpc_connected_subchannel *con, + const grpc_connected_subchannel_call_args *args, + grpc_subchannel_call **call) { + grpc_channel_stack *chanstk = CHANNEL_STACK_FROM_CONNECTION(con); + *call = (grpc_subchannel_call *)gpr_arena_alloc( + args->arena, sizeof(grpc_subchannel_call) + chanstk->call_stack_size); + grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call); + (*call)->connection = GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call"); + const grpc_call_element_args call_args = { + .call_stack = callstk, + .server_transport_data = NULL, + .context = args->context, + .path = args->path, + .start_time = args->start_time, + .deadline = args->deadline, + .arena = args->arena, + .call_combiner = args->call_combiner}; + grpc_error *error = grpc_call_stack_init( + exec_ctx, chanstk, 1, subchannel_call_destroy, *call, &call_args); + if (error != GRPC_ERROR_NONE) { + const char *error_string = grpc_error_string(error); + gpr_log(GPR_ERROR, "error: %s", error_string); + return error; + } + grpc_call_stack_set_pollset_or_pollset_set(exec_ctx, callstk, args->pollent); + return GRPC_ERROR_NONE; +} + +grpc_call_stack *grpc_subchannel_call_get_call_stack( + grpc_subchannel_call *subchannel_call) { + return SUBCHANNEL_CALL_TO_CALL_STACK(subchannel_call); +} + +static void grpc_uri_to_sockaddr(grpc_exec_ctx *exec_ctx, const char *uri_str, + grpc_resolved_address *addr) { + grpc_uri *uri = grpc_uri_parse(exec_ctx, uri_str, 0 /* suppress_errors */); + GPR_ASSERT(uri != NULL); + if (!grpc_parse_uri(uri, addr)) memset(addr, 0, sizeof(*addr)); + grpc_uri_destroy(uri); +} + +void grpc_get_subchannel_address_arg(grpc_exec_ctx *exec_ctx, + const grpc_channel_args *args, + grpc_resolved_address *addr) { + const char *addr_uri_str = grpc_get_subchannel_address_uri_arg(args); + memset(addr, 0, sizeof(*addr)); + if (*addr_uri_str != '\0') { + grpc_uri_to_sockaddr(exec_ctx, addr_uri_str, addr); + } +} + +const char *grpc_get_subchannel_address_uri_arg(const grpc_channel_args *args) { + const grpc_arg *addr_arg = + grpc_channel_args_find(args, GRPC_ARG_SUBCHANNEL_ADDRESS); + GPR_ASSERT(addr_arg != NULL); // Should have been set by LB policy. + GPR_ASSERT(addr_arg->type == GRPC_ARG_STRING); + return addr_arg->value.string; +} + +grpc_arg grpc_create_subchannel_address_arg(const grpc_resolved_address *addr) { + return grpc_channel_arg_string_create( + (char *)GRPC_ARG_SUBCHANNEL_ADDRESS, + addr->len > 0 ? grpc_sockaddr_to_uri(addr) : gpr_strdup("")); +} diff --git a/src/core/ext/filters/client_channel/subchannel_index.c b/src/core/ext/filters/client_channel/subchannel_index.c deleted file mode 100644 index d7a51f3899..0000000000 --- a/src/core/ext/filters/client_channel/subchannel_index.c +++ /dev/null @@ -1,251 +0,0 @@ -// -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -#include "src/core/ext/filters/client_channel/subchannel_index.h" - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" - -// a map of subchannel_key --> subchannel, used for detecting connections -// to the same destination in order to share them -static gpr_avl g_subchannel_index; - -static gpr_mu g_mu; - -static gpr_refcount g_refcount; - -struct grpc_subchannel_key { - grpc_subchannel_args args; -}; - -static bool g_force_creation = false; - -static grpc_subchannel_key *create_key( - const grpc_subchannel_args *args, - grpc_channel_args *(*copy_channel_args)(const grpc_channel_args *args)) { - grpc_subchannel_key *k = (grpc_subchannel_key *)gpr_malloc(sizeof(*k)); - k->args.filter_count = args->filter_count; - if (k->args.filter_count > 0) { - k->args.filters = (const grpc_channel_filter **)gpr_malloc( - sizeof(*k->args.filters) * k->args.filter_count); - memcpy((grpc_channel_filter *)k->args.filters, args->filters, - sizeof(*k->args.filters) * k->args.filter_count); - } else { - k->args.filters = NULL; - } - k->args.args = copy_channel_args(args->args); - return k; -} - -grpc_subchannel_key *grpc_subchannel_key_create( - const grpc_subchannel_args *args) { - return create_key(args, grpc_channel_args_normalize); -} - -static grpc_subchannel_key *subchannel_key_copy(grpc_subchannel_key *k) { - return create_key(&k->args, grpc_channel_args_copy); -} - -int grpc_subchannel_key_compare(const grpc_subchannel_key *a, - const grpc_subchannel_key *b) { - if (g_force_creation) return false; - int c = GPR_ICMP(a->args.filter_count, b->args.filter_count); - if (c != 0) return c; - if (a->args.filter_count > 0) { - c = memcmp(a->args.filters, b->args.filters, - a->args.filter_count * sizeof(*a->args.filters)); - if (c != 0) return c; - } - return grpc_channel_args_compare(a->args.args, b->args.args); -} - -void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx, - grpc_subchannel_key *k) { - gpr_free((grpc_channel_args *)k->args.filters); - grpc_channel_args_destroy(exec_ctx, (grpc_channel_args *)k->args.args); - gpr_free(k); -} - -static void sck_avl_destroy(void *p, void *user_data) { - grpc_exec_ctx *exec_ctx = (grpc_exec_ctx *)user_data; - grpc_subchannel_key_destroy(exec_ctx, (grpc_subchannel_key *)p); -} - -static void *sck_avl_copy(void *p, void *unused) { - return subchannel_key_copy((grpc_subchannel_key *)p); -} - -static long sck_avl_compare(void *a, void *b, void *unused) { - return grpc_subchannel_key_compare((grpc_subchannel_key *)a, - (grpc_subchannel_key *)b); -} - -static void scv_avl_destroy(void *p, void *user_data) { - grpc_exec_ctx *exec_ctx = (grpc_exec_ctx *)user_data; - GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, (grpc_subchannel *)p, - "subchannel_index"); -} - -static void *scv_avl_copy(void *p, void *unused) { - GRPC_SUBCHANNEL_WEAK_REF((grpc_subchannel *)p, "subchannel_index"); - return p; -} - -static const gpr_avl_vtable subchannel_avl_vtable = { - .destroy_key = sck_avl_destroy, - .copy_key = sck_avl_copy, - .compare_keys = sck_avl_compare, - .destroy_value = scv_avl_destroy, - .copy_value = scv_avl_copy}; - -void grpc_subchannel_index_init(void) { - g_subchannel_index = gpr_avl_create(&subchannel_avl_vtable); - gpr_mu_init(&g_mu); - gpr_ref_init(&g_refcount, 1); -} - -void grpc_subchannel_index_shutdown(void) { - // TODO(juanlishen): This refcounting mechanism may lead to memory leackage. - // To solve that, we should force polling to flush any pending callbacks, then - // shutdown safely. - grpc_subchannel_index_unref(); -} - -void grpc_subchannel_index_unref(void) { - if (gpr_unref(&g_refcount)) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_mu_destroy(&g_mu); - gpr_avl_unref(g_subchannel_index, &exec_ctx); - grpc_exec_ctx_finish(&exec_ctx); - } -} - -void grpc_subchannel_index_ref(void) { gpr_ref_non_zero(&g_refcount); } - -grpc_subchannel *grpc_subchannel_index_find(grpc_exec_ctx *exec_ctx, - grpc_subchannel_key *key) { - // Lock, and take a reference to the subchannel index. - // We don't need to do the search under a lock as avl's are immutable. - gpr_mu_lock(&g_mu); - gpr_avl index = gpr_avl_ref(g_subchannel_index, exec_ctx); - gpr_mu_unlock(&g_mu); - - grpc_subchannel *c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF( - (grpc_subchannel *)gpr_avl_get(index, key, exec_ctx), "index_find"); - gpr_avl_unref(index, exec_ctx); - - return c; -} - -grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx, - grpc_subchannel_key *key, - grpc_subchannel *constructed) { - grpc_subchannel *c = NULL; - bool need_to_unref_constructed; - - while (c == NULL) { - need_to_unref_constructed = false; - - // Compare and swap loop: - // - take a reference to the current index - gpr_mu_lock(&g_mu); - gpr_avl index = gpr_avl_ref(g_subchannel_index, exec_ctx); - gpr_mu_unlock(&g_mu); - - // - Check to see if a subchannel already exists - c = (grpc_subchannel *)gpr_avl_get(index, key, exec_ctx); - if (c != NULL) { - c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(c, "index_register"); - } - if (c != NULL) { - // yes -> we're done - need_to_unref_constructed = true; - } else { - // no -> update the avl and compare/swap - gpr_avl updated = gpr_avl_add( - gpr_avl_ref(index, exec_ctx), subchannel_key_copy(key), - GRPC_SUBCHANNEL_WEAK_REF(constructed, "index_register"), exec_ctx); - - // it may happen (but it's expected to be unlikely) - // that some other thread has changed the index: - // compare/swap here to check that, and retry as necessary - gpr_mu_lock(&g_mu); - if (index.root == g_subchannel_index.root) { - GPR_SWAP(gpr_avl, updated, g_subchannel_index); - c = constructed; - } - gpr_mu_unlock(&g_mu); - - gpr_avl_unref(updated, exec_ctx); - } - gpr_avl_unref(index, exec_ctx); - } - - if (need_to_unref_constructed) { - GRPC_SUBCHANNEL_UNREF(exec_ctx, constructed, "index_register"); - } - - return c; -} - -void grpc_subchannel_index_unregister(grpc_exec_ctx *exec_ctx, - grpc_subchannel_key *key, - grpc_subchannel *constructed) { - bool done = false; - while (!done) { - // Compare and swap loop: - // - take a reference to the current index - gpr_mu_lock(&g_mu); - gpr_avl index = gpr_avl_ref(g_subchannel_index, exec_ctx); - gpr_mu_unlock(&g_mu); - - // Check to see if this key still refers to the previously - // registered subchannel - grpc_subchannel *c = (grpc_subchannel *)gpr_avl_get(index, key, exec_ctx); - if (c != constructed) { - gpr_avl_unref(index, exec_ctx); - break; - } - - // compare and swap the update (some other thread may have - // mutated the index behind us) - gpr_avl updated = - gpr_avl_remove(gpr_avl_ref(index, exec_ctx), key, exec_ctx); - - gpr_mu_lock(&g_mu); - if (index.root == g_subchannel_index.root) { - GPR_SWAP(gpr_avl, updated, g_subchannel_index); - done = true; - } - gpr_mu_unlock(&g_mu); - - gpr_avl_unref(updated, exec_ctx); - gpr_avl_unref(index, exec_ctx); - } -} - -void grpc_subchannel_index_test_only_set_force_creation(bool force_creation) { - g_force_creation = force_creation; -} diff --git a/src/core/ext/filters/client_channel/subchannel_index.cc b/src/core/ext/filters/client_channel/subchannel_index.cc new file mode 100644 index 0000000000..d7a51f3899 --- /dev/null +++ b/src/core/ext/filters/client_channel/subchannel_index.cc @@ -0,0 +1,251 @@ +// +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// + +#include "src/core/ext/filters/client_channel/subchannel_index.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" + +// a map of subchannel_key --> subchannel, used for detecting connections +// to the same destination in order to share them +static gpr_avl g_subchannel_index; + +static gpr_mu g_mu; + +static gpr_refcount g_refcount; + +struct grpc_subchannel_key { + grpc_subchannel_args args; +}; + +static bool g_force_creation = false; + +static grpc_subchannel_key *create_key( + const grpc_subchannel_args *args, + grpc_channel_args *(*copy_channel_args)(const grpc_channel_args *args)) { + grpc_subchannel_key *k = (grpc_subchannel_key *)gpr_malloc(sizeof(*k)); + k->args.filter_count = args->filter_count; + if (k->args.filter_count > 0) { + k->args.filters = (const grpc_channel_filter **)gpr_malloc( + sizeof(*k->args.filters) * k->args.filter_count); + memcpy((grpc_channel_filter *)k->args.filters, args->filters, + sizeof(*k->args.filters) * k->args.filter_count); + } else { + k->args.filters = NULL; + } + k->args.args = copy_channel_args(args->args); + return k; +} + +grpc_subchannel_key *grpc_subchannel_key_create( + const grpc_subchannel_args *args) { + return create_key(args, grpc_channel_args_normalize); +} + +static grpc_subchannel_key *subchannel_key_copy(grpc_subchannel_key *k) { + return create_key(&k->args, grpc_channel_args_copy); +} + +int grpc_subchannel_key_compare(const grpc_subchannel_key *a, + const grpc_subchannel_key *b) { + if (g_force_creation) return false; + int c = GPR_ICMP(a->args.filter_count, b->args.filter_count); + if (c != 0) return c; + if (a->args.filter_count > 0) { + c = memcmp(a->args.filters, b->args.filters, + a->args.filter_count * sizeof(*a->args.filters)); + if (c != 0) return c; + } + return grpc_channel_args_compare(a->args.args, b->args.args); +} + +void grpc_subchannel_key_destroy(grpc_exec_ctx *exec_ctx, + grpc_subchannel_key *k) { + gpr_free((grpc_channel_args *)k->args.filters); + grpc_channel_args_destroy(exec_ctx, (grpc_channel_args *)k->args.args); + gpr_free(k); +} + +static void sck_avl_destroy(void *p, void *user_data) { + grpc_exec_ctx *exec_ctx = (grpc_exec_ctx *)user_data; + grpc_subchannel_key_destroy(exec_ctx, (grpc_subchannel_key *)p); +} + +static void *sck_avl_copy(void *p, void *unused) { + return subchannel_key_copy((grpc_subchannel_key *)p); +} + +static long sck_avl_compare(void *a, void *b, void *unused) { + return grpc_subchannel_key_compare((grpc_subchannel_key *)a, + (grpc_subchannel_key *)b); +} + +static void scv_avl_destroy(void *p, void *user_data) { + grpc_exec_ctx *exec_ctx = (grpc_exec_ctx *)user_data; + GRPC_SUBCHANNEL_WEAK_UNREF(exec_ctx, (grpc_subchannel *)p, + "subchannel_index"); +} + +static void *scv_avl_copy(void *p, void *unused) { + GRPC_SUBCHANNEL_WEAK_REF((grpc_subchannel *)p, "subchannel_index"); + return p; +} + +static const gpr_avl_vtable subchannel_avl_vtable = { + .destroy_key = sck_avl_destroy, + .copy_key = sck_avl_copy, + .compare_keys = sck_avl_compare, + .destroy_value = scv_avl_destroy, + .copy_value = scv_avl_copy}; + +void grpc_subchannel_index_init(void) { + g_subchannel_index = gpr_avl_create(&subchannel_avl_vtable); + gpr_mu_init(&g_mu); + gpr_ref_init(&g_refcount, 1); +} + +void grpc_subchannel_index_shutdown(void) { + // TODO(juanlishen): This refcounting mechanism may lead to memory leackage. + // To solve that, we should force polling to flush any pending callbacks, then + // shutdown safely. + grpc_subchannel_index_unref(); +} + +void grpc_subchannel_index_unref(void) { + if (gpr_unref(&g_refcount)) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_mu_destroy(&g_mu); + gpr_avl_unref(g_subchannel_index, &exec_ctx); + grpc_exec_ctx_finish(&exec_ctx); + } +} + +void grpc_subchannel_index_ref(void) { gpr_ref_non_zero(&g_refcount); } + +grpc_subchannel *grpc_subchannel_index_find(grpc_exec_ctx *exec_ctx, + grpc_subchannel_key *key) { + // Lock, and take a reference to the subchannel index. + // We don't need to do the search under a lock as avl's are immutable. + gpr_mu_lock(&g_mu); + gpr_avl index = gpr_avl_ref(g_subchannel_index, exec_ctx); + gpr_mu_unlock(&g_mu); + + grpc_subchannel *c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF( + (grpc_subchannel *)gpr_avl_get(index, key, exec_ctx), "index_find"); + gpr_avl_unref(index, exec_ctx); + + return c; +} + +grpc_subchannel *grpc_subchannel_index_register(grpc_exec_ctx *exec_ctx, + grpc_subchannel_key *key, + grpc_subchannel *constructed) { + grpc_subchannel *c = NULL; + bool need_to_unref_constructed; + + while (c == NULL) { + need_to_unref_constructed = false; + + // Compare and swap loop: + // - take a reference to the current index + gpr_mu_lock(&g_mu); + gpr_avl index = gpr_avl_ref(g_subchannel_index, exec_ctx); + gpr_mu_unlock(&g_mu); + + // - Check to see if a subchannel already exists + c = (grpc_subchannel *)gpr_avl_get(index, key, exec_ctx); + if (c != NULL) { + c = GRPC_SUBCHANNEL_REF_FROM_WEAK_REF(c, "index_register"); + } + if (c != NULL) { + // yes -> we're done + need_to_unref_constructed = true; + } else { + // no -> update the avl and compare/swap + gpr_avl updated = gpr_avl_add( + gpr_avl_ref(index, exec_ctx), subchannel_key_copy(key), + GRPC_SUBCHANNEL_WEAK_REF(constructed, "index_register"), exec_ctx); + + // it may happen (but it's expected to be unlikely) + // that some other thread has changed the index: + // compare/swap here to check that, and retry as necessary + gpr_mu_lock(&g_mu); + if (index.root == g_subchannel_index.root) { + GPR_SWAP(gpr_avl, updated, g_subchannel_index); + c = constructed; + } + gpr_mu_unlock(&g_mu); + + gpr_avl_unref(updated, exec_ctx); + } + gpr_avl_unref(index, exec_ctx); + } + + if (need_to_unref_constructed) { + GRPC_SUBCHANNEL_UNREF(exec_ctx, constructed, "index_register"); + } + + return c; +} + +void grpc_subchannel_index_unregister(grpc_exec_ctx *exec_ctx, + grpc_subchannel_key *key, + grpc_subchannel *constructed) { + bool done = false; + while (!done) { + // Compare and swap loop: + // - take a reference to the current index + gpr_mu_lock(&g_mu); + gpr_avl index = gpr_avl_ref(g_subchannel_index, exec_ctx); + gpr_mu_unlock(&g_mu); + + // Check to see if this key still refers to the previously + // registered subchannel + grpc_subchannel *c = (grpc_subchannel *)gpr_avl_get(index, key, exec_ctx); + if (c != constructed) { + gpr_avl_unref(index, exec_ctx); + break; + } + + // compare and swap the update (some other thread may have + // mutated the index behind us) + gpr_avl updated = + gpr_avl_remove(gpr_avl_ref(index, exec_ctx), key, exec_ctx); + + gpr_mu_lock(&g_mu); + if (index.root == g_subchannel_index.root) { + GPR_SWAP(gpr_avl, updated, g_subchannel_index); + done = true; + } + gpr_mu_unlock(&g_mu); + + gpr_avl_unref(updated, exec_ctx); + gpr_avl_unref(index, exec_ctx); + } +} + +void grpc_subchannel_index_test_only_set_force_creation(bool force_creation) { + g_force_creation = force_creation; +} diff --git a/src/core/ext/filters/client_channel/uri_parser.c b/src/core/ext/filters/client_channel/uri_parser.c deleted file mode 100644 index fb4fb8e694..0000000000 --- a/src/core/ext/filters/client_channel/uri_parser.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/client_channel/uri_parser.h" - -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/slice/percent_encoding.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -/** a size_t default value... maps to all 1's */ -#define NOT_SET (~(size_t)0) - -static grpc_uri *bad_uri(const char *uri_text, size_t pos, const char *section, - bool suppress_errors) { - char *line_prefix; - size_t pfx_len; - - if (!suppress_errors) { - gpr_asprintf(&line_prefix, "bad uri.%s: '", section); - pfx_len = strlen(line_prefix) + pos; - gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text); - gpr_free(line_prefix); - - line_prefix = (char *)gpr_malloc(pfx_len + 1); - memset(line_prefix, ' ', pfx_len); - line_prefix[pfx_len] = 0; - gpr_log(GPR_ERROR, "%s^ here", line_prefix); - gpr_free(line_prefix); - } - - return NULL; -} - -/** Returns a copy of percent decoded \a src[begin, end) */ -static char *decode_and_copy_component(grpc_exec_ctx *exec_ctx, const char *src, - size_t begin, size_t end) { - grpc_slice component = - grpc_slice_from_copied_buffer(src + begin, end - begin); - grpc_slice decoded_component = - grpc_permissive_percent_decode_slice(component); - char *out = grpc_dump_slice(decoded_component, GPR_DUMP_ASCII); - grpc_slice_unref_internal(exec_ctx, component); - grpc_slice_unref_internal(exec_ctx, decoded_component); - return out; -} - -static bool valid_hex(char c) { - return ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) || - ((c >= '0') && (c <= '9')); -} - -/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar - * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent - * sign not followed by two hex digits), NOT_SET is returned. */ -static size_t parse_pchar(const char *uri_text, size_t i) { - /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - / "*" / "+" / "," / ";" / "=" */ - char c = uri_text[i]; - switch (c) { - default: - if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || - ((c >= '0') && (c <= '9'))) { - return 1; - } - break; - case ':': - case '@': - case '-': - case '.': - case '_': - case '~': - case '!': - case '$': - case '&': - case '\'': - case '(': - case ')': - case '*': - case '+': - case ',': - case ';': - case '=': - return 1; - case '%': /* pct-encoded */ - if (valid_hex(uri_text[i + 1]) && valid_hex(uri_text[i + 2])) { - return 2; - } - return NOT_SET; - } - return 0; -} - -/* *( pchar / "?" / "/" ) */ -static int parse_fragment_or_query(const char *uri_text, size_t *i) { - char c; - while ((c = uri_text[*i]) != 0) { - const size_t advance = parse_pchar(uri_text, *i); /* pchar */ - switch (advance) { - case 0: /* uri_text[i] isn't in pchar */ - /* maybe it's ? or / */ - if (uri_text[*i] == '?' || uri_text[*i] == '/') { - (*i)++; - break; - } else { - return 1; - } - GPR_UNREACHABLE_CODE(return 0); - default: - (*i) += advance; - break; - case NOT_SET: /* uri_text[i] introduces an invalid URI */ - return 0; - } - } - /* *i is the first uri_text position past the \a query production, maybe \0 */ - return 1; -} - -static void parse_query_parts(grpc_uri *uri) { - static const char *QUERY_PARTS_SEPARATOR = "&"; - static const char *QUERY_PARTS_VALUE_SEPARATOR = "="; - GPR_ASSERT(uri->query != NULL); - if (uri->query[0] == '\0') { - uri->query_parts = NULL; - uri->query_parts_values = NULL; - uri->num_query_parts = 0; - return; - } - - gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts, - &uri->num_query_parts); - uri->query_parts_values = - (char **)gpr_malloc(uri->num_query_parts * sizeof(char **)); - for (size_t i = 0; i < uri->num_query_parts; i++) { - char **query_param_parts; - size_t num_query_param_parts; - char *full = uri->query_parts[i]; - gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts, - &num_query_param_parts); - GPR_ASSERT(num_query_param_parts > 0); - uri->query_parts[i] = query_param_parts[0]; - if (num_query_param_parts > 1) { - /* TODO(dgq): only the first value after the separator is considered. - * Perhaps all chars after the first separator for the query part should - * be included, even if they include the separator. */ - uri->query_parts_values[i] = query_param_parts[1]; - } else { - uri->query_parts_values[i] = NULL; - } - for (size_t j = 2; j < num_query_param_parts; j++) { - gpr_free(query_param_parts[j]); - } - gpr_free(query_param_parts); - gpr_free(full); - } -} - -grpc_uri *grpc_uri_parse(grpc_exec_ctx *exec_ctx, const char *uri_text, - bool suppress_errors) { - grpc_uri *uri; - size_t scheme_begin = 0; - size_t scheme_end = NOT_SET; - size_t authority_begin = NOT_SET; - size_t authority_end = NOT_SET; - size_t path_begin = NOT_SET; - size_t path_end = NOT_SET; - size_t query_begin = NOT_SET; - size_t query_end = NOT_SET; - size_t fragment_begin = NOT_SET; - size_t fragment_end = NOT_SET; - size_t i; - - for (i = scheme_begin; uri_text[i] != 0; i++) { - if (uri_text[i] == ':') { - scheme_end = i; - break; - } - if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue; - if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue; - if (i != scheme_begin) { - if (uri_text[i] >= '0' && uri_text[i] <= '9') continue; - if (uri_text[i] == '+') continue; - if (uri_text[i] == '-') continue; - if (uri_text[i] == '.') continue; - } - break; - } - if (scheme_end == NOT_SET) { - return bad_uri(uri_text, i, "scheme", suppress_errors); - } - - if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') { - authority_begin = scheme_end + 3; - for (i = authority_begin; uri_text[i] != 0 && authority_end == NOT_SET; - i++) { - if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') { - authority_end = i; - } - } - if (authority_end == NOT_SET && uri_text[i] == 0) { - authority_end = i; - } - if (authority_end == NOT_SET) { - return bad_uri(uri_text, i, "authority", suppress_errors); - } - /* TODO(ctiller): parse the authority correctly */ - path_begin = authority_end; - } else { - path_begin = scheme_end + 1; - } - - for (i = path_begin; uri_text[i] != 0; i++) { - if (uri_text[i] == '?' || uri_text[i] == '#') { - path_end = i; - break; - } - } - if (path_end == NOT_SET && uri_text[i] == 0) { - path_end = i; - } - if (path_end == NOT_SET) { - return bad_uri(uri_text, i, "path", suppress_errors); - } - - if (uri_text[i] == '?') { - query_begin = ++i; - if (!parse_fragment_or_query(uri_text, &i)) { - return bad_uri(uri_text, i, "query", suppress_errors); - } else if (uri_text[i] != 0 && uri_text[i] != '#') { - /* We must be at the end or at the beginning of a fragment */ - return bad_uri(uri_text, i, "query", suppress_errors); - } - query_end = i; - } - if (uri_text[i] == '#') { - fragment_begin = ++i; - if (!parse_fragment_or_query(uri_text, &i)) { - return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors); - } else if (uri_text[i] != 0) { - /* We must be at the end */ - return bad_uri(uri_text, i, "fragment", suppress_errors); - } - fragment_end = i; - } - - uri = (grpc_uri *)gpr_zalloc(sizeof(*uri)); - uri->scheme = - decode_and_copy_component(exec_ctx, uri_text, scheme_begin, scheme_end); - uri->authority = decode_and_copy_component(exec_ctx, uri_text, - authority_begin, authority_end); - uri->path = - decode_and_copy_component(exec_ctx, uri_text, path_begin, path_end); - uri->query = - decode_and_copy_component(exec_ctx, uri_text, query_begin, query_end); - uri->fragment = decode_and_copy_component(exec_ctx, uri_text, fragment_begin, - fragment_end); - parse_query_parts(uri); - - return uri; -} - -const char *grpc_uri_get_query_arg(const grpc_uri *uri, const char *key) { - GPR_ASSERT(key != NULL); - if (key[0] == '\0') return NULL; - - for (size_t i = 0; i < uri->num_query_parts; ++i) { - if (0 == strcmp(key, uri->query_parts[i])) { - return uri->query_parts_values[i]; - } - } - return NULL; -} - -void grpc_uri_destroy(grpc_uri *uri) { - if (!uri) return; - gpr_free(uri->scheme); - gpr_free(uri->authority); - gpr_free(uri->path); - gpr_free(uri->query); - for (size_t i = 0; i < uri->num_query_parts; ++i) { - gpr_free(uri->query_parts[i]); - gpr_free(uri->query_parts_values[i]); - } - gpr_free(uri->query_parts); - gpr_free(uri->query_parts_values); - gpr_free(uri->fragment); - gpr_free(uri); -} diff --git a/src/core/ext/filters/client_channel/uri_parser.cc b/src/core/ext/filters/client_channel/uri_parser.cc new file mode 100644 index 0000000000..fb4fb8e694 --- /dev/null +++ b/src/core/ext/filters/client_channel/uri_parser.cc @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/client_channel/uri_parser.h" + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/slice/percent_encoding.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +/** a size_t default value... maps to all 1's */ +#define NOT_SET (~(size_t)0) + +static grpc_uri *bad_uri(const char *uri_text, size_t pos, const char *section, + bool suppress_errors) { + char *line_prefix; + size_t pfx_len; + + if (!suppress_errors) { + gpr_asprintf(&line_prefix, "bad uri.%s: '", section); + pfx_len = strlen(line_prefix) + pos; + gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text); + gpr_free(line_prefix); + + line_prefix = (char *)gpr_malloc(pfx_len + 1); + memset(line_prefix, ' ', pfx_len); + line_prefix[pfx_len] = 0; + gpr_log(GPR_ERROR, "%s^ here", line_prefix); + gpr_free(line_prefix); + } + + return NULL; +} + +/** Returns a copy of percent decoded \a src[begin, end) */ +static char *decode_and_copy_component(grpc_exec_ctx *exec_ctx, const char *src, + size_t begin, size_t end) { + grpc_slice component = + grpc_slice_from_copied_buffer(src + begin, end - begin); + grpc_slice decoded_component = + grpc_permissive_percent_decode_slice(component); + char *out = grpc_dump_slice(decoded_component, GPR_DUMP_ASCII); + grpc_slice_unref_internal(exec_ctx, component); + grpc_slice_unref_internal(exec_ctx, decoded_component); + return out; +} + +static bool valid_hex(char c) { + return ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) || + ((c >= '0') && (c <= '9')); +} + +/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar + * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent + * sign not followed by two hex digits), NOT_SET is returned. */ +static size_t parse_pchar(const char *uri_text, size_t i) { + /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + * pct-encoded = "%" HEXDIG HEXDIG + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" */ + char c = uri_text[i]; + switch (c) { + default: + if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || + ((c >= '0') && (c <= '9'))) { + return 1; + } + break; + case ':': + case '@': + case '-': + case '.': + case '_': + case '~': + case '!': + case '$': + case '&': + case '\'': + case '(': + case ')': + case '*': + case '+': + case ',': + case ';': + case '=': + return 1; + case '%': /* pct-encoded */ + if (valid_hex(uri_text[i + 1]) && valid_hex(uri_text[i + 2])) { + return 2; + } + return NOT_SET; + } + return 0; +} + +/* *( pchar / "?" / "/" ) */ +static int parse_fragment_or_query(const char *uri_text, size_t *i) { + char c; + while ((c = uri_text[*i]) != 0) { + const size_t advance = parse_pchar(uri_text, *i); /* pchar */ + switch (advance) { + case 0: /* uri_text[i] isn't in pchar */ + /* maybe it's ? or / */ + if (uri_text[*i] == '?' || uri_text[*i] == '/') { + (*i)++; + break; + } else { + return 1; + } + GPR_UNREACHABLE_CODE(return 0); + default: + (*i) += advance; + break; + case NOT_SET: /* uri_text[i] introduces an invalid URI */ + return 0; + } + } + /* *i is the first uri_text position past the \a query production, maybe \0 */ + return 1; +} + +static void parse_query_parts(grpc_uri *uri) { + static const char *QUERY_PARTS_SEPARATOR = "&"; + static const char *QUERY_PARTS_VALUE_SEPARATOR = "="; + GPR_ASSERT(uri->query != NULL); + if (uri->query[0] == '\0') { + uri->query_parts = NULL; + uri->query_parts_values = NULL; + uri->num_query_parts = 0; + return; + } + + gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts, + &uri->num_query_parts); + uri->query_parts_values = + (char **)gpr_malloc(uri->num_query_parts * sizeof(char **)); + for (size_t i = 0; i < uri->num_query_parts; i++) { + char **query_param_parts; + size_t num_query_param_parts; + char *full = uri->query_parts[i]; + gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts, + &num_query_param_parts); + GPR_ASSERT(num_query_param_parts > 0); + uri->query_parts[i] = query_param_parts[0]; + if (num_query_param_parts > 1) { + /* TODO(dgq): only the first value after the separator is considered. + * Perhaps all chars after the first separator for the query part should + * be included, even if they include the separator. */ + uri->query_parts_values[i] = query_param_parts[1]; + } else { + uri->query_parts_values[i] = NULL; + } + for (size_t j = 2; j < num_query_param_parts; j++) { + gpr_free(query_param_parts[j]); + } + gpr_free(query_param_parts); + gpr_free(full); + } +} + +grpc_uri *grpc_uri_parse(grpc_exec_ctx *exec_ctx, const char *uri_text, + bool suppress_errors) { + grpc_uri *uri; + size_t scheme_begin = 0; + size_t scheme_end = NOT_SET; + size_t authority_begin = NOT_SET; + size_t authority_end = NOT_SET; + size_t path_begin = NOT_SET; + size_t path_end = NOT_SET; + size_t query_begin = NOT_SET; + size_t query_end = NOT_SET; + size_t fragment_begin = NOT_SET; + size_t fragment_end = NOT_SET; + size_t i; + + for (i = scheme_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == ':') { + scheme_end = i; + break; + } + if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue; + if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue; + if (i != scheme_begin) { + if (uri_text[i] >= '0' && uri_text[i] <= '9') continue; + if (uri_text[i] == '+') continue; + if (uri_text[i] == '-') continue; + if (uri_text[i] == '.') continue; + } + break; + } + if (scheme_end == NOT_SET) { + return bad_uri(uri_text, i, "scheme", suppress_errors); + } + + if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') { + authority_begin = scheme_end + 3; + for (i = authority_begin; uri_text[i] != 0 && authority_end == NOT_SET; + i++) { + if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') { + authority_end = i; + } + } + if (authority_end == NOT_SET && uri_text[i] == 0) { + authority_end = i; + } + if (authority_end == NOT_SET) { + return bad_uri(uri_text, i, "authority", suppress_errors); + } + /* TODO(ctiller): parse the authority correctly */ + path_begin = authority_end; + } else { + path_begin = scheme_end + 1; + } + + for (i = path_begin; uri_text[i] != 0; i++) { + if (uri_text[i] == '?' || uri_text[i] == '#') { + path_end = i; + break; + } + } + if (path_end == NOT_SET && uri_text[i] == 0) { + path_end = i; + } + if (path_end == NOT_SET) { + return bad_uri(uri_text, i, "path", suppress_errors); + } + + if (uri_text[i] == '?') { + query_begin = ++i; + if (!parse_fragment_or_query(uri_text, &i)) { + return bad_uri(uri_text, i, "query", suppress_errors); + } else if (uri_text[i] != 0 && uri_text[i] != '#') { + /* We must be at the end or at the beginning of a fragment */ + return bad_uri(uri_text, i, "query", suppress_errors); + } + query_end = i; + } + if (uri_text[i] == '#') { + fragment_begin = ++i; + if (!parse_fragment_or_query(uri_text, &i)) { + return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors); + } else if (uri_text[i] != 0) { + /* We must be at the end */ + return bad_uri(uri_text, i, "fragment", suppress_errors); + } + fragment_end = i; + } + + uri = (grpc_uri *)gpr_zalloc(sizeof(*uri)); + uri->scheme = + decode_and_copy_component(exec_ctx, uri_text, scheme_begin, scheme_end); + uri->authority = decode_and_copy_component(exec_ctx, uri_text, + authority_begin, authority_end); + uri->path = + decode_and_copy_component(exec_ctx, uri_text, path_begin, path_end); + uri->query = + decode_and_copy_component(exec_ctx, uri_text, query_begin, query_end); + uri->fragment = decode_and_copy_component(exec_ctx, uri_text, fragment_begin, + fragment_end); + parse_query_parts(uri); + + return uri; +} + +const char *grpc_uri_get_query_arg(const grpc_uri *uri, const char *key) { + GPR_ASSERT(key != NULL); + if (key[0] == '\0') return NULL; + + for (size_t i = 0; i < uri->num_query_parts; ++i) { + if (0 == strcmp(key, uri->query_parts[i])) { + return uri->query_parts_values[i]; + } + } + return NULL; +} + +void grpc_uri_destroy(grpc_uri *uri) { + if (!uri) return; + gpr_free(uri->scheme); + gpr_free(uri->authority); + gpr_free(uri->path); + gpr_free(uri->query); + for (size_t i = 0; i < uri->num_query_parts; ++i) { + gpr_free(uri->query_parts[i]); + gpr_free(uri->query_parts_values[i]); + } + gpr_free(uri->query_parts); + gpr_free(uri->query_parts_values); + gpr_free(uri->fragment); + gpr_free(uri); +} diff --git a/src/core/ext/filters/deadline/deadline_filter.c b/src/core/ext/filters/deadline/deadline_filter.c deleted file mode 100644 index 1aed488077..0000000000 --- a/src/core/ext/filters/deadline/deadline_filter.c +++ /dev/null @@ -1,397 +0,0 @@ -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/ext/filters/deadline/deadline_filter.h" - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/channel_init.h" - -// -// grpc_deadline_state -// - -// The on_complete callback used when sending a cancel_error batch down the -// filter stack. Yields the call combiner when the batch returns. -static void yield_call_combiner(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* ignored) { - grpc_deadline_state* deadline_state = (grpc_deadline_state*)arg; - GRPC_CALL_COMBINER_STOP(exec_ctx, deadline_state->call_combiner, - "got on_complete from cancel_stream batch"); - GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, "deadline_timer"); -} - -// This is called via the call combiner, so access to deadline_state is -// synchronized. -static void send_cancel_op_in_call_combiner(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - grpc_call_element* elem = (grpc_call_element*)arg; - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - grpc_transport_stream_op_batch* batch = grpc_make_transport_stream_op( - GRPC_CLOSURE_INIT(&deadline_state->timer_callback, yield_call_combiner, - deadline_state, grpc_schedule_on_exec_ctx)); - batch->cancel_stream = true; - batch->payload->cancel_stream.cancel_error = GRPC_ERROR_REF(error); - elem->filter->start_transport_stream_op_batch(exec_ctx, elem, batch); -} - -// Timer callback. -static void timer_callback(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - grpc_call_element* elem = (grpc_call_element*)arg; - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - if (error != GRPC_ERROR_CANCELLED) { - error = grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Deadline Exceeded"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_DEADLINE_EXCEEDED); - grpc_call_combiner_cancel(exec_ctx, deadline_state->call_combiner, - GRPC_ERROR_REF(error)); - GRPC_CLOSURE_INIT(&deadline_state->timer_callback, - send_cancel_op_in_call_combiner, elem, - grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START(exec_ctx, deadline_state->call_combiner, - &deadline_state->timer_callback, error, - "deadline exceeded -- sending cancel_stream op"); - } else { - GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, - "deadline_timer"); - } -} - -// Starts the deadline timer. -// This is called via the call combiner, so access to deadline_state is -// synchronized. -static void start_timer_if_needed(grpc_exec_ctx* exec_ctx, - grpc_call_element* elem, - gpr_timespec deadline) { - deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); - if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) == 0) { - return; - } - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - grpc_closure* closure = NULL; - switch (deadline_state->timer_state) { - case GRPC_DEADLINE_STATE_PENDING: - // Note: We do not start the timer if there is already a timer - return; - case GRPC_DEADLINE_STATE_FINISHED: - deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING; - // If we've already created and destroyed a timer, we always create a - // new closure: we have no other guarantee that the inlined closure is - // not in use (it may hold a pending call to timer_callback) - closure = - GRPC_CLOSURE_CREATE(timer_callback, elem, grpc_schedule_on_exec_ctx); - break; - case GRPC_DEADLINE_STATE_INITIAL: - deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING; - closure = - GRPC_CLOSURE_INIT(&deadline_state->timer_callback, timer_callback, - elem, grpc_schedule_on_exec_ctx); - break; - } - GPR_ASSERT(closure != NULL); - GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer"); - grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, closure, - gpr_now(GPR_CLOCK_MONOTONIC)); -} - -// Cancels the deadline timer. -// This is called via the call combiner, so access to deadline_state is -// synchronized. -static void cancel_timer_if_needed(grpc_exec_ctx* exec_ctx, - grpc_deadline_state* deadline_state) { - if (deadline_state->timer_state == GRPC_DEADLINE_STATE_PENDING) { - deadline_state->timer_state = GRPC_DEADLINE_STATE_FINISHED; - grpc_timer_cancel(exec_ctx, &deadline_state->timer); - } else { - // timer was either in STATE_INITAL (nothing to cancel) - // OR in STATE_FINISHED (again nothing to cancel) - } -} - -// Callback run when the call is complete. -static void on_complete(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { - grpc_deadline_state* deadline_state = (grpc_deadline_state*)arg; - cancel_timer_if_needed(exec_ctx, deadline_state); - // Invoke the next callback. - GRPC_CLOSURE_RUN(exec_ctx, deadline_state->next_on_complete, - GRPC_ERROR_REF(error)); -} - -// Inject our own on_complete callback into op. -static void inject_on_complete_cb(grpc_deadline_state* deadline_state, - grpc_transport_stream_op_batch* op) { - deadline_state->next_on_complete = op->on_complete; - GRPC_CLOSURE_INIT(&deadline_state->on_complete, on_complete, deadline_state, - grpc_schedule_on_exec_ctx); - op->on_complete = &deadline_state->on_complete; -} - -// Callback and associated state for starting the timer after call stack -// initialization has been completed. -struct start_timer_after_init_state { - bool in_call_combiner; - grpc_call_element* elem; - gpr_timespec deadline; - grpc_closure closure; -}; -static void start_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - struct start_timer_after_init_state* state = - (struct start_timer_after_init_state*)arg; - grpc_deadline_state* deadline_state = - (grpc_deadline_state*)state->elem->call_data; - if (!state->in_call_combiner) { - // We are initially called without holding the call combiner, so we - // need to bounce ourselves into it. - state->in_call_combiner = true; - GRPC_CALL_COMBINER_START(exec_ctx, deadline_state->call_combiner, - &state->closure, GRPC_ERROR_REF(error), - "scheduling deadline timer"); - return; - } - start_timer_if_needed(exec_ctx, state->elem, state->deadline); - gpr_free(state); - GRPC_CALL_COMBINER_STOP(exec_ctx, deadline_state->call_combiner, - "done scheduling deadline timer"); -} - -void grpc_deadline_state_init(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - grpc_call_stack* call_stack, - grpc_call_combiner* call_combiner, - gpr_timespec deadline) { - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - deadline_state->call_stack = call_stack; - deadline_state->call_combiner = call_combiner; - // Deadline will always be infinite on servers, so the timer will only be - // set on clients with a finite deadline. - deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); - if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) { - // When the deadline passes, we indicate the failure by sending down - // an op with cancel_error set. However, we can't send down any ops - // until after the call stack is fully initialized. If we start the - // timer here, we have no guarantee that the timer won't pop before - // call stack initialization is finished. To avoid that problem, we - // create a closure to start the timer, and we schedule that closure - // to be run after call stack initialization is done. - struct start_timer_after_init_state* state = - (struct start_timer_after_init_state*)gpr_zalloc(sizeof(*state)); - state->elem = elem; - state->deadline = deadline; - GRPC_CLOSURE_INIT(&state->closure, start_timer_after_init, state, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(exec_ctx, &state->closure, GRPC_ERROR_NONE); - } -} - -void grpc_deadline_state_destroy(grpc_exec_ctx* exec_ctx, - grpc_call_element* elem) { - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - cancel_timer_if_needed(exec_ctx, deadline_state); -} - -void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - gpr_timespec new_deadline) { - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - cancel_timer_if_needed(exec_ctx, deadline_state); - start_timer_if_needed(exec_ctx, elem, new_deadline); -} - -void grpc_deadline_state_client_start_transport_stream_op_batch( - grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - grpc_transport_stream_op_batch* op) { - grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; - if (op->cancel_stream) { - cancel_timer_if_needed(exec_ctx, deadline_state); - } else { - // Make sure we know when the call is complete, so that we can cancel - // the timer. - if (op->recv_trailing_metadata) { - inject_on_complete_cb(deadline_state, op); - } - } -} - -// -// filter code -// - -// Constructor for channel_data. Used for both client and server filters. -static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem, - grpc_channel_element_args* args) { - GPR_ASSERT(!args->is_last); - return GRPC_ERROR_NONE; -} - -// Destructor for channel_data. Used for both client and server filters. -static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem) {} - -// Call data used for both client and server filter. -typedef struct base_call_data { - grpc_deadline_state deadline_state; -} base_call_data; - -// Additional call data used only for the server filter. -typedef struct server_call_data { - base_call_data base; // Must be first. - // The closure for receiving initial metadata. - grpc_closure recv_initial_metadata_ready; - // Received initial metadata batch. - grpc_metadata_batch* recv_initial_metadata; - // The original recv_initial_metadata_ready closure, which we chain to - // after our own closure is invoked. - grpc_closure* next_recv_initial_metadata_ready; -} server_call_data; - -// Constructor for call_data. Used for both client and server filters. -static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, - grpc_call_element* elem, - const grpc_call_element_args* args) { - grpc_deadline_state_init(exec_ctx, elem, args->call_stack, - args->call_combiner, args->deadline); - return GRPC_ERROR_NONE; -} - -// Destructor for call_data. Used for both client and server filters. -static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - const grpc_call_final_info* final_info, - grpc_closure* ignored) { - grpc_deadline_state_destroy(exec_ctx, elem); -} - -// Method for starting a call op for client filter. -static void client_start_transport_stream_op_batch( - grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - grpc_transport_stream_op_batch* op) { - grpc_deadline_state_client_start_transport_stream_op_batch(exec_ctx, elem, - op); - // Chain to next filter. - grpc_call_next_op(exec_ctx, elem, op); -} - -// Callback for receiving initial metadata on the server. -static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - grpc_call_element* elem = (grpc_call_element*)arg; - server_call_data* calld = (server_call_data*)elem->call_data; - // Get deadline from metadata and start the timer if needed. - start_timer_if_needed(exec_ctx, elem, calld->recv_initial_metadata->deadline); - // Invoke the next callback. - calld->next_recv_initial_metadata_ready->cb( - exec_ctx, calld->next_recv_initial_metadata_ready->cb_arg, error); -} - -// Method for starting a call op for server filter. -static void server_start_transport_stream_op_batch( - grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - grpc_transport_stream_op_batch* op) { - server_call_data* calld = (server_call_data*)elem->call_data; - if (op->cancel_stream) { - cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state); - } else { - // If we're receiving initial metadata, we need to get the deadline - // from the recv_initial_metadata_ready callback. So we inject our - // own callback into that hook. - if (op->recv_initial_metadata) { - calld->next_recv_initial_metadata_ready = - op->payload->recv_initial_metadata.recv_initial_metadata_ready; - calld->recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata; - GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, - recv_initial_metadata_ready, elem, - grpc_schedule_on_exec_ctx); - op->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->recv_initial_metadata_ready; - } - // Make sure we know when the call is complete, so that we can cancel - // the timer. - // Note that we trigger this on recv_trailing_metadata, even though - // the client never sends trailing metadata, because this is the - // hook that tells us when the call is complete on the server side. - if (op->recv_trailing_metadata) { - inject_on_complete_cb(&calld->base.deadline_state, op); - } - } - // Chain to next filter. - grpc_call_next_op(exec_ctx, elem, op); -} - -const grpc_channel_filter grpc_client_deadline_filter = { - client_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(base_call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - 0, // sizeof(channel_data) - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "deadline", -}; - -const grpc_channel_filter grpc_server_deadline_filter = { - server_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(server_call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - 0, // sizeof(channel_data) - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "deadline", -}; - -bool grpc_deadline_checking_enabled(const grpc_channel_args* channel_args) { - return grpc_channel_arg_get_bool( - grpc_channel_args_find(channel_args, GRPC_ARG_ENABLE_DEADLINE_CHECKS), - !grpc_channel_args_want_minimal_stack(channel_args)); -} - -static bool maybe_add_deadline_filter(grpc_exec_ctx* exec_ctx, - grpc_channel_stack_builder* builder, - void* arg) { - return grpc_deadline_checking_enabled( - grpc_channel_stack_builder_get_channel_arguments(builder)) - ? grpc_channel_stack_builder_prepend_filter( - builder, (const grpc_channel_filter*)arg, NULL, NULL) - : true; -} - -void grpc_deadline_filter_init(void) { - grpc_channel_init_register_stage( - GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_deadline_filter, (void*)&grpc_client_deadline_filter); - grpc_channel_init_register_stage( - GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_deadline_filter, (void*)&grpc_server_deadline_filter); -} - -void grpc_deadline_filter_shutdown(void) {} diff --git a/src/core/ext/filters/deadline/deadline_filter.cc b/src/core/ext/filters/deadline/deadline_filter.cc new file mode 100644 index 0000000000..866ce46acf --- /dev/null +++ b/src/core/ext/filters/deadline/deadline_filter.cc @@ -0,0 +1,397 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/ext/filters/deadline/deadline_filter.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/channel_init.h" + +// +// grpc_deadline_state +// + +// The on_complete callback used when sending a cancel_error batch down the +// filter stack. Yields the call combiner when the batch returns. +static void yield_call_combiner(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* ignored) { + grpc_deadline_state* deadline_state = (grpc_deadline_state*)arg; + GRPC_CALL_COMBINER_STOP(exec_ctx, deadline_state->call_combiner, + "got on_complete from cancel_stream batch"); + GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, "deadline_timer"); +} + +// This is called via the call combiner, so access to deadline_state is +// synchronized. +static void send_cancel_op_in_call_combiner(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + grpc_call_element* elem = (grpc_call_element*)arg; + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + grpc_transport_stream_op_batch* batch = grpc_make_transport_stream_op( + GRPC_CLOSURE_INIT(&deadline_state->timer_callback, yield_call_combiner, + deadline_state, grpc_schedule_on_exec_ctx)); + batch->cancel_stream = true; + batch->payload->cancel_stream.cancel_error = GRPC_ERROR_REF(error); + elem->filter->start_transport_stream_op_batch(exec_ctx, elem, batch); +} + +// Timer callback. +static void timer_callback(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + grpc_call_element* elem = (grpc_call_element*)arg; + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + if (error != GRPC_ERROR_CANCELLED) { + error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Deadline Exceeded"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_DEADLINE_EXCEEDED); + grpc_call_combiner_cancel(exec_ctx, deadline_state->call_combiner, + GRPC_ERROR_REF(error)); + GRPC_CLOSURE_INIT(&deadline_state->timer_callback, + send_cancel_op_in_call_combiner, elem, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(exec_ctx, deadline_state->call_combiner, + &deadline_state->timer_callback, error, + "deadline exceeded -- sending cancel_stream op"); + } else { + GRPC_CALL_STACK_UNREF(exec_ctx, deadline_state->call_stack, + "deadline_timer"); + } +} + +// Starts the deadline timer. +// This is called via the call combiner, so access to deadline_state is +// synchronized. +static void start_timer_if_needed(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem, + gpr_timespec deadline) { + deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) == 0) { + return; + } + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + grpc_closure* closure = NULL; + switch (deadline_state->timer_state) { + case GRPC_DEADLINE_STATE_PENDING: + // Note: We do not start the timer if there is already a timer + return; + case GRPC_DEADLINE_STATE_FINISHED: + deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING; + // If we've already created and destroyed a timer, we always create a + // new closure: we have no other guarantee that the inlined closure is + // not in use (it may hold a pending call to timer_callback) + closure = + GRPC_CLOSURE_CREATE(timer_callback, elem, grpc_schedule_on_exec_ctx); + break; + case GRPC_DEADLINE_STATE_INITIAL: + deadline_state->timer_state = GRPC_DEADLINE_STATE_PENDING; + closure = + GRPC_CLOSURE_INIT(&deadline_state->timer_callback, timer_callback, + elem, grpc_schedule_on_exec_ctx); + break; + } + GPR_ASSERT(closure != NULL); + GRPC_CALL_STACK_REF(deadline_state->call_stack, "deadline_timer"); + grpc_timer_init(exec_ctx, &deadline_state->timer, deadline, closure, + gpr_now(GPR_CLOCK_MONOTONIC)); +} + +// Cancels the deadline timer. +// This is called via the call combiner, so access to deadline_state is +// synchronized. +static void cancel_timer_if_needed(grpc_exec_ctx* exec_ctx, + grpc_deadline_state* deadline_state) { + if (deadline_state->timer_state == GRPC_DEADLINE_STATE_PENDING) { + deadline_state->timer_state = GRPC_DEADLINE_STATE_FINISHED; + grpc_timer_cancel(exec_ctx, &deadline_state->timer); + } else { + // timer was either in STATE_INITAL (nothing to cancel) + // OR in STATE_FINISHED (again nothing to cancel) + } +} + +// Callback run when the call is complete. +static void on_complete(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { + grpc_deadline_state* deadline_state = (grpc_deadline_state*)arg; + cancel_timer_if_needed(exec_ctx, deadline_state); + // Invoke the next callback. + GRPC_CLOSURE_RUN(exec_ctx, deadline_state->next_on_complete, + GRPC_ERROR_REF(error)); +} + +// Inject our own on_complete callback into op. +static void inject_on_complete_cb(grpc_deadline_state* deadline_state, + grpc_transport_stream_op_batch* op) { + deadline_state->next_on_complete = op->on_complete; + GRPC_CLOSURE_INIT(&deadline_state->on_complete, on_complete, deadline_state, + grpc_schedule_on_exec_ctx); + op->on_complete = &deadline_state->on_complete; +} + +// Callback and associated state for starting the timer after call stack +// initialization has been completed. +struct start_timer_after_init_state { + bool in_call_combiner; + grpc_call_element* elem; + gpr_timespec deadline; + grpc_closure closure; +}; +static void start_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + struct start_timer_after_init_state* state = + (struct start_timer_after_init_state*)arg; + grpc_deadline_state* deadline_state = + (grpc_deadline_state*)state->elem->call_data; + if (!state->in_call_combiner) { + // We are initially called without holding the call combiner, so we + // need to bounce ourselves into it. + state->in_call_combiner = true; + GRPC_CALL_COMBINER_START(exec_ctx, deadline_state->call_combiner, + &state->closure, GRPC_ERROR_REF(error), + "scheduling deadline timer"); + return; + } + start_timer_if_needed(exec_ctx, state->elem, state->deadline); + gpr_free(state); + GRPC_CALL_COMBINER_STOP(exec_ctx, deadline_state->call_combiner, + "done scheduling deadline timer"); +} + +void grpc_deadline_state_init(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_call_stack* call_stack, + grpc_call_combiner* call_combiner, + gpr_timespec deadline) { + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + deadline_state->call_stack = call_stack; + deadline_state->call_combiner = call_combiner; + // Deadline will always be infinite on servers, so the timer will only be + // set on clients with a finite deadline. + deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + if (gpr_time_cmp(deadline, gpr_inf_future(GPR_CLOCK_MONOTONIC)) != 0) { + // When the deadline passes, we indicate the failure by sending down + // an op with cancel_error set. However, we can't send down any ops + // until after the call stack is fully initialized. If we start the + // timer here, we have no guarantee that the timer won't pop before + // call stack initialization is finished. To avoid that problem, we + // create a closure to start the timer, and we schedule that closure + // to be run after call stack initialization is done. + struct start_timer_after_init_state* state = + (struct start_timer_after_init_state*)gpr_zalloc(sizeof(*state)); + state->elem = elem; + state->deadline = deadline; + GRPC_CLOSURE_INIT(&state->closure, start_timer_after_init, state, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(exec_ctx, &state->closure, GRPC_ERROR_NONE); + } +} + +void grpc_deadline_state_destroy(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem) { + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + cancel_timer_if_needed(exec_ctx, deadline_state); +} + +void grpc_deadline_state_reset(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + gpr_timespec new_deadline) { + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + cancel_timer_if_needed(exec_ctx, deadline_state); + start_timer_if_needed(exec_ctx, elem, new_deadline); +} + +void grpc_deadline_state_client_start_transport_stream_op_batch( + grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + grpc_deadline_state* deadline_state = (grpc_deadline_state*)elem->call_data; + if (op->cancel_stream) { + cancel_timer_if_needed(exec_ctx, deadline_state); + } else { + // Make sure we know when the call is complete, so that we can cancel + // the timer. + if (op->recv_trailing_metadata) { + inject_on_complete_cb(deadline_state, op); + } + } +} + +// +// filter code +// + +// Constructor for channel_data. Used for both client and server filters. +static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem, + grpc_channel_element_args* args) { + GPR_ASSERT(!args->is_last); + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. Used for both client and server filters. +static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem) {} + +// Call data used for both client and server filter. +typedef struct base_call_data { + grpc_deadline_state deadline_state; +} base_call_data; + +// Additional call data used only for the server filter. +typedef struct server_call_data { + base_call_data base; // Must be first. + // The closure for receiving initial metadata. + grpc_closure recv_initial_metadata_ready; + // Received initial metadata batch. + grpc_metadata_batch* recv_initial_metadata; + // The original recv_initial_metadata_ready closure, which we chain to + // after our own closure is invoked. + grpc_closure* next_recv_initial_metadata_ready; +} server_call_data; + +// Constructor for call_data. Used for both client and server filters. +static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem, + const grpc_call_element_args* args) { + grpc_deadline_state_init(exec_ctx, elem, args->call_stack, + args->call_combiner, args->deadline); + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. Used for both client and server filters. +static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + grpc_deadline_state_destroy(exec_ctx, elem); +} + +// Method for starting a call op for client filter. +static void client_start_transport_stream_op_batch( + grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + grpc_deadline_state_client_start_transport_stream_op_batch(exec_ctx, elem, + op); + // Chain to next filter. + grpc_call_next_op(exec_ctx, elem, op); +} + +// Callback for receiving initial metadata on the server. +static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + grpc_call_element* elem = (grpc_call_element*)arg; + server_call_data* calld = (server_call_data*)elem->call_data; + // Get deadline from metadata and start the timer if needed. + start_timer_if_needed(exec_ctx, elem, calld->recv_initial_metadata->deadline); + // Invoke the next callback. + calld->next_recv_initial_metadata_ready->cb( + exec_ctx, calld->next_recv_initial_metadata_ready->cb_arg, error); +} + +// Method for starting a call op for server filter. +static void server_start_transport_stream_op_batch( + grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + server_call_data* calld = (server_call_data*)elem->call_data; + if (op->cancel_stream) { + cancel_timer_if_needed(exec_ctx, &calld->base.deadline_state); + } else { + // If we're receiving initial metadata, we need to get the deadline + // from the recv_initial_metadata_ready callback. So we inject our + // own callback into that hook. + if (op->recv_initial_metadata) { + calld->next_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + // Make sure we know when the call is complete, so that we can cancel + // the timer. + // Note that we trigger this on recv_trailing_metadata, even though + // the client never sends trailing metadata, because this is the + // hook that tells us when the call is complete on the server side. + if (op->recv_trailing_metadata) { + inject_on_complete_cb(&calld->base.deadline_state, op); + } + } + // Chain to next filter. + grpc_call_next_op(exec_ctx, elem, op); +} + +const grpc_channel_filter grpc_client_deadline_filter = { + client_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(base_call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, // sizeof(channel_data) + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "deadline", +}; + +const grpc_channel_filter grpc_server_deadline_filter = { + server_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(server_call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, // sizeof(channel_data) + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "deadline", +}; + +bool grpc_deadline_checking_enabled(const grpc_channel_args* channel_args) { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(channel_args, GRPC_ARG_ENABLE_DEADLINE_CHECKS), + !grpc_channel_args_want_minimal_stack(channel_args)); +} + +static bool maybe_add_deadline_filter(grpc_exec_ctx* exec_ctx, + grpc_channel_stack_builder* builder, + void* arg) { + return grpc_deadline_checking_enabled( + grpc_channel_stack_builder_get_channel_arguments(builder)) + ? grpc_channel_stack_builder_prepend_filter( + builder, (const grpc_channel_filter*)arg, NULL, NULL) + : true; +} + +extern "C" void grpc_deadline_filter_init(void) { + grpc_channel_init_register_stage( + GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_deadline_filter, (void*)&grpc_client_deadline_filter); + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_deadline_filter, (void*)&grpc_server_deadline_filter); +} + +extern "C" void grpc_deadline_filter_shutdown(void) {} diff --git a/src/core/ext/filters/http/client/http_client_filter.c b/src/core/ext/filters/http/client/http_client_filter.c deleted file mode 100644 index 6208089f2e..0000000000 --- a/src/core/ext/filters/http/client/http_client_filter.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/http/client/http_client_filter.h" -#include -#include -#include -#include -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/b64.h" -#include "src/core/lib/slice/percent_encoding.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/transport_impl.h" - -#define EXPECTED_CONTENT_TYPE "application/grpc" -#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 - -/* default maximum size of payload eligable for GET request */ -static const size_t kMaxPayloadSizeForGet = 2048; - -typedef struct call_data { - grpc_call_combiner *call_combiner; - // State for handling send_initial_metadata ops. - grpc_linked_mdelem method; - grpc_linked_mdelem scheme; - grpc_linked_mdelem authority; - grpc_linked_mdelem te_trailers; - grpc_linked_mdelem content_type; - grpc_linked_mdelem user_agent; - // State for handling recv_initial_metadata ops. - grpc_metadata_batch *recv_initial_metadata; - grpc_closure *original_recv_initial_metadata_ready; - grpc_closure recv_initial_metadata_ready; - // State for handling recv_trailing_metadata ops. - grpc_metadata_batch *recv_trailing_metadata; - grpc_closure *original_recv_trailing_metadata_on_complete; - grpc_closure recv_trailing_metadata_on_complete; - // State for handling send_message ops. - grpc_transport_stream_op_batch *send_message_batch; - size_t send_message_bytes_read; - grpc_byte_stream_cache send_message_cache; - grpc_caching_byte_stream send_message_caching_stream; - grpc_closure on_send_message_next_done; - grpc_closure *original_send_message_on_complete; - grpc_closure send_message_on_complete; -} call_data; - -typedef struct channel_data { - grpc_mdelem static_scheme; - grpc_mdelem user_agent; - size_t max_payload_size_for_get; -} channel_data; - -static grpc_error *client_filter_incoming_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_metadata_batch *b) { - if (b->idx.named.status != NULL) { - if (grpc_mdelem_eq(b->idx.named.status->md, GRPC_MDELEM_STATUS_200)) { - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.status); - } else { - char *val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md), - GPR_DUMP_ASCII); - char *msg; - gpr_asprintf(&msg, "Received http2 header with status: %s", val); - grpc_error *e = grpc_error_set_str( - grpc_error_set_int( - grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Received http2 :status header with non-200 OK status"), - GRPC_ERROR_STR_VALUE, grpc_slice_from_copied_string(val)), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED), - GRPC_ERROR_STR_GRPC_MESSAGE, grpc_slice_from_copied_string(msg)); - gpr_free(val); - gpr_free(msg); - return e; - } - } - - if (b->idx.named.grpc_message != NULL) { - grpc_slice pct_decoded_msg = grpc_permissive_percent_decode_slice( - GRPC_MDVALUE(b->idx.named.grpc_message->md)); - if (grpc_slice_is_equivalent(pct_decoded_msg, - GRPC_MDVALUE(b->idx.named.grpc_message->md))) { - grpc_slice_unref_internal(exec_ctx, pct_decoded_msg); - } else { - grpc_metadata_batch_set_value(exec_ctx, b->idx.named.grpc_message, - pct_decoded_msg); - } - } - - if (b->idx.named.content_type != NULL) { - if (!grpc_mdelem_eq(b->idx.named.content_type->md, - GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) { - if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md), - EXPECTED_CONTENT_TYPE, - EXPECTED_CONTENT_TYPE_LENGTH) && - (GRPC_SLICE_START_PTR(GRPC_MDVALUE( - b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == - '+' || - GRPC_SLICE_START_PTR(GRPC_MDVALUE( - b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == - ';')) { - /* Although the C implementation doesn't (currently) generate them, - any custom +-suffix is explicitly valid. */ - /* TODO(klempner): We should consider preallocating common values such - as +proto or +json, or at least stashing them if we see them. */ - /* TODO(klempner): Should we be surfacing this to application code? */ - } else { - /* TODO(klempner): We're currently allowing this, but we shouldn't - see it without a proxy so log for now. */ - char *val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md), - GPR_DUMP_ASCII); - gpr_log(GPR_INFO, "Unexpected content-type '%s'", val); - gpr_free(val); - } - } - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.content_type); - } - - return GRPC_ERROR_NONE; -} - -static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, - void *user_data, grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - if (error == GRPC_ERROR_NONE) { - error = client_filter_incoming_metadata(exec_ctx, elem, - calld->recv_initial_metadata); - } else { - GRPC_ERROR_REF(error); - } - GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_initial_metadata_ready, - error); -} - -static void recv_trailing_metadata_on_complete(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - if (error == GRPC_ERROR_NONE) { - error = client_filter_incoming_metadata(exec_ctx, elem, - calld->recv_trailing_metadata); - } else { - GRPC_ERROR_REF(error); - } - GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_trailing_metadata_on_complete, - error); -} - -static void send_message_on_complete(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - grpc_byte_stream_cache_destroy(exec_ctx, &calld->send_message_cache); - GRPC_CLOSURE_RUN(exec_ctx, calld->original_send_message_on_complete, - GRPC_ERROR_REF(error)); -} - -// Pulls a slice from the send_message byte stream, updating -// calld->send_message_bytes_read. -static grpc_error *pull_slice_from_send_message(grpc_exec_ctx *exec_ctx, - call_data *calld) { - grpc_slice incoming_slice; - grpc_error *error = grpc_byte_stream_pull( - exec_ctx, &calld->send_message_caching_stream.base, &incoming_slice); - if (error == GRPC_ERROR_NONE) { - calld->send_message_bytes_read += GRPC_SLICE_LENGTH(incoming_slice); - grpc_slice_unref_internal(exec_ctx, incoming_slice); - } - return error; -} - -// Reads as many slices as possible from the send_message byte stream. -// Upon successful return, if calld->send_message_bytes_read == -// calld->send_message_caching_stream.base.length, then we have completed -// reading from the byte stream; otherwise, an async read has been dispatched -// and on_send_message_next_done() will be invoked when it is complete. -static grpc_error *read_all_available_send_message_data(grpc_exec_ctx *exec_ctx, - call_data *calld) { - while (grpc_byte_stream_next(exec_ctx, - &calld->send_message_caching_stream.base, - ~(size_t)0, &calld->on_send_message_next_done)) { - grpc_error *error = pull_slice_from_send_message(exec_ctx, calld); - if (error != GRPC_ERROR_NONE) return error; - if (calld->send_message_bytes_read == - calld->send_message_caching_stream.base.length) { - break; - } - } - return GRPC_ERROR_NONE; -} - -// Async callback for grpc_byte_stream_next(). -static void on_send_message_next_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - if (error != GRPC_ERROR_NONE) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, calld->send_message_batch, error, calld->call_combiner); - return; - } - error = pull_slice_from_send_message(exec_ctx, calld); - if (error != GRPC_ERROR_NONE) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, calld->send_message_batch, error, calld->call_combiner); - return; - } - // There may or may not be more to read, but we don't care. If we got - // here, then we know that all of the data was not available - // synchronously, so we were not able to do a cached call. Instead, - // we just reset the byte stream and then send down the batch as-is. - grpc_caching_byte_stream_reset(&calld->send_message_caching_stream); - grpc_call_next_op(exec_ctx, elem, calld->send_message_batch); -} - -static char *slice_buffer_to_string(grpc_slice_buffer *slice_buffer) { - char *payload_bytes = (char *)gpr_malloc(slice_buffer->length + 1); - size_t offset = 0; - for (size_t i = 0; i < slice_buffer->count; ++i) { - memcpy(payload_bytes + offset, - GRPC_SLICE_START_PTR(slice_buffer->slices[i]), - GRPC_SLICE_LENGTH(slice_buffer->slices[i])); - offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]); - } - *(payload_bytes + offset) = '\0'; - return payload_bytes; -} - -// Modifies the path entry in the batch's send_initial_metadata to -// append the base64-encoded query for a GET request. -static grpc_error *update_path_for_get(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - grpc_slice path_slice = - GRPC_MDVALUE(batch->payload->send_initial_metadata.send_initial_metadata - ->idx.named.path->md); - /* sum up individual component's lengths and allocate enough memory to - * hold combined path+query */ - size_t estimated_len = GRPC_SLICE_LENGTH(path_slice); - estimated_len++; /* for the '?' */ - estimated_len += grpc_base64_estimate_encoded_size( - batch->payload->send_message.send_message->length, true /* url_safe */, - false /* multi_line */); - grpc_slice path_with_query_slice = GRPC_SLICE_MALLOC(estimated_len); - /* memcopy individual pieces into this slice */ - char *write_ptr = (char *)GRPC_SLICE_START_PTR(path_with_query_slice); - char *original_path = (char *)GRPC_SLICE_START_PTR(path_slice); - memcpy(write_ptr, original_path, GRPC_SLICE_LENGTH(path_slice)); - write_ptr += GRPC_SLICE_LENGTH(path_slice); - *write_ptr++ = '?'; - char *payload_bytes = - slice_buffer_to_string(&calld->send_message_cache.cache_buffer); - grpc_base64_encode_core((char *)write_ptr, payload_bytes, - batch->payload->send_message.send_message->length, - true /* url_safe */, false /* multi_line */); - gpr_free(payload_bytes); - /* remove trailing unused memory and add trailing 0 to terminate string */ - char *t = (char *)GRPC_SLICE_START_PTR(path_with_query_slice); - /* safe to use strlen since base64_encode will always add '\0' */ - path_with_query_slice = - grpc_slice_sub_no_ref(path_with_query_slice, 0, strlen(t)); - /* substitute previous path with the new path+query */ - grpc_mdelem mdelem_path_and_query = - grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_PATH, path_with_query_slice); - grpc_metadata_batch *b = - batch->payload->send_initial_metadata.send_initial_metadata; - return grpc_metadata_batch_substitute(exec_ctx, b, b->idx.named.path, - mdelem_path_and_query); -} - -static void remove_if_present(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_metadata_batch_callouts_index idx) { - if (batch->idx.array[idx] != NULL) { - grpc_metadata_batch_remove(exec_ctx, batch, batch->idx.array[idx]); - } -} - -static void hc_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - channel_data *channeld = (channel_data *)elem->channel_data; - GPR_TIMER_BEGIN("hc_start_transport_stream_op_batch", 0); - - if (batch->recv_initial_metadata) { - /* substitute our callback for the higher callback */ - calld->recv_initial_metadata = - batch->payload->recv_initial_metadata.recv_initial_metadata; - calld->original_recv_initial_metadata_ready = - batch->payload->recv_initial_metadata.recv_initial_metadata_ready; - batch->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->recv_initial_metadata_ready; - } - - if (batch->recv_trailing_metadata) { - /* substitute our callback for the higher callback */ - calld->recv_trailing_metadata = - batch->payload->recv_trailing_metadata.recv_trailing_metadata; - calld->original_recv_trailing_metadata_on_complete = batch->on_complete; - batch->on_complete = &calld->recv_trailing_metadata_on_complete; - } - - grpc_error *error = GRPC_ERROR_NONE; - bool batch_will_be_handled_asynchronously = false; - if (batch->send_initial_metadata) { - // Decide which HTTP VERB to use. We use GET if the request is marked - // cacheable, and the operation contains both initial metadata and send - // message, and the payload is below the size threshold, and all the data - // for this request is immediately available. - grpc_mdelem method = GRPC_MDELEM_METHOD_POST; - if (batch->send_message && - (batch->payload->send_initial_metadata.send_initial_metadata_flags & - GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) && - batch->payload->send_message.send_message->length < - channeld->max_payload_size_for_get) { - calld->send_message_bytes_read = 0; - grpc_byte_stream_cache_init(&calld->send_message_cache, - batch->payload->send_message.send_message); - grpc_caching_byte_stream_init(&calld->send_message_caching_stream, - &calld->send_message_cache); - batch->payload->send_message.send_message = - &calld->send_message_caching_stream.base; - calld->original_send_message_on_complete = batch->on_complete; - batch->on_complete = &calld->send_message_on_complete; - calld->send_message_batch = batch; - error = read_all_available_send_message_data(exec_ctx, calld); - if (error != GRPC_ERROR_NONE) goto done; - // If all the data has been read, then we can use GET. - if (calld->send_message_bytes_read == - calld->send_message_caching_stream.base.length) { - method = GRPC_MDELEM_METHOD_GET; - error = update_path_for_get(exec_ctx, elem, batch); - if (error != GRPC_ERROR_NONE) goto done; - batch->send_message = false; - grpc_byte_stream_destroy(exec_ctx, - &calld->send_message_caching_stream.base); - } else { - // Not all data is available. The batch will be sent down - // asynchronously in on_send_message_next_done(). - batch_will_be_handled_asynchronously = true; - // Fall back to POST. - gpr_log(GPR_DEBUG, - "Request is marked Cacheable but not all data is available. " - "Falling back to POST"); - } - } else if (batch->payload->send_initial_metadata - .send_initial_metadata_flags & - GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) { - method = GRPC_MDELEM_METHOD_PUT; - } - - remove_if_present( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - GRPC_BATCH_METHOD); - remove_if_present( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - GRPC_BATCH_SCHEME); - remove_if_present( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - GRPC_BATCH_TE); - remove_if_present( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - GRPC_BATCH_CONTENT_TYPE); - remove_if_present( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - GRPC_BATCH_USER_AGENT); - - /* Send : prefixed headers, which have to be before any application - layer headers. */ - error = grpc_metadata_batch_add_head( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - &calld->method, method); - if (error != GRPC_ERROR_NONE) goto done; - error = grpc_metadata_batch_add_head( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - &calld->scheme, channeld->static_scheme); - if (error != GRPC_ERROR_NONE) goto done; - error = grpc_metadata_batch_add_tail( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - &calld->te_trailers, GRPC_MDELEM_TE_TRAILERS); - if (error != GRPC_ERROR_NONE) goto done; - error = grpc_metadata_batch_add_tail( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - &calld->content_type, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC); - if (error != GRPC_ERROR_NONE) goto done; - error = grpc_metadata_batch_add_tail( - exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, - &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent)); - if (error != GRPC_ERROR_NONE) goto done; - } - -done: - if (error != GRPC_ERROR_NONE) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, calld->send_message_batch, error, calld->call_combiner); - } else if (!batch_will_be_handled_asynchronously) { - grpc_call_next_op(exec_ctx, elem, batch); - } - GPR_TIMER_END("hc_start_transport_stream_op_batch", 0); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - calld->call_combiner = args->call_combiner; - GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, - recv_initial_metadata_ready, elem, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_on_complete, - recv_trailing_metadata_on_complete, elem, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete, - elem, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->on_send_message_next_done, - on_send_message_next_done, elem, grpc_schedule_on_exec_ctx); - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) {} - -static grpc_mdelem scheme_from_args(const grpc_channel_args *args) { - unsigned i; - size_t j; - grpc_mdelem valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP, - GRPC_MDELEM_SCHEME_HTTPS}; - if (args != NULL) { - for (i = 0; i < args->num_args; ++i) { - if (args->args[i].type == GRPC_ARG_STRING && - strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) { - for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) { - if (0 == grpc_slice_str_cmp(GRPC_MDVALUE(valid_schemes[j]), - args->args[i].value.string)) { - return valid_schemes[j]; - } - } - } - } - } - return GRPC_MDELEM_SCHEME_HTTP; -} - -static size_t max_payload_size_from_args(const grpc_channel_args *args) { - if (args != NULL) { - for (size_t i = 0; i < args->num_args; ++i) { - if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) { - if (args->args[i].type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s: must be an integer", - GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET); - } else { - return (size_t)args->args[i].value.integer; - } - } - } - } - return kMaxPayloadSizeForGet; -} - -static grpc_slice user_agent_from_args(const grpc_channel_args *args, - const char *transport_name) { - gpr_strvec v; - size_t i; - int is_first = 1; - char *tmp; - grpc_slice result; - - gpr_strvec_init(&v); - - for (i = 0; args && i < args->num_args; i++) { - if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) { - if (args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "Channel argument '%s' should be a string", - GRPC_ARG_PRIMARY_USER_AGENT_STRING); - } else { - if (!is_first) gpr_strvec_add(&v, gpr_strdup(" ")); - is_first = 0; - gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string)); - } - } - } - - gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s; %s)", is_first ? "" : " ", - grpc_version_string(), GPR_PLATFORM_STRING, transport_name, - grpc_g_stands_for()); - is_first = 0; - gpr_strvec_add(&v, tmp); - - for (i = 0; args && i < args->num_args; i++) { - if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) { - if (args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "Channel argument '%s' should be a string", - GRPC_ARG_SECONDARY_USER_AGENT_STRING); - } else { - if (!is_first) gpr_strvec_add(&v, gpr_strdup(" ")); - is_first = 0; - gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string)); - } - } - } - - tmp = gpr_strvec_flatten(&v, NULL); - gpr_strvec_destroy(&v); - result = grpc_slice_intern(grpc_slice_from_static_string(tmp)); - gpr_free(tmp); - - return result; -} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = (channel_data *)elem->channel_data; - GPR_ASSERT(!args->is_last); - GPR_ASSERT(args->optional_transport != NULL); - chand->static_scheme = scheme_from_args(args->channel_args); - chand->max_payload_size_for_get = - max_payload_size_from_args(args->channel_args); - chand->user_agent = grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_USER_AGENT, - user_agent_from_args(args->channel_args, - args->optional_transport->vtable->name)); - return GRPC_ERROR_NONE; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - GRPC_MDELEM_UNREF(exec_ctx, chand->user_agent); -} - -const grpc_channel_filter grpc_http_client_filter = { - hc_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "http-client"}; diff --git a/src/core/ext/filters/http/client/http_client_filter.cc b/src/core/ext/filters/http/client/http_client_filter.cc new file mode 100644 index 0000000000..6208089f2e --- /dev/null +++ b/src/core/ext/filters/http/client/http_client_filter.cc @@ -0,0 +1,570 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/http/client/http_client_filter.h" +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/percent_encoding.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/transport_impl.h" + +#define EXPECTED_CONTENT_TYPE "application/grpc" +#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 + +/* default maximum size of payload eligable for GET request */ +static const size_t kMaxPayloadSizeForGet = 2048; + +typedef struct call_data { + grpc_call_combiner *call_combiner; + // State for handling send_initial_metadata ops. + grpc_linked_mdelem method; + grpc_linked_mdelem scheme; + grpc_linked_mdelem authority; + grpc_linked_mdelem te_trailers; + grpc_linked_mdelem content_type; + grpc_linked_mdelem user_agent; + // State for handling recv_initial_metadata ops. + grpc_metadata_batch *recv_initial_metadata; + grpc_closure *original_recv_initial_metadata_ready; + grpc_closure recv_initial_metadata_ready; + // State for handling recv_trailing_metadata ops. + grpc_metadata_batch *recv_trailing_metadata; + grpc_closure *original_recv_trailing_metadata_on_complete; + grpc_closure recv_trailing_metadata_on_complete; + // State for handling send_message ops. + grpc_transport_stream_op_batch *send_message_batch; + size_t send_message_bytes_read; + grpc_byte_stream_cache send_message_cache; + grpc_caching_byte_stream send_message_caching_stream; + grpc_closure on_send_message_next_done; + grpc_closure *original_send_message_on_complete; + grpc_closure send_message_on_complete; +} call_data; + +typedef struct channel_data { + grpc_mdelem static_scheme; + grpc_mdelem user_agent; + size_t max_payload_size_for_get; +} channel_data; + +static grpc_error *client_filter_incoming_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_metadata_batch *b) { + if (b->idx.named.status != NULL) { + if (grpc_mdelem_eq(b->idx.named.status->md, GRPC_MDELEM_STATUS_200)) { + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.status); + } else { + char *val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.status->md), + GPR_DUMP_ASCII); + char *msg; + gpr_asprintf(&msg, "Received http2 header with status: %s", val); + grpc_error *e = grpc_error_set_str( + grpc_error_set_int( + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Received http2 :status header with non-200 OK status"), + GRPC_ERROR_STR_VALUE, grpc_slice_from_copied_string(val)), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED), + GRPC_ERROR_STR_GRPC_MESSAGE, grpc_slice_from_copied_string(msg)); + gpr_free(val); + gpr_free(msg); + return e; + } + } + + if (b->idx.named.grpc_message != NULL) { + grpc_slice pct_decoded_msg = grpc_permissive_percent_decode_slice( + GRPC_MDVALUE(b->idx.named.grpc_message->md)); + if (grpc_slice_is_equivalent(pct_decoded_msg, + GRPC_MDVALUE(b->idx.named.grpc_message->md))) { + grpc_slice_unref_internal(exec_ctx, pct_decoded_msg); + } else { + grpc_metadata_batch_set_value(exec_ctx, b->idx.named.grpc_message, + pct_decoded_msg); + } + } + + if (b->idx.named.content_type != NULL) { + if (!grpc_mdelem_eq(b->idx.named.content_type->md, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) { + if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md), + EXPECTED_CONTENT_TYPE, + EXPECTED_CONTENT_TYPE_LENGTH) && + (GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + '+' || + GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + ';')) { + /* Although the C implementation doesn't (currently) generate them, + any custom +-suffix is explicitly valid. */ + /* TODO(klempner): We should consider preallocating common values such + as +proto or +json, or at least stashing them if we see them. */ + /* TODO(klempner): Should we be surfacing this to application code? */ + } else { + /* TODO(klempner): We're currently allowing this, but we shouldn't + see it without a proxy so log for now. */ + char *val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md), + GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "Unexpected content-type '%s'", val); + gpr_free(val); + } + } + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.content_type); + } + + return GRPC_ERROR_NONE; +} + +static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, + void *user_data, grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + if (error == GRPC_ERROR_NONE) { + error = client_filter_incoming_metadata(exec_ctx, elem, + calld->recv_initial_metadata); + } else { + GRPC_ERROR_REF(error); + } + GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_initial_metadata_ready, + error); +} + +static void recv_trailing_metadata_on_complete(grpc_exec_ctx *exec_ctx, + void *user_data, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + if (error == GRPC_ERROR_NONE) { + error = client_filter_incoming_metadata(exec_ctx, elem, + calld->recv_trailing_metadata); + } else { + GRPC_ERROR_REF(error); + } + GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_trailing_metadata_on_complete, + error); +} + +static void send_message_on_complete(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + grpc_byte_stream_cache_destroy(exec_ctx, &calld->send_message_cache); + GRPC_CLOSURE_RUN(exec_ctx, calld->original_send_message_on_complete, + GRPC_ERROR_REF(error)); +} + +// Pulls a slice from the send_message byte stream, updating +// calld->send_message_bytes_read. +static grpc_error *pull_slice_from_send_message(grpc_exec_ctx *exec_ctx, + call_data *calld) { + grpc_slice incoming_slice; + grpc_error *error = grpc_byte_stream_pull( + exec_ctx, &calld->send_message_caching_stream.base, &incoming_slice); + if (error == GRPC_ERROR_NONE) { + calld->send_message_bytes_read += GRPC_SLICE_LENGTH(incoming_slice); + grpc_slice_unref_internal(exec_ctx, incoming_slice); + } + return error; +} + +// Reads as many slices as possible from the send_message byte stream. +// Upon successful return, if calld->send_message_bytes_read == +// calld->send_message_caching_stream.base.length, then we have completed +// reading from the byte stream; otherwise, an async read has been dispatched +// and on_send_message_next_done() will be invoked when it is complete. +static grpc_error *read_all_available_send_message_data(grpc_exec_ctx *exec_ctx, + call_data *calld) { + while (grpc_byte_stream_next(exec_ctx, + &calld->send_message_caching_stream.base, + ~(size_t)0, &calld->on_send_message_next_done)) { + grpc_error *error = pull_slice_from_send_message(exec_ctx, calld); + if (error != GRPC_ERROR_NONE) return error; + if (calld->send_message_bytes_read == + calld->send_message_caching_stream.base.length) { + break; + } + } + return GRPC_ERROR_NONE; +} + +// Async callback for grpc_byte_stream_next(). +static void on_send_message_next_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, calld->send_message_batch, error, calld->call_combiner); + return; + } + error = pull_slice_from_send_message(exec_ctx, calld); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, calld->send_message_batch, error, calld->call_combiner); + return; + } + // There may or may not be more to read, but we don't care. If we got + // here, then we know that all of the data was not available + // synchronously, so we were not able to do a cached call. Instead, + // we just reset the byte stream and then send down the batch as-is. + grpc_caching_byte_stream_reset(&calld->send_message_caching_stream); + grpc_call_next_op(exec_ctx, elem, calld->send_message_batch); +} + +static char *slice_buffer_to_string(grpc_slice_buffer *slice_buffer) { + char *payload_bytes = (char *)gpr_malloc(slice_buffer->length + 1); + size_t offset = 0; + for (size_t i = 0; i < slice_buffer->count; ++i) { + memcpy(payload_bytes + offset, + GRPC_SLICE_START_PTR(slice_buffer->slices[i]), + GRPC_SLICE_LENGTH(slice_buffer->slices[i])); + offset += GRPC_SLICE_LENGTH(slice_buffer->slices[i]); + } + *(payload_bytes + offset) = '\0'; + return payload_bytes; +} + +// Modifies the path entry in the batch's send_initial_metadata to +// append the base64-encoded query for a GET request. +static grpc_error *update_path_for_get(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + grpc_slice path_slice = + GRPC_MDVALUE(batch->payload->send_initial_metadata.send_initial_metadata + ->idx.named.path->md); + /* sum up individual component's lengths and allocate enough memory to + * hold combined path+query */ + size_t estimated_len = GRPC_SLICE_LENGTH(path_slice); + estimated_len++; /* for the '?' */ + estimated_len += grpc_base64_estimate_encoded_size( + batch->payload->send_message.send_message->length, true /* url_safe */, + false /* multi_line */); + grpc_slice path_with_query_slice = GRPC_SLICE_MALLOC(estimated_len); + /* memcopy individual pieces into this slice */ + char *write_ptr = (char *)GRPC_SLICE_START_PTR(path_with_query_slice); + char *original_path = (char *)GRPC_SLICE_START_PTR(path_slice); + memcpy(write_ptr, original_path, GRPC_SLICE_LENGTH(path_slice)); + write_ptr += GRPC_SLICE_LENGTH(path_slice); + *write_ptr++ = '?'; + char *payload_bytes = + slice_buffer_to_string(&calld->send_message_cache.cache_buffer); + grpc_base64_encode_core((char *)write_ptr, payload_bytes, + batch->payload->send_message.send_message->length, + true /* url_safe */, false /* multi_line */); + gpr_free(payload_bytes); + /* remove trailing unused memory and add trailing 0 to terminate string */ + char *t = (char *)GRPC_SLICE_START_PTR(path_with_query_slice); + /* safe to use strlen since base64_encode will always add '\0' */ + path_with_query_slice = + grpc_slice_sub_no_ref(path_with_query_slice, 0, strlen(t)); + /* substitute previous path with the new path+query */ + grpc_mdelem mdelem_path_and_query = + grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_PATH, path_with_query_slice); + grpc_metadata_batch *b = + batch->payload->send_initial_metadata.send_initial_metadata; + return grpc_metadata_batch_substitute(exec_ctx, b, b->idx.named.path, + mdelem_path_and_query); +} + +static void remove_if_present(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_metadata_batch_callouts_index idx) { + if (batch->idx.array[idx] != NULL) { + grpc_metadata_batch_remove(exec_ctx, batch, batch->idx.array[idx]); + } +} + +static void hc_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + channel_data *channeld = (channel_data *)elem->channel_data; + GPR_TIMER_BEGIN("hc_start_transport_stream_op_batch", 0); + + if (batch->recv_initial_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_initial_metadata = + batch->payload->recv_initial_metadata.recv_initial_metadata; + calld->original_recv_initial_metadata_ready = + batch->payload->recv_initial_metadata.recv_initial_metadata_ready; + batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + + if (batch->recv_trailing_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_trailing_metadata = + batch->payload->recv_trailing_metadata.recv_trailing_metadata; + calld->original_recv_trailing_metadata_on_complete = batch->on_complete; + batch->on_complete = &calld->recv_trailing_metadata_on_complete; + } + + grpc_error *error = GRPC_ERROR_NONE; + bool batch_will_be_handled_asynchronously = false; + if (batch->send_initial_metadata) { + // Decide which HTTP VERB to use. We use GET if the request is marked + // cacheable, and the operation contains both initial metadata and send + // message, and the payload is below the size threshold, and all the data + // for this request is immediately available. + grpc_mdelem method = GRPC_MDELEM_METHOD_POST; + if (batch->send_message && + (batch->payload->send_initial_metadata.send_initial_metadata_flags & + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) && + batch->payload->send_message.send_message->length < + channeld->max_payload_size_for_get) { + calld->send_message_bytes_read = 0; + grpc_byte_stream_cache_init(&calld->send_message_cache, + batch->payload->send_message.send_message); + grpc_caching_byte_stream_init(&calld->send_message_caching_stream, + &calld->send_message_cache); + batch->payload->send_message.send_message = + &calld->send_message_caching_stream.base; + calld->original_send_message_on_complete = batch->on_complete; + batch->on_complete = &calld->send_message_on_complete; + calld->send_message_batch = batch; + error = read_all_available_send_message_data(exec_ctx, calld); + if (error != GRPC_ERROR_NONE) goto done; + // If all the data has been read, then we can use GET. + if (calld->send_message_bytes_read == + calld->send_message_caching_stream.base.length) { + method = GRPC_MDELEM_METHOD_GET; + error = update_path_for_get(exec_ctx, elem, batch); + if (error != GRPC_ERROR_NONE) goto done; + batch->send_message = false; + grpc_byte_stream_destroy(exec_ctx, + &calld->send_message_caching_stream.base); + } else { + // Not all data is available. The batch will be sent down + // asynchronously in on_send_message_next_done(). + batch_will_be_handled_asynchronously = true; + // Fall back to POST. + gpr_log(GPR_DEBUG, + "Request is marked Cacheable but not all data is available. " + "Falling back to POST"); + } + } else if (batch->payload->send_initial_metadata + .send_initial_metadata_flags & + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) { + method = GRPC_MDELEM_METHOD_PUT; + } + + remove_if_present( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_METHOD); + remove_if_present( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_SCHEME); + remove_if_present( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_TE); + remove_if_present( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_CONTENT_TYPE); + remove_if_present( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + GRPC_BATCH_USER_AGENT); + + /* Send : prefixed headers, which have to be before any application + layer headers. */ + error = grpc_metadata_batch_add_head( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + &calld->method, method); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_head( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + &calld->scheme, channeld->static_scheme); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_tail( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + &calld->te_trailers, GRPC_MDELEM_TE_TRAILERS); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_tail( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + &calld->content_type, GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC); + if (error != GRPC_ERROR_NONE) goto done; + error = grpc_metadata_batch_add_tail( + exec_ctx, batch->payload->send_initial_metadata.send_initial_metadata, + &calld->user_agent, GRPC_MDELEM_REF(channeld->user_agent)); + if (error != GRPC_ERROR_NONE) goto done; + } + +done: + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, calld->send_message_batch, error, calld->call_combiner); + } else if (!batch_will_be_handled_asynchronously) { + grpc_call_next_op(exec_ctx, elem, batch); + } + GPR_TIMER_END("hc_start_transport_stream_op_batch", 0); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + calld->call_combiner = args->call_combiner; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->recv_trailing_metadata_on_complete, + recv_trailing_metadata_on_complete, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete, + elem, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->on_send_message_next_done, + on_send_message_next_done, elem, grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) {} + +static grpc_mdelem scheme_from_args(const grpc_channel_args *args) { + unsigned i; + size_t j; + grpc_mdelem valid_schemes[] = {GRPC_MDELEM_SCHEME_HTTP, + GRPC_MDELEM_SCHEME_HTTPS}; + if (args != NULL) { + for (i = 0; i < args->num_args; ++i) { + if (args->args[i].type == GRPC_ARG_STRING && + strcmp(args->args[i].key, GRPC_ARG_HTTP2_SCHEME) == 0) { + for (j = 0; j < GPR_ARRAY_SIZE(valid_schemes); j++) { + if (0 == grpc_slice_str_cmp(GRPC_MDVALUE(valid_schemes[j]), + args->args[i].value.string)) { + return valid_schemes[j]; + } + } + } + } + } + return GRPC_MDELEM_SCHEME_HTTP; +} + +static size_t max_payload_size_from_args(const grpc_channel_args *args) { + if (args != NULL) { + for (size_t i = 0; i < args->num_args; ++i) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET)) { + if (args->args[i].type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s: must be an integer", + GRPC_ARG_MAX_PAYLOAD_SIZE_FOR_GET); + } else { + return (size_t)args->args[i].value.integer; + } + } + } + } + return kMaxPayloadSizeForGet; +} + +static grpc_slice user_agent_from_args(const grpc_channel_args *args, + const char *transport_name) { + gpr_strvec v; + size_t i; + int is_first = 1; + char *tmp; + grpc_slice result; + + gpr_strvec_init(&v); + + for (i = 0; args && i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_PRIMARY_USER_AGENT_STRING)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "Channel argument '%s' should be a string", + GRPC_ARG_PRIMARY_USER_AGENT_STRING); + } else { + if (!is_first) gpr_strvec_add(&v, gpr_strdup(" ")); + is_first = 0; + gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string)); + } + } + } + + gpr_asprintf(&tmp, "%sgrpc-c/%s (%s; %s; %s)", is_first ? "" : " ", + grpc_version_string(), GPR_PLATFORM_STRING, transport_name, + grpc_g_stands_for()); + is_first = 0; + gpr_strvec_add(&v, tmp); + + for (i = 0; args && i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_SECONDARY_USER_AGENT_STRING)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "Channel argument '%s' should be a string", + GRPC_ARG_SECONDARY_USER_AGENT_STRING); + } else { + if (!is_first) gpr_strvec_add(&v, gpr_strdup(" ")); + is_first = 0; + gpr_strvec_add(&v, gpr_strdup(args->args[i].value.string)); + } + } + } + + tmp = gpr_strvec_flatten(&v, NULL); + gpr_strvec_destroy(&v); + result = grpc_slice_intern(grpc_slice_from_static_string(tmp)); + gpr_free(tmp); + + return result; +} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + channel_data *chand = (channel_data *)elem->channel_data; + GPR_ASSERT(!args->is_last); + GPR_ASSERT(args->optional_transport != NULL); + chand->static_scheme = scheme_from_args(args->channel_args); + chand->max_payload_size_for_get = + max_payload_size_from_args(args->channel_args); + chand->user_agent = grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_USER_AGENT, + user_agent_from_args(args->channel_args, + args->optional_transport->vtable->name)); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + GRPC_MDELEM_UNREF(exec_ctx, chand->user_agent); +} + +const grpc_channel_filter grpc_http_client_filter = { + hc_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "http-client"}; diff --git a/src/core/ext/filters/http/http_filters_plugin.c b/src/core/ext/filters/http/http_filters_plugin.c deleted file mode 100644 index 8f5b856317..0000000000 --- a/src/core/ext/filters/http/http_filters_plugin.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/ext/filters/http/client/http_client_filter.h" -#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" -#include "src/core/ext/filters/http/server/http_server_filter.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/transport_impl.h" - -typedef struct { - const grpc_channel_filter *filter; - const char *control_channel_arg; -} optional_filter; - -static optional_filter compress_filter = { - &grpc_message_compress_filter, GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION}; - -static bool is_building_http_like_transport( - grpc_channel_stack_builder *builder) { - grpc_transport *t = grpc_channel_stack_builder_get_transport(builder); - return t != NULL && strstr(t->vtable->name, "http"); -} - -static bool maybe_add_optional_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, - void *arg) { - if (!is_building_http_like_transport(builder)) return true; - optional_filter *filtarg = (optional_filter *)arg; - const grpc_channel_args *channel_args = - grpc_channel_stack_builder_get_channel_arguments(builder); - bool enable = grpc_channel_arg_get_bool( - grpc_channel_args_find(channel_args, filtarg->control_channel_arg), - !grpc_channel_args_want_minimal_stack(channel_args)); - return enable ? grpc_channel_stack_builder_prepend_filter( - builder, filtarg->filter, NULL, NULL) - : true; -} - -static bool maybe_add_required_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, - void *arg) { - return is_building_http_like_transport(builder) - ? grpc_channel_stack_builder_prepend_filter( - builder, (const grpc_channel_filter *)arg, NULL, NULL) - : true; -} - -extern "C" void grpc_http_filters_init(void) { - grpc_register_tracer(&grpc_compression_trace); - grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_optional_filter, &compress_filter); - grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_optional_filter, &compress_filter); - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_optional_filter, &compress_filter); - grpc_channel_init_register_stage( - GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_required_filter, (void *)&grpc_http_client_filter); - grpc_channel_init_register_stage( - GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_required_filter, (void *)&grpc_http_client_filter); - grpc_channel_init_register_stage( - GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_required_filter, (void *)&grpc_http_server_filter); -} - -extern "C" void grpc_http_filters_shutdown(void) {} diff --git a/src/core/ext/filters/http/http_filters_plugin.cc b/src/core/ext/filters/http/http_filters_plugin.cc new file mode 100644 index 0000000000..8f5b856317 --- /dev/null +++ b/src/core/ext/filters/http/http_filters_plugin.cc @@ -0,0 +1,89 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/ext/filters/http/client/http_client_filter.h" +#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" +#include "src/core/ext/filters/http/server/http_server_filter.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/transport_impl.h" + +typedef struct { + const grpc_channel_filter *filter; + const char *control_channel_arg; +} optional_filter; + +static optional_filter compress_filter = { + &grpc_message_compress_filter, GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION}; + +static bool is_building_http_like_transport( + grpc_channel_stack_builder *builder) { + grpc_transport *t = grpc_channel_stack_builder_get_transport(builder); + return t != NULL && strstr(t->vtable->name, "http"); +} + +static bool maybe_add_optional_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, + void *arg) { + if (!is_building_http_like_transport(builder)) return true; + optional_filter *filtarg = (optional_filter *)arg; + const grpc_channel_args *channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + bool enable = grpc_channel_arg_get_bool( + grpc_channel_args_find(channel_args, filtarg->control_channel_arg), + !grpc_channel_args_want_minimal_stack(channel_args)); + return enable ? grpc_channel_stack_builder_prepend_filter( + builder, filtarg->filter, NULL, NULL) + : true; +} + +static bool maybe_add_required_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, + void *arg) { + return is_building_http_like_transport(builder) + ? grpc_channel_stack_builder_prepend_filter( + builder, (const grpc_channel_filter *)arg, NULL, NULL) + : true; +} + +extern "C" void grpc_http_filters_init(void) { + grpc_register_tracer(&grpc_compression_trace); + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_optional_filter, &compress_filter); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_optional_filter, &compress_filter); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_optional_filter, &compress_filter); + grpc_channel_init_register_stage( + GRPC_CLIENT_SUBCHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_required_filter, (void *)&grpc_http_client_filter); + grpc_channel_init_register_stage( + GRPC_CLIENT_DIRECT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_required_filter, (void *)&grpc_http_client_filter); + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_required_filter, (void *)&grpc_http_server_filter); +} + +extern "C" void grpc_http_filters_shutdown(void) {} diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.c b/src/core/ext/filters/http/message_compress/message_compress_filter.c deleted file mode 100644 index f785e1355d..0000000000 --- a/src/core/ext/filters/http/message_compress/message_compress_filter.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include -#include -#include -#include - -#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/compression/algorithm_metadata.h" -#include "src/core/lib/compression/message_compress.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/transport/static_metadata.h" - -typedef enum { - // Initial metadata not yet seen. - INITIAL_METADATA_UNSEEN = 0, - // Initial metadata seen; compression algorithm set. - HAS_COMPRESSION_ALGORITHM, - // Initial metadata seen; no compression algorithm set. - NO_COMPRESSION_ALGORITHM, -} initial_metadata_state; - -typedef struct call_data { - grpc_call_combiner *call_combiner; - grpc_linked_mdelem compression_algorithm_storage; - grpc_linked_mdelem stream_compression_algorithm_storage; - grpc_linked_mdelem accept_encoding_storage; - grpc_linked_mdelem accept_stream_encoding_storage; - /** Compression algorithm we'll try to use. It may be given by incoming - * metadata, or by the channel's default compression settings. */ - grpc_compression_algorithm compression_algorithm; - initial_metadata_state send_initial_metadata_state; - grpc_error *cancel_error; - grpc_closure start_send_message_batch_in_call_combiner; - grpc_transport_stream_op_batch *send_message_batch; - grpc_slice_buffer slices; /**< Buffers up input slices to be compressed */ - grpc_slice_buffer_stream replacement_stream; - grpc_closure *original_send_message_on_complete; - grpc_closure send_message_on_complete; - grpc_closure on_send_message_next_done; -} call_data; - -typedef struct channel_data { - /** The default, channel-level, compression algorithm */ - grpc_compression_algorithm default_compression_algorithm; - /** Bitset of enabled algorithms */ - uint32_t enabled_algorithms_bitset; - /** Supported compression algorithms */ - uint32_t supported_compression_algorithms; - - /** The default, channel-level, stream compression algorithm */ - grpc_stream_compression_algorithm default_stream_compression_algorithm; - /** Bitset of enabled stream compression algorithms */ - uint32_t enabled_stream_compression_algorithms_bitset; - /** Supported stream compression algorithms */ - uint32_t supported_stream_compression_algorithms; -} channel_data; - -static bool skip_compression(grpc_call_element *elem, uint32_t flags, - bool has_compression_algorithm) { - call_data *calld = (call_data *)elem->call_data; - channel_data *channeld = (channel_data *)elem->channel_data; - - if (flags & (GRPC_WRITE_NO_COMPRESS | GRPC_WRITE_INTERNAL_COMPRESS)) { - return true; - } - if (has_compression_algorithm) { - if (calld->compression_algorithm == GRPC_COMPRESS_NONE) { - return true; - } - return false; /* we have an actual call-specific algorithm */ - } - /* no per-call compression override */ - return channeld->default_compression_algorithm == GRPC_COMPRESS_NONE; -} - -/** Filter initial metadata */ -static grpc_error *process_send_initial_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_metadata_batch *initial_metadata, - bool *has_compression_algorithm) GRPC_MUST_USE_RESULT; -static grpc_error *process_send_initial_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_metadata_batch *initial_metadata, bool *has_compression_algorithm) { - call_data *calld = (call_data *)elem->call_data; - channel_data *channeld = (channel_data *)elem->channel_data; - *has_compression_algorithm = false; - grpc_stream_compression_algorithm stream_compression_algorithm = - GRPC_STREAM_COMPRESS_NONE; - if (initial_metadata->idx.named.grpc_internal_stream_encoding_request != - NULL) { - grpc_mdelem md = - initial_metadata->idx.named.grpc_internal_stream_encoding_request->md; - if (!grpc_stream_compression_algorithm_parse( - GRPC_MDVALUE(md), &stream_compression_algorithm)) { - char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log(GPR_ERROR, - "Invalid stream compression algorithm: '%s' (unknown). Ignoring.", - val); - gpr_free(val); - stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; - } - if (!GPR_BITGET(channeld->enabled_stream_compression_algorithms_bitset, - stream_compression_algorithm)) { - char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log( - GPR_ERROR, - "Invalid stream compression algorithm: '%s' (previously disabled). " - "Ignoring.", - val); - gpr_free(val); - stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; - } - *has_compression_algorithm = true; - grpc_metadata_batch_remove( - exec_ctx, initial_metadata, - initial_metadata->idx.named.grpc_internal_stream_encoding_request); - /* Disable message-wise compression */ - calld->compression_algorithm = GRPC_COMPRESS_NONE; - if (initial_metadata->idx.named.grpc_internal_encoding_request != NULL) { - grpc_metadata_batch_remove( - exec_ctx, initial_metadata, - initial_metadata->idx.named.grpc_internal_encoding_request); - } - } else if (initial_metadata->idx.named.grpc_internal_encoding_request != - NULL) { - grpc_mdelem md = - initial_metadata->idx.named.grpc_internal_encoding_request->md; - if (!grpc_compression_algorithm_parse(GRPC_MDVALUE(md), - &calld->compression_algorithm)) { - char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log(GPR_ERROR, - "Invalid compression algorithm: '%s' (unknown). Ignoring.", val); - gpr_free(val); - calld->compression_algorithm = GRPC_COMPRESS_NONE; - } - *has_compression_algorithm = true; - grpc_metadata_batch_remove( - exec_ctx, initial_metadata, - initial_metadata->idx.named.grpc_internal_encoding_request); - } else { - /* If no algorithm was found in the metadata and we aren't - * exceptionally skipping compression, fall back to the channel - * default */ - if (channeld->default_stream_compression_algorithm != - GRPC_STREAM_COMPRESS_NONE) { - stream_compression_algorithm = - channeld->default_stream_compression_algorithm; - calld->compression_algorithm = GRPC_COMPRESS_NONE; - } else { - calld->compression_algorithm = channeld->default_compression_algorithm; - } - *has_compression_algorithm = true; - } - - grpc_error *error = GRPC_ERROR_NONE; - /* hint compression algorithm */ - if (stream_compression_algorithm != GRPC_STREAM_COMPRESS_NONE) { - error = grpc_metadata_batch_add_tail( - exec_ctx, initial_metadata, - &calld->stream_compression_algorithm_storage, - grpc_stream_compression_encoding_mdelem(stream_compression_algorithm)); - } else if (calld->compression_algorithm != GRPC_COMPRESS_NONE) { - error = grpc_metadata_batch_add_tail( - exec_ctx, initial_metadata, &calld->compression_algorithm_storage, - grpc_compression_encoding_mdelem(calld->compression_algorithm)); - } - - if (error != GRPC_ERROR_NONE) return error; - - /* convey supported compression algorithms */ - error = grpc_metadata_batch_add_tail( - exec_ctx, initial_metadata, &calld->accept_encoding_storage, - GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS( - channeld->supported_compression_algorithms)); - - if (error != GRPC_ERROR_NONE) return error; - - /* Do not overwrite accept-encoding header if it already presents. */ - if (!initial_metadata->idx.named.accept_encoding) { - error = grpc_metadata_batch_add_tail( - exec_ctx, initial_metadata, &calld->accept_stream_encoding_storage, - GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS( - channeld->supported_stream_compression_algorithms)); - } - - return error; -} - -static void send_message_on_complete(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &calld->slices); - GRPC_CLOSURE_RUN(exec_ctx, calld->original_send_message_on_complete, - GRPC_ERROR_REF(error)); -} - -static void send_message_batch_continue(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - call_data *calld = (call_data *)elem->call_data; - // Note: The call to grpc_call_next_op() results in yielding the - // call combiner, so we need to clear calld->send_message_batch - // before we do that. - grpc_transport_stream_op_batch *send_message_batch = - calld->send_message_batch; - calld->send_message_batch = NULL; - grpc_call_next_op(exec_ctx, elem, send_message_batch); -} - -static void finish_send_message(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - call_data *calld = (call_data *)elem->call_data; - // Compress the data if appropriate. - grpc_slice_buffer tmp; - grpc_slice_buffer_init(&tmp); - uint32_t send_flags = - calld->send_message_batch->payload->send_message.send_message->flags; - bool did_compress = grpc_msg_compress(exec_ctx, calld->compression_algorithm, - &calld->slices, &tmp); - if (did_compress) { - if (GRPC_TRACER_ON(grpc_compression_trace)) { - const char *algo_name; - const size_t before_size = calld->slices.length; - const size_t after_size = tmp.length; - const float savings_ratio = 1.0f - (float)after_size / (float)before_size; - GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm, - &algo_name)); - gpr_log(GPR_DEBUG, "Compressed[%s] %" PRIuPTR " bytes vs. %" PRIuPTR - " bytes (%.2f%% savings)", - algo_name, before_size, after_size, 100 * savings_ratio); - } - grpc_slice_buffer_swap(&calld->slices, &tmp); - send_flags |= GRPC_WRITE_INTERNAL_COMPRESS; - } else { - if (GRPC_TRACER_ON(grpc_compression_trace)) { - const char *algo_name; - GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm, - &algo_name)); - gpr_log(GPR_DEBUG, - "Algorithm '%s' enabled but decided not to compress. Input size: " - "%" PRIuPTR, - algo_name, calld->slices.length); - } - } - grpc_slice_buffer_destroy_internal(exec_ctx, &tmp); - // Swap out the original byte stream with our new one and send the - // batch down. - grpc_byte_stream_destroy( - exec_ctx, calld->send_message_batch->payload->send_message.send_message); - grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices, - send_flags); - calld->send_message_batch->payload->send_message.send_message = - &calld->replacement_stream.base; - calld->original_send_message_on_complete = - calld->send_message_batch->on_complete; - calld->send_message_batch->on_complete = &calld->send_message_on_complete; - send_message_batch_continue(exec_ctx, elem); -} - -static void fail_send_message_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, - void *arg, - grpc_error *error) { - call_data *calld = (call_data *)arg; - if (calld->send_message_batch != NULL) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, calld->send_message_batch, GRPC_ERROR_REF(error), - calld->call_combiner); - calld->send_message_batch = NULL; - } -} - -// Pulls a slice from the send_message byte stream and adds it to calld->slices. -static grpc_error *pull_slice_from_send_message(grpc_exec_ctx *exec_ctx, - call_data *calld) { - grpc_slice incoming_slice; - grpc_error *error = grpc_byte_stream_pull( - exec_ctx, calld->send_message_batch->payload->send_message.send_message, - &incoming_slice); - if (error == GRPC_ERROR_NONE) { - grpc_slice_buffer_add(&calld->slices, incoming_slice); - } - return error; -} - -// Reads as many slices as possible from the send_message byte stream. -// If all data has been read, invokes finish_send_message(). Otherwise, -// an async call to grpc_byte_stream_next() has been started, which will -// eventually result in calling on_send_message_next_done(). -static void continue_reading_send_message(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem) { - call_data *calld = (call_data *)elem->call_data; - while (grpc_byte_stream_next( - exec_ctx, calld->send_message_batch->payload->send_message.send_message, - ~(size_t)0, &calld->on_send_message_next_done)) { - grpc_error *error = pull_slice_from_send_message(exec_ctx, calld); - if (error != GRPC_ERROR_NONE) { - // Closure callback; does not take ownership of error. - fail_send_message_batch_in_call_combiner(exec_ctx, calld, error); - GRPC_ERROR_UNREF(error); - return; - } - if (calld->slices.length == - calld->send_message_batch->payload->send_message.send_message->length) { - finish_send_message(exec_ctx, elem); - break; - } - } -} - -// Async callback for grpc_byte_stream_next(). -static void on_send_message_next_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - if (error != GRPC_ERROR_NONE) { - // Closure callback; does not take ownership of error. - fail_send_message_batch_in_call_combiner(exec_ctx, calld, error); - return; - } - error = pull_slice_from_send_message(exec_ctx, calld); - if (error != GRPC_ERROR_NONE) { - // Closure callback; does not take ownership of error. - fail_send_message_batch_in_call_combiner(exec_ctx, calld, error); - GRPC_ERROR_UNREF(error); - return; - } - if (calld->slices.length == - calld->send_message_batch->payload->send_message.send_message->length) { - finish_send_message(exec_ctx, elem); - } else { - continue_reading_send_message(exec_ctx, elem); - } -} - -static void start_send_message_batch(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *unused) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - if (skip_compression( - elem, - calld->send_message_batch->payload->send_message.send_message->flags, - calld->send_initial_metadata_state == HAS_COMPRESSION_ALGORITHM)) { - send_message_batch_continue(exec_ctx, elem); - } else { - continue_reading_send_message(exec_ctx, elem); - } -} - -static void compress_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - GPR_TIMER_BEGIN("compress_start_transport_stream_op_batch", 0); - // Handle cancel_stream. - if (batch->cancel_stream) { - GRPC_ERROR_UNREF(calld->cancel_error); - calld->cancel_error = - GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); - if (calld->send_message_batch != NULL) { - if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) { - GRPC_CALL_COMBINER_START( - exec_ctx, calld->call_combiner, - GRPC_CLOSURE_CREATE(fail_send_message_batch_in_call_combiner, calld, - grpc_schedule_on_exec_ctx), - GRPC_ERROR_REF(calld->cancel_error), "failing send_message op"); - } else { - grpc_byte_stream_shutdown( - exec_ctx, - calld->send_message_batch->payload->send_message.send_message, - GRPC_ERROR_REF(calld->cancel_error)); - } - } - } else if (calld->cancel_error != GRPC_ERROR_NONE) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, batch, GRPC_ERROR_REF(calld->cancel_error), - calld->call_combiner); - goto done; - } - // Handle send_initial_metadata. - if (batch->send_initial_metadata) { - GPR_ASSERT(calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN); - bool has_compression_algorithm; - grpc_error *error = process_send_initial_metadata( - exec_ctx, elem, - batch->payload->send_initial_metadata.send_initial_metadata, - &has_compression_algorithm); - if (error != GRPC_ERROR_NONE) { - grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, batch, error, - calld->call_combiner); - goto done; - } - calld->send_initial_metadata_state = has_compression_algorithm - ? HAS_COMPRESSION_ALGORITHM - : NO_COMPRESSION_ALGORITHM; - // If we had previously received a batch containing a send_message op, - // handle it now. Note that we need to re-enter the call combiner - // for this, since we can't send two batches down while holding the - // call combiner, since the connected_channel filter (at the bottom of - // the call stack) will release the call combiner for each batch it sees. - if (calld->send_message_batch != NULL) { - GRPC_CALL_COMBINER_START( - exec_ctx, calld->call_combiner, - &calld->start_send_message_batch_in_call_combiner, GRPC_ERROR_NONE, - "starting send_message after send_initial_metadata"); - } - } - // Handle send_message. - if (batch->send_message) { - GPR_ASSERT(calld->send_message_batch == NULL); - calld->send_message_batch = batch; - // If we have not yet seen send_initial_metadata, then we have to - // wait. We save the batch in calld and then drop the call - // combiner, which we'll have to pick up again later when we get - // send_initial_metadata. - if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) { - GRPC_CALL_COMBINER_STOP( - exec_ctx, calld->call_combiner, - "send_message batch pending send_initial_metadata"); - goto done; - } - start_send_message_batch(exec_ctx, elem, GRPC_ERROR_NONE); - } else { - // Pass control down the stack. - grpc_call_next_op(exec_ctx, elem, batch); - } -done: - GPR_TIMER_END("compress_start_transport_stream_op_batch", 0); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - calld->call_combiner = args->call_combiner; - calld->cancel_error = GRPC_ERROR_NONE; - grpc_slice_buffer_init(&calld->slices); - GRPC_CLOSURE_INIT(&calld->start_send_message_batch_in_call_combiner, - start_send_message_batch, elem, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->on_send_message_next_done, - on_send_message_next_done, elem, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete, - elem, grpc_schedule_on_exec_ctx); - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *calld = (call_data *)elem->call_data; - grpc_slice_buffer_destroy_internal(exec_ctx, &calld->slices); - GRPC_ERROR_UNREF(calld->cancel_error); -} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *channeld = (channel_data *)elem->channel_data; - - /* Configuration for message compression */ - channeld->enabled_algorithms_bitset = - grpc_channel_args_compression_algorithm_get_states(args->channel_args); - - channeld->default_compression_algorithm = - grpc_channel_args_get_compression_algorithm(args->channel_args); - /* Make sure the default isn't disabled. */ - if (!GPR_BITGET(channeld->enabled_algorithms_bitset, - channeld->default_compression_algorithm)) { - gpr_log(GPR_DEBUG, - "compression algorithm %d not enabled: switching to none", - channeld->default_compression_algorithm); - channeld->default_compression_algorithm = GRPC_COMPRESS_NONE; - } - - channeld->supported_compression_algorithms = - (((1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1) & - channeld->enabled_algorithms_bitset) | - 1u; - - /* Configuration for stream compression */ - channeld->enabled_stream_compression_algorithms_bitset = - grpc_channel_args_stream_compression_algorithm_get_states( - args->channel_args); - - channeld->default_stream_compression_algorithm = - grpc_channel_args_get_stream_compression_algorithm(args->channel_args); - - if (!GPR_BITGET(channeld->enabled_stream_compression_algorithms_bitset, - channeld->default_stream_compression_algorithm)) { - gpr_log(GPR_DEBUG, - "stream compression algorithm %d not enabled: switching to none", - channeld->default_stream_compression_algorithm); - channeld->default_stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; - } - - channeld->supported_stream_compression_algorithms = - (((1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 1) & - channeld->enabled_stream_compression_algorithms_bitset) | - 1u; - - GPR_ASSERT(!args->is_last); - return GRPC_ERROR_NONE; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) {} - -const grpc_channel_filter grpc_message_compress_filter = { - compress_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "message_compress"}; diff --git a/src/core/ext/filters/http/message_compress/message_compress_filter.cc b/src/core/ext/filters/http/message_compress/message_compress_filter.cc new file mode 100644 index 0000000000..f785e1355d --- /dev/null +++ b/src/core/ext/filters/http/message_compress/message_compress_filter.cc @@ -0,0 +1,547 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include +#include +#include + +#include "src/core/ext/filters/http/message_compress/message_compress_filter.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/compression/message_compress.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/transport/static_metadata.h" + +typedef enum { + // Initial metadata not yet seen. + INITIAL_METADATA_UNSEEN = 0, + // Initial metadata seen; compression algorithm set. + HAS_COMPRESSION_ALGORITHM, + // Initial metadata seen; no compression algorithm set. + NO_COMPRESSION_ALGORITHM, +} initial_metadata_state; + +typedef struct call_data { + grpc_call_combiner *call_combiner; + grpc_linked_mdelem compression_algorithm_storage; + grpc_linked_mdelem stream_compression_algorithm_storage; + grpc_linked_mdelem accept_encoding_storage; + grpc_linked_mdelem accept_stream_encoding_storage; + /** Compression algorithm we'll try to use. It may be given by incoming + * metadata, or by the channel's default compression settings. */ + grpc_compression_algorithm compression_algorithm; + initial_metadata_state send_initial_metadata_state; + grpc_error *cancel_error; + grpc_closure start_send_message_batch_in_call_combiner; + grpc_transport_stream_op_batch *send_message_batch; + grpc_slice_buffer slices; /**< Buffers up input slices to be compressed */ + grpc_slice_buffer_stream replacement_stream; + grpc_closure *original_send_message_on_complete; + grpc_closure send_message_on_complete; + grpc_closure on_send_message_next_done; +} call_data; + +typedef struct channel_data { + /** The default, channel-level, compression algorithm */ + grpc_compression_algorithm default_compression_algorithm; + /** Bitset of enabled algorithms */ + uint32_t enabled_algorithms_bitset; + /** Supported compression algorithms */ + uint32_t supported_compression_algorithms; + + /** The default, channel-level, stream compression algorithm */ + grpc_stream_compression_algorithm default_stream_compression_algorithm; + /** Bitset of enabled stream compression algorithms */ + uint32_t enabled_stream_compression_algorithms_bitset; + /** Supported stream compression algorithms */ + uint32_t supported_stream_compression_algorithms; +} channel_data; + +static bool skip_compression(grpc_call_element *elem, uint32_t flags, + bool has_compression_algorithm) { + call_data *calld = (call_data *)elem->call_data; + channel_data *channeld = (channel_data *)elem->channel_data; + + if (flags & (GRPC_WRITE_NO_COMPRESS | GRPC_WRITE_INTERNAL_COMPRESS)) { + return true; + } + if (has_compression_algorithm) { + if (calld->compression_algorithm == GRPC_COMPRESS_NONE) { + return true; + } + return false; /* we have an actual call-specific algorithm */ + } + /* no per-call compression override */ + return channeld->default_compression_algorithm == GRPC_COMPRESS_NONE; +} + +/** Filter initial metadata */ +static grpc_error *process_send_initial_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_metadata_batch *initial_metadata, + bool *has_compression_algorithm) GRPC_MUST_USE_RESULT; +static grpc_error *process_send_initial_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_metadata_batch *initial_metadata, bool *has_compression_algorithm) { + call_data *calld = (call_data *)elem->call_data; + channel_data *channeld = (channel_data *)elem->channel_data; + *has_compression_algorithm = false; + grpc_stream_compression_algorithm stream_compression_algorithm = + GRPC_STREAM_COMPRESS_NONE; + if (initial_metadata->idx.named.grpc_internal_stream_encoding_request != + NULL) { + grpc_mdelem md = + initial_metadata->idx.named.grpc_internal_stream_encoding_request->md; + if (!grpc_stream_compression_algorithm_parse( + GRPC_MDVALUE(md), &stream_compression_algorithm)) { + char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid stream compression algorithm: '%s' (unknown). Ignoring.", + val); + gpr_free(val); + stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; + } + if (!GPR_BITGET(channeld->enabled_stream_compression_algorithms_bitset, + stream_compression_algorithm)) { + char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log( + GPR_ERROR, + "Invalid stream compression algorithm: '%s' (previously disabled). " + "Ignoring.", + val); + gpr_free(val); + stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; + } + *has_compression_algorithm = true; + grpc_metadata_batch_remove( + exec_ctx, initial_metadata, + initial_metadata->idx.named.grpc_internal_stream_encoding_request); + /* Disable message-wise compression */ + calld->compression_algorithm = GRPC_COMPRESS_NONE; + if (initial_metadata->idx.named.grpc_internal_encoding_request != NULL) { + grpc_metadata_batch_remove( + exec_ctx, initial_metadata, + initial_metadata->idx.named.grpc_internal_encoding_request); + } + } else if (initial_metadata->idx.named.grpc_internal_encoding_request != + NULL) { + grpc_mdelem md = + initial_metadata->idx.named.grpc_internal_encoding_request->md; + if (!grpc_compression_algorithm_parse(GRPC_MDVALUE(md), + &calld->compression_algorithm)) { + char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid compression algorithm: '%s' (unknown). Ignoring.", val); + gpr_free(val); + calld->compression_algorithm = GRPC_COMPRESS_NONE; + } + *has_compression_algorithm = true; + grpc_metadata_batch_remove( + exec_ctx, initial_metadata, + initial_metadata->idx.named.grpc_internal_encoding_request); + } else { + /* If no algorithm was found in the metadata and we aren't + * exceptionally skipping compression, fall back to the channel + * default */ + if (channeld->default_stream_compression_algorithm != + GRPC_STREAM_COMPRESS_NONE) { + stream_compression_algorithm = + channeld->default_stream_compression_algorithm; + calld->compression_algorithm = GRPC_COMPRESS_NONE; + } else { + calld->compression_algorithm = channeld->default_compression_algorithm; + } + *has_compression_algorithm = true; + } + + grpc_error *error = GRPC_ERROR_NONE; + /* hint compression algorithm */ + if (stream_compression_algorithm != GRPC_STREAM_COMPRESS_NONE) { + error = grpc_metadata_batch_add_tail( + exec_ctx, initial_metadata, + &calld->stream_compression_algorithm_storage, + grpc_stream_compression_encoding_mdelem(stream_compression_algorithm)); + } else if (calld->compression_algorithm != GRPC_COMPRESS_NONE) { + error = grpc_metadata_batch_add_tail( + exec_ctx, initial_metadata, &calld->compression_algorithm_storage, + grpc_compression_encoding_mdelem(calld->compression_algorithm)); + } + + if (error != GRPC_ERROR_NONE) return error; + + /* convey supported compression algorithms */ + error = grpc_metadata_batch_add_tail( + exec_ctx, initial_metadata, &calld->accept_encoding_storage, + GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS( + channeld->supported_compression_algorithms)); + + if (error != GRPC_ERROR_NONE) return error; + + /* Do not overwrite accept-encoding header if it already presents. */ + if (!initial_metadata->idx.named.accept_encoding) { + error = grpc_metadata_batch_add_tail( + exec_ctx, initial_metadata, &calld->accept_stream_encoding_storage, + GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS( + channeld->supported_stream_compression_algorithms)); + } + + return error; +} + +static void send_message_on_complete(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &calld->slices); + GRPC_CLOSURE_RUN(exec_ctx, calld->original_send_message_on_complete, + GRPC_ERROR_REF(error)); +} + +static void send_message_batch_continue(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + call_data *calld = (call_data *)elem->call_data; + // Note: The call to grpc_call_next_op() results in yielding the + // call combiner, so we need to clear calld->send_message_batch + // before we do that. + grpc_transport_stream_op_batch *send_message_batch = + calld->send_message_batch; + calld->send_message_batch = NULL; + grpc_call_next_op(exec_ctx, elem, send_message_batch); +} + +static void finish_send_message(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + call_data *calld = (call_data *)elem->call_data; + // Compress the data if appropriate. + grpc_slice_buffer tmp; + grpc_slice_buffer_init(&tmp); + uint32_t send_flags = + calld->send_message_batch->payload->send_message.send_message->flags; + bool did_compress = grpc_msg_compress(exec_ctx, calld->compression_algorithm, + &calld->slices, &tmp); + if (did_compress) { + if (GRPC_TRACER_ON(grpc_compression_trace)) { + const char *algo_name; + const size_t before_size = calld->slices.length; + const size_t after_size = tmp.length; + const float savings_ratio = 1.0f - (float)after_size / (float)before_size; + GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm, + &algo_name)); + gpr_log(GPR_DEBUG, "Compressed[%s] %" PRIuPTR " bytes vs. %" PRIuPTR + " bytes (%.2f%% savings)", + algo_name, before_size, after_size, 100 * savings_ratio); + } + grpc_slice_buffer_swap(&calld->slices, &tmp); + send_flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } else { + if (GRPC_TRACER_ON(grpc_compression_trace)) { + const char *algo_name; + GPR_ASSERT(grpc_compression_algorithm_name(calld->compression_algorithm, + &algo_name)); + gpr_log(GPR_DEBUG, + "Algorithm '%s' enabled but decided not to compress. Input size: " + "%" PRIuPTR, + algo_name, calld->slices.length); + } + } + grpc_slice_buffer_destroy_internal(exec_ctx, &tmp); + // Swap out the original byte stream with our new one and send the + // batch down. + grpc_byte_stream_destroy( + exec_ctx, calld->send_message_batch->payload->send_message.send_message); + grpc_slice_buffer_stream_init(&calld->replacement_stream, &calld->slices, + send_flags); + calld->send_message_batch->payload->send_message.send_message = + &calld->replacement_stream.base; + calld->original_send_message_on_complete = + calld->send_message_batch->on_complete; + calld->send_message_batch->on_complete = &calld->send_message_on_complete; + send_message_batch_continue(exec_ctx, elem); +} + +static void fail_send_message_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, + void *arg, + grpc_error *error) { + call_data *calld = (call_data *)arg; + if (calld->send_message_batch != NULL) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, calld->send_message_batch, GRPC_ERROR_REF(error), + calld->call_combiner); + calld->send_message_batch = NULL; + } +} + +// Pulls a slice from the send_message byte stream and adds it to calld->slices. +static grpc_error *pull_slice_from_send_message(grpc_exec_ctx *exec_ctx, + call_data *calld) { + grpc_slice incoming_slice; + grpc_error *error = grpc_byte_stream_pull( + exec_ctx, calld->send_message_batch->payload->send_message.send_message, + &incoming_slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&calld->slices, incoming_slice); + } + return error; +} + +// Reads as many slices as possible from the send_message byte stream. +// If all data has been read, invokes finish_send_message(). Otherwise, +// an async call to grpc_byte_stream_next() has been started, which will +// eventually result in calling on_send_message_next_done(). +static void continue_reading_send_message(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem) { + call_data *calld = (call_data *)elem->call_data; + while (grpc_byte_stream_next( + exec_ctx, calld->send_message_batch->payload->send_message.send_message, + ~(size_t)0, &calld->on_send_message_next_done)) { + grpc_error *error = pull_slice_from_send_message(exec_ctx, calld); + if (error != GRPC_ERROR_NONE) { + // Closure callback; does not take ownership of error. + fail_send_message_batch_in_call_combiner(exec_ctx, calld, error); + GRPC_ERROR_UNREF(error); + return; + } + if (calld->slices.length == + calld->send_message_batch->payload->send_message.send_message->length) { + finish_send_message(exec_ctx, elem); + break; + } + } +} + +// Async callback for grpc_byte_stream_next(). +static void on_send_message_next_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + if (error != GRPC_ERROR_NONE) { + // Closure callback; does not take ownership of error. + fail_send_message_batch_in_call_combiner(exec_ctx, calld, error); + return; + } + error = pull_slice_from_send_message(exec_ctx, calld); + if (error != GRPC_ERROR_NONE) { + // Closure callback; does not take ownership of error. + fail_send_message_batch_in_call_combiner(exec_ctx, calld, error); + GRPC_ERROR_UNREF(error); + return; + } + if (calld->slices.length == + calld->send_message_batch->payload->send_message.send_message->length) { + finish_send_message(exec_ctx, elem); + } else { + continue_reading_send_message(exec_ctx, elem); + } +} + +static void start_send_message_batch(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *unused) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + if (skip_compression( + elem, + calld->send_message_batch->payload->send_message.send_message->flags, + calld->send_initial_metadata_state == HAS_COMPRESSION_ALGORITHM)) { + send_message_batch_continue(exec_ctx, elem); + } else { + continue_reading_send_message(exec_ctx, elem); + } +} + +static void compress_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + GPR_TIMER_BEGIN("compress_start_transport_stream_op_batch", 0); + // Handle cancel_stream. + if (batch->cancel_stream) { + GRPC_ERROR_UNREF(calld->cancel_error); + calld->cancel_error = + GRPC_ERROR_REF(batch->payload->cancel_stream.cancel_error); + if (calld->send_message_batch != NULL) { + if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) { + GRPC_CALL_COMBINER_START( + exec_ctx, calld->call_combiner, + GRPC_CLOSURE_CREATE(fail_send_message_batch_in_call_combiner, calld, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_REF(calld->cancel_error), "failing send_message op"); + } else { + grpc_byte_stream_shutdown( + exec_ctx, + calld->send_message_batch->payload->send_message.send_message, + GRPC_ERROR_REF(calld->cancel_error)); + } + } + } else if (calld->cancel_error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, batch, GRPC_ERROR_REF(calld->cancel_error), + calld->call_combiner); + goto done; + } + // Handle send_initial_metadata. + if (batch->send_initial_metadata) { + GPR_ASSERT(calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN); + bool has_compression_algorithm; + grpc_error *error = process_send_initial_metadata( + exec_ctx, elem, + batch->payload->send_initial_metadata.send_initial_metadata, + &has_compression_algorithm); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, batch, error, + calld->call_combiner); + goto done; + } + calld->send_initial_metadata_state = has_compression_algorithm + ? HAS_COMPRESSION_ALGORITHM + : NO_COMPRESSION_ALGORITHM; + // If we had previously received a batch containing a send_message op, + // handle it now. Note that we need to re-enter the call combiner + // for this, since we can't send two batches down while holding the + // call combiner, since the connected_channel filter (at the bottom of + // the call stack) will release the call combiner for each batch it sees. + if (calld->send_message_batch != NULL) { + GRPC_CALL_COMBINER_START( + exec_ctx, calld->call_combiner, + &calld->start_send_message_batch_in_call_combiner, GRPC_ERROR_NONE, + "starting send_message after send_initial_metadata"); + } + } + // Handle send_message. + if (batch->send_message) { + GPR_ASSERT(calld->send_message_batch == NULL); + calld->send_message_batch = batch; + // If we have not yet seen send_initial_metadata, then we have to + // wait. We save the batch in calld and then drop the call + // combiner, which we'll have to pick up again later when we get + // send_initial_metadata. + if (calld->send_initial_metadata_state == INITIAL_METADATA_UNSEEN) { + GRPC_CALL_COMBINER_STOP( + exec_ctx, calld->call_combiner, + "send_message batch pending send_initial_metadata"); + goto done; + } + start_send_message_batch(exec_ctx, elem, GRPC_ERROR_NONE); + } else { + // Pass control down the stack. + grpc_call_next_op(exec_ctx, elem, batch); + } +done: + GPR_TIMER_END("compress_start_transport_stream_op_batch", 0); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + calld->call_combiner = args->call_combiner; + calld->cancel_error = GRPC_ERROR_NONE; + grpc_slice_buffer_init(&calld->slices); + GRPC_CLOSURE_INIT(&calld->start_send_message_batch_in_call_combiner, + start_send_message_batch, elem, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->on_send_message_next_done, + on_send_message_next_done, elem, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->send_message_on_complete, send_message_on_complete, + elem, grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *calld = (call_data *)elem->call_data; + grpc_slice_buffer_destroy_internal(exec_ctx, &calld->slices); + GRPC_ERROR_UNREF(calld->cancel_error); +} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + channel_data *channeld = (channel_data *)elem->channel_data; + + /* Configuration for message compression */ + channeld->enabled_algorithms_bitset = + grpc_channel_args_compression_algorithm_get_states(args->channel_args); + + channeld->default_compression_algorithm = + grpc_channel_args_get_compression_algorithm(args->channel_args); + /* Make sure the default isn't disabled. */ + if (!GPR_BITGET(channeld->enabled_algorithms_bitset, + channeld->default_compression_algorithm)) { + gpr_log(GPR_DEBUG, + "compression algorithm %d not enabled: switching to none", + channeld->default_compression_algorithm); + channeld->default_compression_algorithm = GRPC_COMPRESS_NONE; + } + + channeld->supported_compression_algorithms = + (((1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1) & + channeld->enabled_algorithms_bitset) | + 1u; + + /* Configuration for stream compression */ + channeld->enabled_stream_compression_algorithms_bitset = + grpc_channel_args_stream_compression_algorithm_get_states( + args->channel_args); + + channeld->default_stream_compression_algorithm = + grpc_channel_args_get_stream_compression_algorithm(args->channel_args); + + if (!GPR_BITGET(channeld->enabled_stream_compression_algorithms_bitset, + channeld->default_stream_compression_algorithm)) { + gpr_log(GPR_DEBUG, + "stream compression algorithm %d not enabled: switching to none", + channeld->default_stream_compression_algorithm); + channeld->default_stream_compression_algorithm = GRPC_STREAM_COMPRESS_NONE; + } + + channeld->supported_stream_compression_algorithms = + (((1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 1) & + channeld->enabled_stream_compression_algorithms_bitset) | + 1u; + + GPR_ASSERT(!args->is_last); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) {} + +const grpc_channel_filter grpc_message_compress_filter = { + compress_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "message_compress"}; diff --git a/src/core/ext/filters/http/server/http_server_filter.c b/src/core/ext/filters/http/server/http_server_filter.c deleted file mode 100644 index 03958136b4..0000000000 --- a/src/core/ext/filters/http/server/http_server_filter.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/http/server/http_server_filter.h" - -#include -#include -#include -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/b64.h" -#include "src/core/lib/slice/percent_encoding.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/transport/static_metadata.h" - -#define EXPECTED_CONTENT_TYPE "application/grpc" -#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 - -typedef struct call_data { - grpc_call_combiner *call_combiner; - - grpc_linked_mdelem status; - grpc_linked_mdelem content_type; - - /* did this request come with path query containing request payload */ - bool seen_path_with_query; - /* flag to ensure payload_bin is delivered only once */ - bool payload_bin_delivered; - - grpc_metadata_batch *recv_initial_metadata; - uint32_t *recv_initial_metadata_flags; - /** Closure to call when finished with the hs_on_recv hook */ - grpc_closure *on_done_recv; - /** Closure to call when we retrieve read message from the path URI - */ - grpc_closure *recv_message_ready; - grpc_closure *on_complete; - grpc_byte_stream **pp_recv_message; - grpc_slice_buffer read_slice_buffer; - grpc_slice_buffer_stream read_stream; - - /** Receive closures are chained: we inject this closure as the on_done_recv - up-call on transport_op, and remember to call our on_done_recv member - after handling it. */ - grpc_closure hs_on_recv; - grpc_closure hs_on_complete; - grpc_closure hs_recv_message_ready; -} call_data; - -typedef struct channel_data { uint8_t unused; } channel_data; - -static grpc_error *server_filter_outgoing_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_metadata_batch *b) { - if (b->idx.named.grpc_message != NULL) { - grpc_slice pct_encoded_msg = grpc_percent_encode_slice( - GRPC_MDVALUE(b->idx.named.grpc_message->md), - grpc_compatible_percent_encoding_unreserved_bytes); - if (grpc_slice_is_equivalent(pct_encoded_msg, - GRPC_MDVALUE(b->idx.named.grpc_message->md))) { - grpc_slice_unref_internal(exec_ctx, pct_encoded_msg); - } else { - grpc_metadata_batch_set_value(exec_ctx, b->idx.named.grpc_message, - pct_encoded_msg); - } - } - return GRPC_ERROR_NONE; -} - -static void add_error(const char *error_name, grpc_error **cumulative, - grpc_error *new_err) { - if (new_err == GRPC_ERROR_NONE) return; - if (*cumulative == GRPC_ERROR_NONE) { - *cumulative = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_name); - } - *cumulative = grpc_error_add_child(*cumulative, new_err); -} - -static grpc_error *server_filter_incoming_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_metadata_batch *b) { - call_data *calld = (call_data *)elem->call_data; - grpc_error *error = GRPC_ERROR_NONE; - static const char *error_name = "Failed processing incoming headers"; - - if (b->idx.named.method != NULL) { - if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) { - *calld->recv_initial_metadata_flags &= - ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST | - GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST); - } else if (grpc_mdelem_eq(b->idx.named.method->md, - GRPC_MDELEM_METHOD_PUT)) { - *calld->recv_initial_metadata_flags &= - ~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; - *calld->recv_initial_metadata_flags |= - GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; - } else if (grpc_mdelem_eq(b->idx.named.method->md, - GRPC_MDELEM_METHOD_GET)) { - *calld->recv_initial_metadata_flags |= - GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; - *calld->recv_initial_metadata_flags &= - ~GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; - } else { - add_error(error_name, &error, - grpc_attach_md_to_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), - b->idx.named.method->md)); - } - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.method); - } else { - add_error( - error_name, &error, - grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), - GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":method"))); - } - - if (b->idx.named.te != NULL) { - if (!grpc_mdelem_eq(b->idx.named.te->md, GRPC_MDELEM_TE_TRAILERS)) { - add_error(error_name, &error, - grpc_attach_md_to_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), - b->idx.named.te->md)); - } - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.te); - } else { - add_error(error_name, &error, - grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), - GRPC_ERROR_STR_KEY, grpc_slice_from_static_string("te"))); - } - - if (b->idx.named.scheme != NULL) { - if (!grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) && - !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) && - !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_GRPC)) { - add_error(error_name, &error, - grpc_attach_md_to_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), - b->idx.named.scheme->md)); - } - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.scheme); - } else { - add_error( - error_name, &error, - grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), - GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":scheme"))); - } - - if (b->idx.named.content_type != NULL) { - if (!grpc_mdelem_eq(b->idx.named.content_type->md, - GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) { - if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md), - EXPECTED_CONTENT_TYPE, - EXPECTED_CONTENT_TYPE_LENGTH) && - (GRPC_SLICE_START_PTR(GRPC_MDVALUE( - b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == - '+' || - GRPC_SLICE_START_PTR(GRPC_MDVALUE( - b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == - ';')) { - /* Although the C implementation doesn't (currently) generate them, - any custom +-suffix is explicitly valid. */ - /* TODO(klempner): We should consider preallocating common values such - as +proto or +json, or at least stashing them if we see them. */ - /* TODO(klempner): Should we be surfacing this to application code? */ - } else { - /* TODO(klempner): We're currently allowing this, but we shouldn't - see it without a proxy so log for now. */ - char *val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md), - GPR_DUMP_ASCII); - gpr_log(GPR_INFO, "Unexpected content-type '%s'", val); - gpr_free(val); - } - } - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.content_type); - } - - if (b->idx.named.path == NULL) { - add_error(error_name, &error, - grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), - GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path"))); - } else if (*calld->recv_initial_metadata_flags & - GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) { - /* We have a cacheable request made with GET verb. The path contains the - * query parameter which is base64 encoded request payload. */ - const char k_query_separator = '?'; - grpc_slice path_slice = GRPC_MDVALUE(b->idx.named.path->md); - uint8_t *path_ptr = (uint8_t *)GRPC_SLICE_START_PTR(path_slice); - size_t path_length = GRPC_SLICE_LENGTH(path_slice); - /* offset of the character '?' */ - size_t offset = 0; - for (offset = 0; offset < path_length && *path_ptr != k_query_separator; - path_ptr++, offset++) - ; - if (offset < path_length) { - grpc_slice query_slice = - grpc_slice_sub(path_slice, offset + 1, path_length); - - /* substitute path metadata with just the path (not query) */ - grpc_mdelem mdelem_path_without_query = grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_PATH, grpc_slice_sub(path_slice, 0, offset)); - - grpc_metadata_batch_substitute(exec_ctx, b, b->idx.named.path, - mdelem_path_without_query); - - /* decode payload from query and add to the slice buffer to be returned */ - const int k_url_safe = 1; - grpc_slice_buffer_add( - &calld->read_slice_buffer, - grpc_base64_decode_with_len( - exec_ctx, (const char *)GRPC_SLICE_START_PTR(query_slice), - GRPC_SLICE_LENGTH(query_slice), k_url_safe)); - grpc_slice_buffer_stream_init(&calld->read_stream, - &calld->read_slice_buffer, 0); - calld->seen_path_with_query = true; - grpc_slice_unref_internal(exec_ctx, query_slice); - } else { - gpr_log(GPR_ERROR, "GET request without QUERY"); - } - } - - if (b->idx.named.host != NULL && b->idx.named.authority == NULL) { - grpc_linked_mdelem *el = b->idx.named.host; - grpc_mdelem md = GRPC_MDELEM_REF(el->md); - grpc_metadata_batch_remove(exec_ctx, b, el); - add_error( - error_name, &error, - grpc_metadata_batch_add_head( - exec_ctx, b, el, grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_AUTHORITY, - grpc_slice_ref_internal(GRPC_MDVALUE(md))))); - GRPC_MDELEM_UNREF(exec_ctx, md); - } - - if (b->idx.named.authority == NULL) { - add_error( - error_name, &error, - grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), - GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":authority"))); - } - - return error; -} - -static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *err) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - if (err == GRPC_ERROR_NONE) { - err = server_filter_incoming_metadata(exec_ctx, elem, - calld->recv_initial_metadata); - } else { - GRPC_ERROR_REF(err); - } - GRPC_CLOSURE_RUN(exec_ctx, calld->on_done_recv, err); -} - -static void hs_on_complete(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *err) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - /* Call recv_message_ready if we got the payload via the path field */ - if (calld->seen_path_with_query && calld->recv_message_ready != NULL) { - *calld->pp_recv_message = calld->payload_bin_delivered - ? NULL - : (grpc_byte_stream *)&calld->read_stream; - // Re-enter call combiner for recv_message_ready, since the surface - // code will release the call combiner for each callback it receives. - GRPC_CALL_COMBINER_START(exec_ctx, calld->call_combiner, - calld->recv_message_ready, GRPC_ERROR_REF(err), - "resuming recv_message_ready from on_complete"); - calld->recv_message_ready = NULL; - calld->payload_bin_delivered = true; - } - GRPC_CLOSURE_RUN(exec_ctx, calld->on_complete, GRPC_ERROR_REF(err)); -} - -static void hs_recv_message_ready(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *err) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - if (calld->seen_path_with_query) { - // Do nothing. This is probably a GET request, and payload will be - // returned in hs_on_complete callback. - // Note that we release the call combiner here, so that other - // callbacks can run. - GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, - "pausing recv_message_ready until on_complete"); - } else { - GRPC_CLOSURE_RUN(exec_ctx, calld->recv_message_ready, GRPC_ERROR_REF(err)); - } -} - -static grpc_error *hs_mutate_op(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - /* grab pointers to our data from the call element */ - call_data *calld = (call_data *)elem->call_data; - - if (op->send_initial_metadata) { - grpc_error *error = GRPC_ERROR_NONE; - static const char *error_name = "Failed sending initial metadata"; - add_error( - error_name, &error, - grpc_metadata_batch_add_head( - exec_ctx, op->payload->send_initial_metadata.send_initial_metadata, - &calld->status, GRPC_MDELEM_STATUS_200)); - add_error( - error_name, &error, - grpc_metadata_batch_add_tail( - exec_ctx, op->payload->send_initial_metadata.send_initial_metadata, - &calld->content_type, - GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)); - add_error(error_name, &error, - server_filter_outgoing_metadata( - exec_ctx, elem, - op->payload->send_initial_metadata.send_initial_metadata)); - if (error != GRPC_ERROR_NONE) return error; - } - - if (op->recv_initial_metadata) { - /* substitute our callback for the higher callback */ - GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags != NULL); - calld->recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata; - calld->recv_initial_metadata_flags = - op->payload->recv_initial_metadata.recv_flags; - calld->on_done_recv = - op->payload->recv_initial_metadata.recv_initial_metadata_ready; - op->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->hs_on_recv; - } - - if (op->recv_message) { - calld->recv_message_ready = op->payload->recv_message.recv_message_ready; - calld->pp_recv_message = op->payload->recv_message.recv_message; - if (op->payload->recv_message.recv_message_ready) { - op->payload->recv_message.recv_message_ready = - &calld->hs_recv_message_ready; - } - if (op->on_complete) { - calld->on_complete = op->on_complete; - op->on_complete = &calld->hs_on_complete; - } - } - - if (op->send_trailing_metadata) { - grpc_error *error = server_filter_outgoing_metadata( - exec_ctx, elem, - op->payload->send_trailing_metadata.send_trailing_metadata); - if (error != GRPC_ERROR_NONE) return error; - } - - return GRPC_ERROR_NONE; -} - -static void hs_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - call_data *calld = (call_data *)elem->call_data; - GPR_TIMER_BEGIN("hs_start_transport_stream_op_batch", 0); - grpc_error *error = hs_mutate_op(exec_ctx, elem, op); - if (error != GRPC_ERROR_NONE) { - grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error, - calld->call_combiner); - } else { - grpc_call_next_op(exec_ctx, elem, op); - } - GPR_TIMER_END("hs_start_transport_stream_op_batch", 0); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - /* grab pointers to our data from the call element */ - call_data *calld = (call_data *)elem->call_data; - /* initialize members */ - calld->call_combiner = args->call_combiner; - GRPC_CLOSURE_INIT(&calld->hs_on_recv, hs_on_recv, elem, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->hs_on_complete, hs_on_complete, elem, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&calld->hs_recv_message_ready, hs_recv_message_ready, elem, - grpc_schedule_on_exec_ctx); - grpc_slice_buffer_init(&calld->read_slice_buffer); - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *calld = (call_data *)elem->call_data; - grpc_slice_buffer_destroy_internal(exec_ctx, &calld->read_slice_buffer); -} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - GPR_ASSERT(!args->is_last); - return GRPC_ERROR_NONE; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) {} - -const grpc_channel_filter grpc_http_server_filter = { - hs_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "http-server"}; diff --git a/src/core/ext/filters/http/server/http_server_filter.cc b/src/core/ext/filters/http/server/http_server_filter.cc new file mode 100644 index 0000000000..03958136b4 --- /dev/null +++ b/src/core/ext/filters/http/server/http_server_filter.cc @@ -0,0 +1,440 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/http/server/http_server_filter.h" + +#include +#include +#include +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/percent_encoding.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/static_metadata.h" + +#define EXPECTED_CONTENT_TYPE "application/grpc" +#define EXPECTED_CONTENT_TYPE_LENGTH sizeof(EXPECTED_CONTENT_TYPE) - 1 + +typedef struct call_data { + grpc_call_combiner *call_combiner; + + grpc_linked_mdelem status; + grpc_linked_mdelem content_type; + + /* did this request come with path query containing request payload */ + bool seen_path_with_query; + /* flag to ensure payload_bin is delivered only once */ + bool payload_bin_delivered; + + grpc_metadata_batch *recv_initial_metadata; + uint32_t *recv_initial_metadata_flags; + /** Closure to call when finished with the hs_on_recv hook */ + grpc_closure *on_done_recv; + /** Closure to call when we retrieve read message from the path URI + */ + grpc_closure *recv_message_ready; + grpc_closure *on_complete; + grpc_byte_stream **pp_recv_message; + grpc_slice_buffer read_slice_buffer; + grpc_slice_buffer_stream read_stream; + + /** Receive closures are chained: we inject this closure as the on_done_recv + up-call on transport_op, and remember to call our on_done_recv member + after handling it. */ + grpc_closure hs_on_recv; + grpc_closure hs_on_complete; + grpc_closure hs_recv_message_ready; +} call_data; + +typedef struct channel_data { uint8_t unused; } channel_data; + +static grpc_error *server_filter_outgoing_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_metadata_batch *b) { + if (b->idx.named.grpc_message != NULL) { + grpc_slice pct_encoded_msg = grpc_percent_encode_slice( + GRPC_MDVALUE(b->idx.named.grpc_message->md), + grpc_compatible_percent_encoding_unreserved_bytes); + if (grpc_slice_is_equivalent(pct_encoded_msg, + GRPC_MDVALUE(b->idx.named.grpc_message->md))) { + grpc_slice_unref_internal(exec_ctx, pct_encoded_msg); + } else { + grpc_metadata_batch_set_value(exec_ctx, b->idx.named.grpc_message, + pct_encoded_msg); + } + } + return GRPC_ERROR_NONE; +} + +static void add_error(const char *error_name, grpc_error **cumulative, + grpc_error *new_err) { + if (new_err == GRPC_ERROR_NONE) return; + if (*cumulative == GRPC_ERROR_NONE) { + *cumulative = GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_name); + } + *cumulative = grpc_error_add_child(*cumulative, new_err); +} + +static grpc_error *server_filter_incoming_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_metadata_batch *b) { + call_data *calld = (call_data *)elem->call_data; + grpc_error *error = GRPC_ERROR_NONE; + static const char *error_name = "Failed processing incoming headers"; + + if (b->idx.named.method != NULL) { + if (grpc_mdelem_eq(b->idx.named.method->md, GRPC_MDELEM_METHOD_POST)) { + *calld->recv_initial_metadata_flags &= + ~(GRPC_INITIAL_METADATA_CACHEABLE_REQUEST | + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST); + } else if (grpc_mdelem_eq(b->idx.named.method->md, + GRPC_MDELEM_METHOD_PUT)) { + *calld->recv_initial_metadata_flags &= + ~GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; + *calld->recv_initial_metadata_flags |= + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + } else if (grpc_mdelem_eq(b->idx.named.method->md, + GRPC_MDELEM_METHOD_GET)) { + *calld->recv_initial_metadata_flags |= + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST; + *calld->recv_initial_metadata_flags &= + ~GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + } else { + add_error(error_name, &error, + grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), + b->idx.named.method->md)); + } + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.method); + } else { + add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":method"))); + } + + if (b->idx.named.te != NULL) { + if (!grpc_mdelem_eq(b->idx.named.te->md, GRPC_MDELEM_TE_TRAILERS)) { + add_error(error_name, &error, + grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), + b->idx.named.te->md)); + } + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.te); + } else { + add_error(error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string("te"))); + } + + if (b->idx.named.scheme != NULL) { + if (!grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTP) && + !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_HTTPS) && + !grpc_mdelem_eq(b->idx.named.scheme->md, GRPC_MDELEM_SCHEME_GRPC)) { + add_error(error_name, &error, + grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad header"), + b->idx.named.scheme->md)); + } + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.scheme); + } else { + add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":scheme"))); + } + + if (b->idx.named.content_type != NULL) { + if (!grpc_mdelem_eq(b->idx.named.content_type->md, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)) { + if (grpc_slice_buf_start_eq(GRPC_MDVALUE(b->idx.named.content_type->md), + EXPECTED_CONTENT_TYPE, + EXPECTED_CONTENT_TYPE_LENGTH) && + (GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + '+' || + GRPC_SLICE_START_PTR(GRPC_MDVALUE( + b->idx.named.content_type->md))[EXPECTED_CONTENT_TYPE_LENGTH] == + ';')) { + /* Although the C implementation doesn't (currently) generate them, + any custom +-suffix is explicitly valid. */ + /* TODO(klempner): We should consider preallocating common values such + as +proto or +json, or at least stashing them if we see them. */ + /* TODO(klempner): Should we be surfacing this to application code? */ + } else { + /* TODO(klempner): We're currently allowing this, but we shouldn't + see it without a proxy so log for now. */ + char *val = grpc_dump_slice(GRPC_MDVALUE(b->idx.named.content_type->md), + GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "Unexpected content-type '%s'", val); + gpr_free(val); + } + } + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.content_type); + } + + if (b->idx.named.path == NULL) { + add_error(error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":path"))); + } else if (*calld->recv_initial_metadata_flags & + GRPC_INITIAL_METADATA_CACHEABLE_REQUEST) { + /* We have a cacheable request made with GET verb. The path contains the + * query parameter which is base64 encoded request payload. */ + const char k_query_separator = '?'; + grpc_slice path_slice = GRPC_MDVALUE(b->idx.named.path->md); + uint8_t *path_ptr = (uint8_t *)GRPC_SLICE_START_PTR(path_slice); + size_t path_length = GRPC_SLICE_LENGTH(path_slice); + /* offset of the character '?' */ + size_t offset = 0; + for (offset = 0; offset < path_length && *path_ptr != k_query_separator; + path_ptr++, offset++) + ; + if (offset < path_length) { + grpc_slice query_slice = + grpc_slice_sub(path_slice, offset + 1, path_length); + + /* substitute path metadata with just the path (not query) */ + grpc_mdelem mdelem_path_without_query = grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_PATH, grpc_slice_sub(path_slice, 0, offset)); + + grpc_metadata_batch_substitute(exec_ctx, b, b->idx.named.path, + mdelem_path_without_query); + + /* decode payload from query and add to the slice buffer to be returned */ + const int k_url_safe = 1; + grpc_slice_buffer_add( + &calld->read_slice_buffer, + grpc_base64_decode_with_len( + exec_ctx, (const char *)GRPC_SLICE_START_PTR(query_slice), + GRPC_SLICE_LENGTH(query_slice), k_url_safe)); + grpc_slice_buffer_stream_init(&calld->read_stream, + &calld->read_slice_buffer, 0); + calld->seen_path_with_query = true; + grpc_slice_unref_internal(exec_ctx, query_slice); + } else { + gpr_log(GPR_ERROR, "GET request without QUERY"); + } + } + + if (b->idx.named.host != NULL && b->idx.named.authority == NULL) { + grpc_linked_mdelem *el = b->idx.named.host; + grpc_mdelem md = GRPC_MDELEM_REF(el->md); + grpc_metadata_batch_remove(exec_ctx, b, el); + add_error( + error_name, &error, + grpc_metadata_batch_add_head( + exec_ctx, b, el, grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_ref_internal(GRPC_MDVALUE(md))))); + GRPC_MDELEM_UNREF(exec_ctx, md); + } + + if (b->idx.named.authority == NULL) { + add_error( + error_name, &error, + grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing header"), + GRPC_ERROR_STR_KEY, grpc_slice_from_static_string(":authority"))); + } + + return error; +} + +static void hs_on_recv(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *err) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + if (err == GRPC_ERROR_NONE) { + err = server_filter_incoming_metadata(exec_ctx, elem, + calld->recv_initial_metadata); + } else { + GRPC_ERROR_REF(err); + } + GRPC_CLOSURE_RUN(exec_ctx, calld->on_done_recv, err); +} + +static void hs_on_complete(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *err) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + /* Call recv_message_ready if we got the payload via the path field */ + if (calld->seen_path_with_query && calld->recv_message_ready != NULL) { + *calld->pp_recv_message = calld->payload_bin_delivered + ? NULL + : (grpc_byte_stream *)&calld->read_stream; + // Re-enter call combiner for recv_message_ready, since the surface + // code will release the call combiner for each callback it receives. + GRPC_CALL_COMBINER_START(exec_ctx, calld->call_combiner, + calld->recv_message_ready, GRPC_ERROR_REF(err), + "resuming recv_message_ready from on_complete"); + calld->recv_message_ready = NULL; + calld->payload_bin_delivered = true; + } + GRPC_CLOSURE_RUN(exec_ctx, calld->on_complete, GRPC_ERROR_REF(err)); +} + +static void hs_recv_message_ready(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *err) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + if (calld->seen_path_with_query) { + // Do nothing. This is probably a GET request, and payload will be + // returned in hs_on_complete callback. + // Note that we release the call combiner here, so that other + // callbacks can run. + GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, + "pausing recv_message_ready until on_complete"); + } else { + GRPC_CLOSURE_RUN(exec_ctx, calld->recv_message_ready, GRPC_ERROR_REF(err)); + } +} + +static grpc_error *hs_mutate_op(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + /* grab pointers to our data from the call element */ + call_data *calld = (call_data *)elem->call_data; + + if (op->send_initial_metadata) { + grpc_error *error = GRPC_ERROR_NONE; + static const char *error_name = "Failed sending initial metadata"; + add_error( + error_name, &error, + grpc_metadata_batch_add_head( + exec_ctx, op->payload->send_initial_metadata.send_initial_metadata, + &calld->status, GRPC_MDELEM_STATUS_200)); + add_error( + error_name, &error, + grpc_metadata_batch_add_tail( + exec_ctx, op->payload->send_initial_metadata.send_initial_metadata, + &calld->content_type, + GRPC_MDELEM_CONTENT_TYPE_APPLICATION_SLASH_GRPC)); + add_error(error_name, &error, + server_filter_outgoing_metadata( + exec_ctx, elem, + op->payload->send_initial_metadata.send_initial_metadata)); + if (error != GRPC_ERROR_NONE) return error; + } + + if (op->recv_initial_metadata) { + /* substitute our callback for the higher callback */ + GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags != NULL); + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + calld->recv_initial_metadata_flags = + op->payload->recv_initial_metadata.recv_flags; + calld->on_done_recv = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->hs_on_recv; + } + + if (op->recv_message) { + calld->recv_message_ready = op->payload->recv_message.recv_message_ready; + calld->pp_recv_message = op->payload->recv_message.recv_message; + if (op->payload->recv_message.recv_message_ready) { + op->payload->recv_message.recv_message_ready = + &calld->hs_recv_message_ready; + } + if (op->on_complete) { + calld->on_complete = op->on_complete; + op->on_complete = &calld->hs_on_complete; + } + } + + if (op->send_trailing_metadata) { + grpc_error *error = server_filter_outgoing_metadata( + exec_ctx, elem, + op->payload->send_trailing_metadata.send_trailing_metadata); + if (error != GRPC_ERROR_NONE) return error; + } + + return GRPC_ERROR_NONE; +} + +static void hs_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + call_data *calld = (call_data *)elem->call_data; + GPR_TIMER_BEGIN("hs_start_transport_stream_op_batch", 0); + grpc_error *error = hs_mutate_op(exec_ctx, elem, op); + if (error != GRPC_ERROR_NONE) { + grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, op, error, + calld->call_combiner); + } else { + grpc_call_next_op(exec_ctx, elem, op); + } + GPR_TIMER_END("hs_start_transport_stream_op_batch", 0); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + /* grab pointers to our data from the call element */ + call_data *calld = (call_data *)elem->call_data; + /* initialize members */ + calld->call_combiner = args->call_combiner; + GRPC_CLOSURE_INIT(&calld->hs_on_recv, hs_on_recv, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->hs_on_complete, hs_on_complete, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&calld->hs_recv_message_ready, hs_recv_message_ready, elem, + grpc_schedule_on_exec_ctx); + grpc_slice_buffer_init(&calld->read_slice_buffer); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *calld = (call_data *)elem->call_data; + grpc_slice_buffer_destroy_internal(exec_ctx, &calld->read_slice_buffer); +} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + GPR_ASSERT(!args->is_last); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) {} + +const grpc_channel_filter grpc_http_server_filter = { + hs_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "http-server"}; diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.c b/src/core/ext/filters/load_reporting/server_load_reporting_filter.c deleted file mode 100644 index ca8a3b2a13..0000000000 --- a/src/core/ext/filters/load_reporting/server_load_reporting_filter.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h" -#include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/transport/static_metadata.h" - -typedef struct call_data { - intptr_t id; /**< an id unique to the call */ - bool have_trailing_md_string; - grpc_slice trailing_md_string; - bool have_initial_md_string; - grpc_slice initial_md_string; - bool have_service_method; - grpc_slice service_method; - - /* stores the recv_initial_metadata op's ready closure, which we wrap with our - * own (on_initial_md_ready) in order to capture the incoming initial metadata - * */ - grpc_closure *ops_recv_initial_metadata_ready; - - /* to get notified of the availability of the incoming initial metadata. */ - grpc_closure on_initial_md_ready; - grpc_metadata_batch *recv_initial_metadata; -} call_data; - -typedef struct channel_data { - intptr_t id; /**< an id unique to the channel */ -} channel_data; - -static void on_initial_md_ready(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *err) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - - if (err == GRPC_ERROR_NONE) { - if (calld->recv_initial_metadata->idx.named.path != NULL) { - calld->service_method = grpc_slice_ref_internal( - GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md)); - calld->have_service_method = true; - } else { - err = grpc_error_add_child( - err, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing :path header")); - } - if (calld->recv_initial_metadata->idx.named.lb_token != NULL) { - calld->initial_md_string = grpc_slice_ref_internal( - GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.lb_token->md)); - calld->have_initial_md_string = true; - grpc_metadata_batch_remove( - exec_ctx, calld->recv_initial_metadata, - calld->recv_initial_metadata->idx.named.lb_token); - } - } else { - GRPC_ERROR_REF(err); - } - calld->ops_recv_initial_metadata_ready->cb( - exec_ctx, calld->ops_recv_initial_metadata_ready->cb_arg, err); - GRPC_ERROR_UNREF(err); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - calld->id = (intptr_t)args->call_stack; - GRPC_CLOSURE_INIT(&calld->on_initial_md_ready, on_initial_md_ready, elem, - grpc_schedule_on_exec_ctx); - - /* TODO(dgq): do something with the data - channel_data *chand = elem->channel_data; - grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_CREATION, - (intptr_t)chand->id, - (intptr_t)calld->id, - NULL, - NULL, - NULL, - NULL}; - */ - - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *calld = (call_data *)elem->call_data; - - /* TODO(dgq): do something with the data - channel_data *chand = elem->channel_data; - grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_DESTRUCTION, - (intptr_t)chand->id, - (intptr_t)calld->id, - final_info, - calld->initial_md_string, - calld->trailing_md_string, - calld->service_method}; - */ - - if (calld->have_initial_md_string) { - grpc_slice_unref_internal(exec_ctx, calld->initial_md_string); - } - if (calld->have_trailing_md_string) { - grpc_slice_unref_internal(exec_ctx, calld->trailing_md_string); - } - if (calld->have_service_method) { - grpc_slice_unref_internal(exec_ctx, calld->service_method); - } -} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - GPR_ASSERT(!args->is_last); - - channel_data *chand = (channel_data *)elem->channel_data; - chand->id = (intptr_t)args->channel_stack; - - /* TODO(dgq): do something with the data - grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CHANNEL_CREATION, - (intptr_t)chand, - 0, - NULL, - NULL, - NULL, - NULL}; - */ - - return GRPC_ERROR_NONE; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - /* TODO(dgq): do something with the data - channel_data *chand = elem->channel_data; - grpc_load_reporting_call_data lr_call_data = { - GRPC_LR_POINT_CHANNEL_DESTRUCTION, - (intptr_t)chand->id, - 0, - NULL, - NULL, - NULL, - NULL}; - */ -} - -static grpc_filtered_mdelem lr_trailing_md_filter(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_mdelem md) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_LB_COST_BIN)) { - calld->trailing_md_string = GRPC_MDVALUE(md); - return GRPC_FILTERED_REMOVE(); - } - return GRPC_FILTERED_MDELEM(md); -} - -static void lr_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - GPR_TIMER_BEGIN("lr_start_transport_stream_op_batch", 0); - call_data *calld = (call_data *)elem->call_data; - - if (op->recv_initial_metadata) { - /* substitute our callback for the higher callback */ - calld->recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata; - calld->ops_recv_initial_metadata_ready = - op->payload->recv_initial_metadata.recv_initial_metadata_ready; - op->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->on_initial_md_ready; - } else if (op->send_trailing_metadata) { - GRPC_LOG_IF_ERROR( - "grpc_metadata_batch_filter", - grpc_metadata_batch_filter( - exec_ctx, - op->payload->send_trailing_metadata.send_trailing_metadata, - lr_trailing_md_filter, elem, - "LR trailing metadata filtering error")); - } - grpc_call_next_op(exec_ctx, elem, op); - - GPR_TIMER_END("lr_start_transport_stream_op_batch", 0); -} - -const grpc_channel_filter grpc_server_load_reporting_filter = { - lr_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "load_reporting"}; diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc new file mode 100644 index 0000000000..ca8a3b2a13 --- /dev/null +++ b/src/core/ext/filters/load_reporting/server_load_reporting_filter.cc @@ -0,0 +1,227 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h" +#include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/static_metadata.h" + +typedef struct call_data { + intptr_t id; /**< an id unique to the call */ + bool have_trailing_md_string; + grpc_slice trailing_md_string; + bool have_initial_md_string; + grpc_slice initial_md_string; + bool have_service_method; + grpc_slice service_method; + + /* stores the recv_initial_metadata op's ready closure, which we wrap with our + * own (on_initial_md_ready) in order to capture the incoming initial metadata + * */ + grpc_closure *ops_recv_initial_metadata_ready; + + /* to get notified of the availability of the incoming initial metadata. */ + grpc_closure on_initial_md_ready; + grpc_metadata_batch *recv_initial_metadata; +} call_data; + +typedef struct channel_data { + intptr_t id; /**< an id unique to the channel */ +} channel_data; + +static void on_initial_md_ready(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *err) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + + if (err == GRPC_ERROR_NONE) { + if (calld->recv_initial_metadata->idx.named.path != NULL) { + calld->service_method = grpc_slice_ref_internal( + GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md)); + calld->have_service_method = true; + } else { + err = grpc_error_add_child( + err, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Missing :path header")); + } + if (calld->recv_initial_metadata->idx.named.lb_token != NULL) { + calld->initial_md_string = grpc_slice_ref_internal( + GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.lb_token->md)); + calld->have_initial_md_string = true; + grpc_metadata_batch_remove( + exec_ctx, calld->recv_initial_metadata, + calld->recv_initial_metadata->idx.named.lb_token); + } + } else { + GRPC_ERROR_REF(err); + } + calld->ops_recv_initial_metadata_ready->cb( + exec_ctx, calld->ops_recv_initial_metadata_ready->cb_arg, err); + GRPC_ERROR_UNREF(err); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + calld->id = (intptr_t)args->call_stack; + GRPC_CLOSURE_INIT(&calld->on_initial_md_ready, on_initial_md_ready, elem, + grpc_schedule_on_exec_ctx); + + /* TODO(dgq): do something with the data + channel_data *chand = elem->channel_data; + grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_CREATION, + (intptr_t)chand->id, + (intptr_t)calld->id, + NULL, + NULL, + NULL, + NULL}; + */ + + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *calld = (call_data *)elem->call_data; + + /* TODO(dgq): do something with the data + channel_data *chand = elem->channel_data; + grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CALL_DESTRUCTION, + (intptr_t)chand->id, + (intptr_t)calld->id, + final_info, + calld->initial_md_string, + calld->trailing_md_string, + calld->service_method}; + */ + + if (calld->have_initial_md_string) { + grpc_slice_unref_internal(exec_ctx, calld->initial_md_string); + } + if (calld->have_trailing_md_string) { + grpc_slice_unref_internal(exec_ctx, calld->trailing_md_string); + } + if (calld->have_service_method) { + grpc_slice_unref_internal(exec_ctx, calld->service_method); + } +} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + GPR_ASSERT(!args->is_last); + + channel_data *chand = (channel_data *)elem->channel_data; + chand->id = (intptr_t)args->channel_stack; + + /* TODO(dgq): do something with the data + grpc_load_reporting_call_data lr_call_data = {GRPC_LR_POINT_CHANNEL_CREATION, + (intptr_t)chand, + 0, + NULL, + NULL, + NULL, + NULL}; + */ + + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + /* TODO(dgq): do something with the data + channel_data *chand = elem->channel_data; + grpc_load_reporting_call_data lr_call_data = { + GRPC_LR_POINT_CHANNEL_DESTRUCTION, + (intptr_t)chand->id, + 0, + NULL, + NULL, + NULL, + NULL}; + */ +} + +static grpc_filtered_mdelem lr_trailing_md_filter(grpc_exec_ctx *exec_ctx, + void *user_data, + grpc_mdelem md) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_LB_COST_BIN)) { + calld->trailing_md_string = GRPC_MDVALUE(md); + return GRPC_FILTERED_REMOVE(); + } + return GRPC_FILTERED_MDELEM(md); +} + +static void lr_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + GPR_TIMER_BEGIN("lr_start_transport_stream_op_batch", 0); + call_data *calld = (call_data *)elem->call_data; + + if (op->recv_initial_metadata) { + /* substitute our callback for the higher callback */ + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + calld->ops_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->on_initial_md_ready; + } else if (op->send_trailing_metadata) { + GRPC_LOG_IF_ERROR( + "grpc_metadata_batch_filter", + grpc_metadata_batch_filter( + exec_ctx, + op->payload->send_trailing_metadata.send_trailing_metadata, + lr_trailing_md_filter, elem, + "LR trailing metadata filtering error")); + } + grpc_call_next_op(exec_ctx, elem, op); + + GPR_TIMER_END("lr_start_transport_stream_op_batch", 0); +} + +const grpc_channel_filter grpc_server_load_reporting_filter = { + lr_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "load_reporting"}; diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.c b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.c deleted file mode 100644 index 2486ead427..0000000000 --- a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include -#include -#include - -#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h" -#include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel_init.h" - -static bool is_load_reporting_enabled(const grpc_channel_args *a) { - return grpc_channel_arg_get_bool( - grpc_channel_args_find(a, GRPC_ARG_ENABLE_LOAD_REPORTING), false); -} - -static bool maybe_add_server_load_reporting_filter( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { - const grpc_channel_args *args = - grpc_channel_stack_builder_get_channel_arguments(builder); - const grpc_channel_filter *filter = (const grpc_channel_filter *)arg; - grpc_channel_stack_builder_iterator *it = - grpc_channel_stack_builder_iterator_find(builder, filter->name); - const bool already_has_load_reporting_filter = - !grpc_channel_stack_builder_iterator_is_end(it); - grpc_channel_stack_builder_iterator_destroy(it); - if (is_load_reporting_enabled(args) && !already_has_load_reporting_filter) { - return grpc_channel_stack_builder_prepend_filter(builder, filter, NULL, - NULL); - } - return true; -} - -grpc_arg grpc_load_reporting_enable_arg() { - return grpc_channel_arg_integer_create((char *)GRPC_ARG_ENABLE_LOAD_REPORTING, - 1); -} - -/* Plugin registration */ - -void grpc_server_load_reporting_plugin_init(void) { - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, - maybe_add_server_load_reporting_filter, - (void *)&grpc_server_load_reporting_filter); -} - -void grpc_server_load_reporting_plugin_shutdown() {} diff --git a/src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc new file mode 100644 index 0000000000..223fb3ee8b --- /dev/null +++ b/src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc @@ -0,0 +1,70 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include + +#include "src/core/ext/filters/load_reporting/server_load_reporting_filter.h" +#include "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" + +static bool is_load_reporting_enabled(const grpc_channel_args *a) { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(a, GRPC_ARG_ENABLE_LOAD_REPORTING), false); +} + +static bool maybe_add_server_load_reporting_filter( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { + const grpc_channel_args *args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_channel_filter *filter = (const grpc_channel_filter *)arg; + grpc_channel_stack_builder_iterator *it = + grpc_channel_stack_builder_iterator_find(builder, filter->name); + const bool already_has_load_reporting_filter = + !grpc_channel_stack_builder_iterator_is_end(it); + grpc_channel_stack_builder_iterator_destroy(it); + if (is_load_reporting_enabled(args) && !already_has_load_reporting_filter) { + return grpc_channel_stack_builder_prepend_filter(builder, filter, NULL, + NULL); + } + return true; +} + +grpc_arg grpc_load_reporting_enable_arg() { + return grpc_channel_arg_integer_create((char *)GRPC_ARG_ENABLE_LOAD_REPORTING, + 1); +} + +/* Plugin registration */ + +extern "C" void grpc_server_load_reporting_plugin_init(void) { + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, + maybe_add_server_load_reporting_filter, + (void *)&grpc_server_load_reporting_filter); +} + +extern "C" void grpc_server_load_reporting_plugin_shutdown() {} diff --git a/src/core/ext/filters/max_age/max_age_filter.c b/src/core/ext/filters/max_age/max_age_filter.c deleted file mode 100644 index 0ac803ed41..0000000000 --- a/src/core/ext/filters/max_age/max_age_filter.c +++ /dev/null @@ -1,423 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/filters/max_age/max_age_filter.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/http2_errors.h" - -#define DEFAULT_MAX_CONNECTION_AGE_MS INT_MAX -#define DEFAULT_MAX_CONNECTION_AGE_GRACE_MS INT_MAX -#define DEFAULT_MAX_CONNECTION_IDLE_MS INT_MAX -#define MAX_CONNECTION_AGE_JITTER 0.1 - -#define MAX_CONNECTION_AGE_INTEGER_OPTIONS \ - (grpc_integer_options) { DEFAULT_MAX_CONNECTION_AGE_MS, 1, INT_MAX } -#define MAX_CONNECTION_IDLE_INTEGER_OPTIONS \ - (grpc_integer_options) { DEFAULT_MAX_CONNECTION_IDLE_MS, 1, INT_MAX } - -typedef struct channel_data { - /* We take a reference to the channel stack for the timer callback */ - grpc_channel_stack* channel_stack; - /* Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer - and max_age_grace_timer_pending */ - gpr_mu max_age_timer_mu; - /* True if the max_age timer callback is currently pending */ - bool max_age_timer_pending; - /* True if the max_age_grace timer callback is currently pending */ - bool max_age_grace_timer_pending; - /* The timer for checking if the channel has reached its max age */ - grpc_timer max_age_timer; - /* The timer for checking if the max-aged channel has uesed up the grace - period */ - grpc_timer max_age_grace_timer; - /* The timer for checking if the channel's idle duration reaches - max_connection_idle */ - grpc_timer max_idle_timer; - /* Allowed max time a channel may have no outstanding rpcs */ - gpr_timespec max_connection_idle; - /* Allowed max time a channel may exist */ - gpr_timespec max_connection_age; - /* Allowed grace period after the channel reaches its max age */ - gpr_timespec max_connection_age_grace; - /* Closure to run when the channel's idle duration reaches max_connection_idle - and should be closed gracefully */ - grpc_closure close_max_idle_channel; - /* Closure to run when the channel reaches its max age and should be closed - gracefully */ - grpc_closure close_max_age_channel; - /* Closure to run the channel uses up its max age grace time and should be - closed forcibly */ - grpc_closure force_close_max_age_channel; - /* Closure to run when the init fo channel stack is done and the max_idle - timer should be started */ - grpc_closure start_max_idle_timer_after_init; - /* Closure to run when the init fo channel stack is done and the max_age timer - should be started */ - grpc_closure start_max_age_timer_after_init; - /* Closure to run when the goaway op is finished and the max_age_timer */ - grpc_closure start_max_age_grace_timer_after_goaway_op; - /* Closure to run when the channel connectivity state changes */ - grpc_closure channel_connectivity_changed; - /* Records the current connectivity state */ - grpc_connectivity_state connectivity_state; - /* Number of active calls */ - gpr_atm call_count; -} channel_data; - -/* Increase the nubmer of active calls. Before the increasement, if there are no - calls, the max_idle_timer should be cancelled. */ -static void increase_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) { - if (gpr_atm_full_fetch_add(&chand->call_count, 1) == 0) { - grpc_timer_cancel(exec_ctx, &chand->max_idle_timer); - } -} - -/* Decrease the nubmer of active calls. After the decrement, if there are no - calls, the max_idle_timer should be started. */ -static void decrease_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) { - if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) { - GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_idle_timer"); - grpc_timer_init( - exec_ctx, &chand->max_idle_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_idle), - &chand->close_max_idle_channel, gpr_now(GPR_CLOCK_MONOTONIC)); - } -} - -static void start_max_idle_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - /* Decrease call_count. If there are no active calls at this time, - max_idle_timer will start here. If the number of active calls is not 0, - max_idle_timer will start after all the active calls end. */ - decrease_call_count(exec_ctx, chand); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age start_max_idle_timer_after_init"); -} - -static void start_max_age_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - gpr_mu_lock(&chand->max_age_timer_mu); - chand->max_age_timer_pending = true; - GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_timer"); - grpc_timer_init( - exec_ctx, &chand->max_age_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_age), - &chand->close_max_age_channel, gpr_now(GPR_CLOCK_MONOTONIC)); - gpr_mu_unlock(&chand->max_age_timer_mu); - grpc_transport_op* op = grpc_make_transport_op(NULL); - op->on_connectivity_state_change = &chand->channel_connectivity_changed, - op->connectivity_state = &chand->connectivity_state; - grpc_channel_next_op(exec_ctx, - grpc_channel_stack_element(chand->channel_stack, 0), op); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age start_max_age_timer_after_init"); -} - -static void start_max_age_grace_timer_after_goaway_op(grpc_exec_ctx* exec_ctx, - void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - gpr_mu_lock(&chand->max_age_timer_mu); - chand->max_age_grace_timer_pending = true; - GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_grace_timer"); - grpc_timer_init(exec_ctx, &chand->max_age_grace_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), - chand->max_connection_age_grace), - &chand->force_close_max_age_channel, - gpr_now(GPR_CLOCK_MONOTONIC)); - gpr_mu_unlock(&chand->max_age_timer_mu); - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age start_max_age_grace_timer_after_goaway_op"); -} - -static void close_max_idle_channel(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - if (error == GRPC_ERROR_NONE) { - /* Prevent the max idle timer from being set again */ - gpr_atm_no_barrier_fetch_add(&chand->call_count, 1); - grpc_transport_op* op = grpc_make_transport_op(NULL); - op->goaway_error = - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_idle"), - GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); - grpc_channel_element* elem = - grpc_channel_stack_element(chand->channel_stack, 0); - elem->filter->start_transport_op(exec_ctx, elem, op); - } else if (error != GRPC_ERROR_CANCELLED) { - GRPC_LOG_IF_ERROR("close_max_idle_channel", error); - } - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age max_idle_timer"); -} - -static void close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - gpr_mu_lock(&chand->max_age_timer_mu); - chand->max_age_timer_pending = false; - gpr_mu_unlock(&chand->max_age_timer_mu); - if (error == GRPC_ERROR_NONE) { - GRPC_CHANNEL_STACK_REF(chand->channel_stack, - "max_age start_max_age_grace_timer_after_goaway_op"); - grpc_transport_op* op = grpc_make_transport_op( - &chand->start_max_age_grace_timer_after_goaway_op); - op->goaway_error = - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"), - GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); - grpc_channel_element* elem = - grpc_channel_stack_element(chand->channel_stack, 0); - elem->filter->start_transport_op(exec_ctx, elem, op); - } else if (error != GRPC_ERROR_CANCELLED) { - GRPC_LOG_IF_ERROR("close_max_age_channel", error); - } - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age max_age_timer"); -} - -static void force_close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - gpr_mu_lock(&chand->max_age_timer_mu); - chand->max_age_grace_timer_pending = false; - gpr_mu_unlock(&chand->max_age_timer_mu); - if (error == GRPC_ERROR_NONE) { - grpc_transport_op* op = grpc_make_transport_op(NULL); - op->disconnect_with_error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel reaches max age"); - grpc_channel_element* elem = - grpc_channel_stack_element(chand->channel_stack, 0); - elem->filter->start_transport_op(exec_ctx, elem, op); - } else if (error != GRPC_ERROR_CANCELLED) { - GRPC_LOG_IF_ERROR("force_close_max_age_channel", error); - } - GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, - "max_age max_age_grace_timer"); -} - -static void channel_connectivity_changed(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - channel_data* chand = (channel_data*)arg; - if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { - grpc_transport_op* op = grpc_make_transport_op(NULL); - op->on_connectivity_state_change = &chand->channel_connectivity_changed, - op->connectivity_state = &chand->connectivity_state; - grpc_channel_next_op( - exec_ctx, grpc_channel_stack_element(chand->channel_stack, 0), op); - } else { - gpr_mu_lock(&chand->max_age_timer_mu); - if (chand->max_age_timer_pending) { - grpc_timer_cancel(exec_ctx, &chand->max_age_timer); - chand->max_age_timer_pending = false; - } - if (chand->max_age_grace_timer_pending) { - grpc_timer_cancel(exec_ctx, &chand->max_age_grace_timer); - chand->max_age_grace_timer_pending = false; - } - gpr_mu_unlock(&chand->max_age_timer_mu); - /* If there are no active calls, this increasement will cancel - max_idle_timer, and prevent max_idle_timer from being started in the - future. */ - increase_call_count(exec_ctx, chand); - } -} - -/* A random jitter of +/-10% will be added to MAX_CONNECTION_AGE to spread out - connection storms. Note that the MAX_CONNECTION_AGE option without jitter - would not create connection storms by itself, but if there happened to be a - connection storm it could cause it to repeat at a fixed period. */ -static int add_random_max_connection_age_jitter(int value) { - /* generate a random number between 1 - MAX_CONNECTION_AGE_JITTER and - 1 + MAX_CONNECTION_AGE_JITTER */ - double multiplier = rand() * MAX_CONNECTION_AGE_JITTER * 2.0 / RAND_MAX + - 1.0 - MAX_CONNECTION_AGE_JITTER; - double result = multiplier * value; - /* INT_MAX - 0.5 converts the value to float, so that result will not be - cast to int implicitly before the comparison. */ - return result > INT_MAX - 0.5 ? INT_MAX : (int)result; -} - -/* Constructor for call_data. */ -static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, - grpc_call_element* elem, - const grpc_call_element_args* args) { - channel_data* chand = (channel_data*)elem->channel_data; - increase_call_count(exec_ctx, chand); - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data. */ -static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - const grpc_call_final_info* final_info, - grpc_closure* ignored) { - channel_data* chand = (channel_data*)elem->channel_data; - decrease_call_count(exec_ctx, chand); -} - -/* Constructor for channel_data. */ -static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem, - grpc_channel_element_args* args) { - channel_data* chand = (channel_data*)elem->channel_data; - gpr_mu_init(&chand->max_age_timer_mu); - chand->max_age_timer_pending = false; - chand->max_age_grace_timer_pending = false; - chand->channel_stack = args->channel_stack; - chand->max_connection_age = - DEFAULT_MAX_CONNECTION_AGE_MS == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(add_random_max_connection_age_jitter( - DEFAULT_MAX_CONNECTION_AGE_MS), - GPR_TIMESPAN); - chand->max_connection_age_grace = - DEFAULT_MAX_CONNECTION_AGE_GRACE_MS == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, - GPR_TIMESPAN); - chand->max_connection_idle = - DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(DEFAULT_MAX_CONNECTION_IDLE_MS, GPR_TIMESPAN); - for (size_t i = 0; i < args->channel_args->num_args; ++i) { - if (0 == strcmp(args->channel_args->args[i].key, - GRPC_ARG_MAX_CONNECTION_AGE_MS)) { - const int value = grpc_channel_arg_get_integer( - &args->channel_args->args[i], MAX_CONNECTION_AGE_INTEGER_OPTIONS); - chand->max_connection_age = - value == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis( - add_random_max_connection_age_jitter(value), GPR_TIMESPAN); - } else if (0 == strcmp(args->channel_args->args[i].key, - GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) { - const int value = grpc_channel_arg_get_integer( - &args->channel_args->args[i], - (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, - INT_MAX}); - chand->max_connection_age_grace = - value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(value, GPR_TIMESPAN); - } else if (0 == strcmp(args->channel_args->args[i].key, - GRPC_ARG_MAX_CONNECTION_IDLE_MS)) { - const int value = grpc_channel_arg_get_integer( - &args->channel_args->args[i], MAX_CONNECTION_IDLE_INTEGER_OPTIONS); - chand->max_connection_idle = - value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(value, GPR_TIMESPAN); - } - } - GRPC_CLOSURE_INIT(&chand->close_max_idle_channel, close_max_idle_channel, - chand, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&chand->close_max_age_channel, close_max_age_channel, chand, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&chand->force_close_max_age_channel, - force_close_max_age_channel, chand, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&chand->start_max_idle_timer_after_init, - start_max_idle_timer_after_init, chand, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&chand->start_max_age_timer_after_init, - start_max_age_timer_after_init, chand, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&chand->start_max_age_grace_timer_after_goaway_op, - start_max_age_grace_timer_after_goaway_op, chand, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed, - channel_connectivity_changed, chand, - grpc_schedule_on_exec_ctx); - - if (gpr_time_cmp(chand->max_connection_age, gpr_inf_future(GPR_TIMESPAN)) != - 0) { - /* When the channel reaches its max age, we send down an op with - goaway_error set. However, we can't send down any ops until after the - channel stack is fully initialized. If we start the timer here, we have - no guarantee that the timer won't pop before channel stack initialization - is finished. To avoid that problem, we create a closure to start the - timer, and we schedule that closure to be run after call stack - initialization is done. */ - GRPC_CHANNEL_STACK_REF(chand->channel_stack, - "max_age start_max_age_timer_after_init"); - GRPC_CLOSURE_SCHED(exec_ctx, &chand->start_max_age_timer_after_init, - GRPC_ERROR_NONE); - } - - /* Initialize the number of calls as 1, so that the max_idle_timer will not - start until start_max_idle_timer_after_init is invoked. */ - gpr_atm_rel_store(&chand->call_count, 1); - if (gpr_time_cmp(chand->max_connection_idle, gpr_inf_future(GPR_TIMESPAN)) != - 0) { - GRPC_CHANNEL_STACK_REF(chand->channel_stack, - "max_age start_max_idle_timer_after_init"); - GRPC_CLOSURE_SCHED(exec_ctx, &chand->start_max_idle_timer_after_init, - GRPC_ERROR_NONE); - } - return GRPC_ERROR_NONE; -} - -/* Destructor for channel_data. */ -static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem) {} - -const grpc_channel_filter grpc_max_age_filter = { - grpc_call_next_op, - grpc_channel_next_op, - 0, /* sizeof_call_data */ - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "max_age"}; - -static bool maybe_add_max_age_filter(grpc_exec_ctx* exec_ctx, - grpc_channel_stack_builder* builder, - void* arg) { - const grpc_channel_args* channel_args = - grpc_channel_stack_builder_get_channel_arguments(builder); - bool enable = - grpc_channel_arg_get_integer( - grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_AGE_MS), - MAX_CONNECTION_AGE_INTEGER_OPTIONS) != INT_MAX || - grpc_channel_arg_get_integer( - grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_IDLE_MS), - MAX_CONNECTION_IDLE_INTEGER_OPTIONS) != INT_MAX; - if (enable) { - return grpc_channel_stack_builder_prepend_filter( - builder, &grpc_max_age_filter, NULL, NULL); - } else { - return true; - } -} - -void grpc_max_age_filter_init(void) { - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_max_age_filter, NULL); -} - -void grpc_max_age_filter_shutdown(void) {} diff --git a/src/core/ext/filters/max_age/max_age_filter.cc b/src/core/ext/filters/max_age/max_age_filter.cc new file mode 100644 index 0000000000..9639ec9fb0 --- /dev/null +++ b/src/core/ext/filters/max_age/max_age_filter.cc @@ -0,0 +1,423 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/filters/max_age/max_age_filter.h" + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/http2_errors.h" + +#define DEFAULT_MAX_CONNECTION_AGE_MS INT_MAX +#define DEFAULT_MAX_CONNECTION_AGE_GRACE_MS INT_MAX +#define DEFAULT_MAX_CONNECTION_IDLE_MS INT_MAX +#define MAX_CONNECTION_AGE_JITTER 0.1 + +#define MAX_CONNECTION_AGE_INTEGER_OPTIONS \ + (grpc_integer_options) { DEFAULT_MAX_CONNECTION_AGE_MS, 1, INT_MAX } +#define MAX_CONNECTION_IDLE_INTEGER_OPTIONS \ + (grpc_integer_options) { DEFAULT_MAX_CONNECTION_IDLE_MS, 1, INT_MAX } + +typedef struct channel_data { + /* We take a reference to the channel stack for the timer callback */ + grpc_channel_stack* channel_stack; + /* Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer + and max_age_grace_timer_pending */ + gpr_mu max_age_timer_mu; + /* True if the max_age timer callback is currently pending */ + bool max_age_timer_pending; + /* True if the max_age_grace timer callback is currently pending */ + bool max_age_grace_timer_pending; + /* The timer for checking if the channel has reached its max age */ + grpc_timer max_age_timer; + /* The timer for checking if the max-aged channel has uesed up the grace + period */ + grpc_timer max_age_grace_timer; + /* The timer for checking if the channel's idle duration reaches + max_connection_idle */ + grpc_timer max_idle_timer; + /* Allowed max time a channel may have no outstanding rpcs */ + gpr_timespec max_connection_idle; + /* Allowed max time a channel may exist */ + gpr_timespec max_connection_age; + /* Allowed grace period after the channel reaches its max age */ + gpr_timespec max_connection_age_grace; + /* Closure to run when the channel's idle duration reaches max_connection_idle + and should be closed gracefully */ + grpc_closure close_max_idle_channel; + /* Closure to run when the channel reaches its max age and should be closed + gracefully */ + grpc_closure close_max_age_channel; + /* Closure to run the channel uses up its max age grace time and should be + closed forcibly */ + grpc_closure force_close_max_age_channel; + /* Closure to run when the init fo channel stack is done and the max_idle + timer should be started */ + grpc_closure start_max_idle_timer_after_init; + /* Closure to run when the init fo channel stack is done and the max_age timer + should be started */ + grpc_closure start_max_age_timer_after_init; + /* Closure to run when the goaway op is finished and the max_age_timer */ + grpc_closure start_max_age_grace_timer_after_goaway_op; + /* Closure to run when the channel connectivity state changes */ + grpc_closure channel_connectivity_changed; + /* Records the current connectivity state */ + grpc_connectivity_state connectivity_state; + /* Number of active calls */ + gpr_atm call_count; +} channel_data; + +/* Increase the nubmer of active calls. Before the increasement, if there are no + calls, the max_idle_timer should be cancelled. */ +static void increase_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) { + if (gpr_atm_full_fetch_add(&chand->call_count, 1) == 0) { + grpc_timer_cancel(exec_ctx, &chand->max_idle_timer); + } +} + +/* Decrease the nubmer of active calls. After the decrement, if there are no + calls, the max_idle_timer should be started. */ +static void decrease_call_count(grpc_exec_ctx* exec_ctx, channel_data* chand) { + if (gpr_atm_full_fetch_add(&chand->call_count, -1) == 1) { + GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_idle_timer"); + grpc_timer_init( + exec_ctx, &chand->max_idle_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_idle), + &chand->close_max_idle_channel, gpr_now(GPR_CLOCK_MONOTONIC)); + } +} + +static void start_max_idle_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + /* Decrease call_count. If there are no active calls at this time, + max_idle_timer will start here. If the number of active calls is not 0, + max_idle_timer will start after all the active calls end. */ + decrease_call_count(exec_ctx, chand); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age start_max_idle_timer_after_init"); +} + +static void start_max_age_timer_after_init(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_timer_pending = true; + GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_timer"); + grpc_timer_init( + exec_ctx, &chand->max_age_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), chand->max_connection_age), + &chand->close_max_age_channel, gpr_now(GPR_CLOCK_MONOTONIC)); + gpr_mu_unlock(&chand->max_age_timer_mu); + grpc_transport_op* op = grpc_make_transport_op(NULL); + op->on_connectivity_state_change = &chand->channel_connectivity_changed, + op->connectivity_state = &chand->connectivity_state; + grpc_channel_next_op(exec_ctx, + grpc_channel_stack_element(chand->channel_stack, 0), op); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age start_max_age_timer_after_init"); +} + +static void start_max_age_grace_timer_after_goaway_op(grpc_exec_ctx* exec_ctx, + void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_grace_timer_pending = true; + GRPC_CHANNEL_STACK_REF(chand->channel_stack, "max_age max_age_grace_timer"); + grpc_timer_init(exec_ctx, &chand->max_age_grace_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), + chand->max_connection_age_grace), + &chand->force_close_max_age_channel, + gpr_now(GPR_CLOCK_MONOTONIC)); + gpr_mu_unlock(&chand->max_age_timer_mu); + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age start_max_age_grace_timer_after_goaway_op"); +} + +static void close_max_idle_channel(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + if (error == GRPC_ERROR_NONE) { + /* Prevent the max idle timer from being set again */ + gpr_atm_no_barrier_fetch_add(&chand->call_count, 1); + grpc_transport_op* op = grpc_make_transport_op(NULL); + op->goaway_error = + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_idle"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(exec_ctx, elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("close_max_idle_channel", error); + } + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age max_idle_timer"); +} + +static void close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_timer_pending = false; + gpr_mu_unlock(&chand->max_age_timer_mu); + if (error == GRPC_ERROR_NONE) { + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_age_grace_timer_after_goaway_op"); + grpc_transport_op* op = grpc_make_transport_op( + &chand->start_max_age_grace_timer_after_goaway_op); + op->goaway_error = + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("max_age"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_NO_ERROR); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(exec_ctx, elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("close_max_age_channel", error); + } + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age max_age_timer"); +} + +static void force_close_max_age_channel(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + gpr_mu_lock(&chand->max_age_timer_mu); + chand->max_age_grace_timer_pending = false; + gpr_mu_unlock(&chand->max_age_timer_mu); + if (error == GRPC_ERROR_NONE) { + grpc_transport_op* op = grpc_make_transport_op(NULL); + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel reaches max age"); + grpc_channel_element* elem = + grpc_channel_stack_element(chand->channel_stack, 0); + elem->filter->start_transport_op(exec_ctx, elem, op); + } else if (error != GRPC_ERROR_CANCELLED) { + GRPC_LOG_IF_ERROR("force_close_max_age_channel", error); + } + GRPC_CHANNEL_STACK_UNREF(exec_ctx, chand->channel_stack, + "max_age max_age_grace_timer"); +} + +static void channel_connectivity_changed(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + channel_data* chand = (channel_data*)arg; + if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { + grpc_transport_op* op = grpc_make_transport_op(NULL); + op->on_connectivity_state_change = &chand->channel_connectivity_changed, + op->connectivity_state = &chand->connectivity_state; + grpc_channel_next_op( + exec_ctx, grpc_channel_stack_element(chand->channel_stack, 0), op); + } else { + gpr_mu_lock(&chand->max_age_timer_mu); + if (chand->max_age_timer_pending) { + grpc_timer_cancel(exec_ctx, &chand->max_age_timer); + chand->max_age_timer_pending = false; + } + if (chand->max_age_grace_timer_pending) { + grpc_timer_cancel(exec_ctx, &chand->max_age_grace_timer); + chand->max_age_grace_timer_pending = false; + } + gpr_mu_unlock(&chand->max_age_timer_mu); + /* If there are no active calls, this increasement will cancel + max_idle_timer, and prevent max_idle_timer from being started in the + future. */ + increase_call_count(exec_ctx, chand); + } +} + +/* A random jitter of +/-10% will be added to MAX_CONNECTION_AGE to spread out + connection storms. Note that the MAX_CONNECTION_AGE option without jitter + would not create connection storms by itself, but if there happened to be a + connection storm it could cause it to repeat at a fixed period. */ +static int add_random_max_connection_age_jitter(int value) { + /* generate a random number between 1 - MAX_CONNECTION_AGE_JITTER and + 1 + MAX_CONNECTION_AGE_JITTER */ + double multiplier = rand() * MAX_CONNECTION_AGE_JITTER * 2.0 / RAND_MAX + + 1.0 - MAX_CONNECTION_AGE_JITTER; + double result = multiplier * value; + /* INT_MAX - 0.5 converts the value to float, so that result will not be + cast to int implicitly before the comparison. */ + return result > INT_MAX - 0.5 ? INT_MAX : (int)result; +} + +/* Constructor for call_data. */ +static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem, + const grpc_call_element_args* args) { + channel_data* chand = (channel_data*)elem->channel_data; + increase_call_count(exec_ctx, chand); + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data. */ +static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + channel_data* chand = (channel_data*)elem->channel_data; + decrease_call_count(exec_ctx, chand); +} + +/* Constructor for channel_data. */ +static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem, + grpc_channel_element_args* args) { + channel_data* chand = (channel_data*)elem->channel_data; + gpr_mu_init(&chand->max_age_timer_mu); + chand->max_age_timer_pending = false; + chand->max_age_grace_timer_pending = false; + chand->channel_stack = args->channel_stack; + chand->max_connection_age = + DEFAULT_MAX_CONNECTION_AGE_MS == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(add_random_max_connection_age_jitter( + DEFAULT_MAX_CONNECTION_AGE_MS), + GPR_TIMESPAN); + chand->max_connection_age_grace = + DEFAULT_MAX_CONNECTION_AGE_GRACE_MS == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, + GPR_TIMESPAN); + chand->max_connection_idle = + DEFAULT_MAX_CONNECTION_IDLE_MS == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(DEFAULT_MAX_CONNECTION_IDLE_MS, GPR_TIMESPAN); + for (size_t i = 0; i < args->channel_args->num_args; ++i) { + if (0 == strcmp(args->channel_args->args[i].key, + GRPC_ARG_MAX_CONNECTION_AGE_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], MAX_CONNECTION_AGE_INTEGER_OPTIONS); + chand->max_connection_age = + value == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis( + add_random_max_connection_age_jitter(value), GPR_TIMESPAN); + } else if (0 == strcmp(args->channel_args->args[i].key, + GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], + (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, + INT_MAX}); + chand->max_connection_age_grace = + value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(value, GPR_TIMESPAN); + } else if (0 == strcmp(args->channel_args->args[i].key, + GRPC_ARG_MAX_CONNECTION_IDLE_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->channel_args->args[i], MAX_CONNECTION_IDLE_INTEGER_OPTIONS); + chand->max_connection_idle = + value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(value, GPR_TIMESPAN); + } + } + GRPC_CLOSURE_INIT(&chand->close_max_idle_channel, close_max_idle_channel, + chand, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->close_max_age_channel, close_max_age_channel, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->force_close_max_age_channel, + force_close_max_age_channel, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->start_max_idle_timer_after_init, + start_max_idle_timer_after_init, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->start_max_age_timer_after_init, + start_max_age_timer_after_init, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->start_max_age_grace_timer_after_goaway_op, + start_max_age_grace_timer_after_goaway_op, chand, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand, + grpc_schedule_on_exec_ctx); + + if (gpr_time_cmp(chand->max_connection_age, gpr_inf_future(GPR_TIMESPAN)) != + 0) { + /* When the channel reaches its max age, we send down an op with + goaway_error set. However, we can't send down any ops until after the + channel stack is fully initialized. If we start the timer here, we have + no guarantee that the timer won't pop before channel stack initialization + is finished. To avoid that problem, we create a closure to start the + timer, and we schedule that closure to be run after call stack + initialization is done. */ + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_age_timer_after_init"); + GRPC_CLOSURE_SCHED(exec_ctx, &chand->start_max_age_timer_after_init, + GRPC_ERROR_NONE); + } + + /* Initialize the number of calls as 1, so that the max_idle_timer will not + start until start_max_idle_timer_after_init is invoked. */ + gpr_atm_rel_store(&chand->call_count, 1); + if (gpr_time_cmp(chand->max_connection_idle, gpr_inf_future(GPR_TIMESPAN)) != + 0) { + GRPC_CHANNEL_STACK_REF(chand->channel_stack, + "max_age start_max_idle_timer_after_init"); + GRPC_CLOSURE_SCHED(exec_ctx, &chand->start_max_idle_timer_after_init, + GRPC_ERROR_NONE); + } + return GRPC_ERROR_NONE; +} + +/* Destructor for channel_data. */ +static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem) {} + +const grpc_channel_filter grpc_max_age_filter = { + grpc_call_next_op, + grpc_channel_next_op, + 0, /* sizeof_call_data */ + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "max_age"}; + +static bool maybe_add_max_age_filter(grpc_exec_ctx* exec_ctx, + grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + bool enable = + grpc_channel_arg_get_integer( + grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_AGE_MS), + MAX_CONNECTION_AGE_INTEGER_OPTIONS) != INT_MAX || + grpc_channel_arg_get_integer( + grpc_channel_args_find(channel_args, GRPC_ARG_MAX_CONNECTION_IDLE_MS), + MAX_CONNECTION_IDLE_INTEGER_OPTIONS) != INT_MAX; + if (enable) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_max_age_filter, NULL, NULL); + } else { + return true; + } +} + +extern "C" void grpc_max_age_filter_init(void) { + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_max_age_filter, NULL); +} + +extern "C" void grpc_max_age_filter_shutdown(void) {} diff --git a/src/core/ext/filters/message_size/message_size_filter.c b/src/core/ext/filters/message_size/message_size_filter.c deleted file mode 100644 index 47763b1deb..0000000000 --- a/src/core/ext/filters/message_size/message_size_filter.c +++ /dev/null @@ -1,303 +0,0 @@ -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/ext/filters/message_size/message_size_filter.h" - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/service_config.h" - -typedef struct message_size_limits { - int max_send_size; - int max_recv_size; -} message_size_limits; - -static void message_size_limits_free(grpc_exec_ctx* exec_ctx, void* value) { - gpr_free(value); -} - -static void* message_size_limits_create_from_json(const grpc_json* json) { - int max_request_message_bytes = -1; - int max_response_message_bytes = -1; - for (grpc_json* field = json->child; field != NULL; field = field->next) { - if (field->key == NULL) continue; - if (strcmp(field->key, "maxRequestMessageBytes") == 0) { - if (max_request_message_bytes >= 0) return NULL; // Duplicate. - if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) { - return NULL; - } - max_request_message_bytes = gpr_parse_nonnegative_int(field->value); - if (max_request_message_bytes == -1) return NULL; - } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) { - if (max_response_message_bytes >= 0) return NULL; // Duplicate. - if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) { - return NULL; - } - max_response_message_bytes = gpr_parse_nonnegative_int(field->value); - if (max_response_message_bytes == -1) return NULL; - } - } - message_size_limits* value = - (message_size_limits*)gpr_malloc(sizeof(message_size_limits)); - value->max_send_size = max_request_message_bytes; - value->max_recv_size = max_response_message_bytes; - return value; -} - -typedef struct call_data { - grpc_call_combiner* call_combiner; - message_size_limits limits; - // Receive closures are chained: we inject this closure as the - // recv_message_ready up-call on transport_stream_op, and remember to - // call our next_recv_message_ready member after handling it. - grpc_closure recv_message_ready; - // Used by recv_message_ready. - grpc_byte_stream** recv_message; - // Original recv_message_ready callback, invoked after our own. - grpc_closure* next_recv_message_ready; -} call_data; - -typedef struct channel_data { - message_size_limits limits; - // Maps path names to message_size_limits structs. - grpc_slice_hash_table* method_limit_table; -} channel_data; - -// Callback invoked when we receive a message. Here we check the max -// receive message size. -static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data, - grpc_error* error) { - grpc_call_element* elem = (grpc_call_element*)user_data; - call_data* calld = (call_data*)elem->call_data; - if (*calld->recv_message != NULL && calld->limits.max_recv_size >= 0 && - (*calld->recv_message)->length > (size_t)calld->limits.max_recv_size) { - char* message_string; - gpr_asprintf(&message_string, - "Received message larger than max (%u vs. %d)", - (*calld->recv_message)->length, calld->limits.max_recv_size); - grpc_error* new_error = grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED); - if (error == GRPC_ERROR_NONE) { - error = new_error; - } else { - error = grpc_error_add_child(error, new_error); - GRPC_ERROR_UNREF(new_error); - } - gpr_free(message_string); - } else { - GRPC_ERROR_REF(error); - } - // Invoke the next callback. - GRPC_CLOSURE_RUN(exec_ctx, calld->next_recv_message_ready, error); -} - -// Start transport stream op. -static void start_transport_stream_op_batch( - grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - grpc_transport_stream_op_batch* op) { - call_data* calld = (call_data*)elem->call_data; - // Check max send message size. - if (op->send_message && calld->limits.max_send_size >= 0 && - op->payload->send_message.send_message->length > - (size_t)calld->limits.max_send_size) { - char* message_string; - gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)", - op->payload->send_message.send_message->length, - calld->limits.max_send_size); - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, op, - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_RESOURCE_EXHAUSTED), - calld->call_combiner); - gpr_free(message_string); - return; - } - // Inject callback for receiving a message. - if (op->recv_message) { - calld->next_recv_message_ready = - op->payload->recv_message.recv_message_ready; - calld->recv_message = op->payload->recv_message.recv_message; - op->payload->recv_message.recv_message_ready = &calld->recv_message_ready; - } - // Chain to the next filter. - grpc_call_next_op(exec_ctx, elem, op); -} - -// Constructor for call_data. -static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, - grpc_call_element* elem, - const grpc_call_element_args* args) { - channel_data* chand = (channel_data*)elem->channel_data; - call_data* calld = (call_data*)elem->call_data; - calld->call_combiner = args->call_combiner; - calld->next_recv_message_ready = NULL; - GRPC_CLOSURE_INIT(&calld->recv_message_ready, recv_message_ready, elem, - grpc_schedule_on_exec_ctx); - // Get max sizes from channel data, then merge in per-method config values. - // Note: Per-method config is only available on the client, so we - // apply the max request size to the send limit and the max response - // size to the receive limit. - calld->limits = chand->limits; - if (chand->method_limit_table != NULL) { - message_size_limits* limits = - (message_size_limits*)grpc_method_config_table_get( - exec_ctx, chand->method_limit_table, args->path); - if (limits != NULL) { - if (limits->max_send_size >= 0 && - (limits->max_send_size < calld->limits.max_send_size || - calld->limits.max_send_size < 0)) { - calld->limits.max_send_size = limits->max_send_size; - } - if (limits->max_recv_size >= 0 && - (limits->max_recv_size < calld->limits.max_recv_size || - calld->limits.max_recv_size < 0)) { - calld->limits.max_recv_size = limits->max_recv_size; - } - } - } - return GRPC_ERROR_NONE; -} - -// Destructor for call_data. -static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - const grpc_call_final_info* final_info, - grpc_closure* ignored) {} - -static int default_size(const grpc_channel_args* args, - int without_minimal_stack) { - if (grpc_channel_args_want_minimal_stack(args)) { - return -1; - } - return without_minimal_stack; -} - -message_size_limits get_message_size_limits( - const grpc_channel_args* channel_args) { - message_size_limits lim; - lim.max_send_size = - default_size(channel_args, GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH); - lim.max_recv_size = - default_size(channel_args, GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH); - for (size_t i = 0; i < channel_args->num_args; ++i) { - if (strcmp(channel_args->args[i].key, GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == - 0) { - const grpc_integer_options options = {lim.max_send_size, -1, INT_MAX}; - lim.max_send_size = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - } - if (strcmp(channel_args->args[i].key, - GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) { - const grpc_integer_options options = {lim.max_recv_size, -1, INT_MAX}; - lim.max_recv_size = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - } - } - return lim; -} - -// Constructor for channel_data. -static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem, - grpc_channel_element_args* args) { - GPR_ASSERT(!args->is_last); - channel_data* chand = (channel_data*)elem->channel_data; - chand->limits = get_message_size_limits(args->channel_args); - // Get method config table from channel args. - const grpc_arg* channel_arg = - grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); - if (channel_arg != NULL) { - GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); - grpc_service_config* service_config = - grpc_service_config_create(channel_arg->value.string); - if (service_config != NULL) { - chand->method_limit_table = - grpc_service_config_create_method_config_table( - exec_ctx, service_config, message_size_limits_create_from_json, - message_size_limits_free); - grpc_service_config_destroy(service_config); - } - } - return GRPC_ERROR_NONE; -} - -// Destructor for channel_data. -static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem) { - channel_data* chand = (channel_data*)elem->channel_data; - grpc_slice_hash_table_unref(exec_ctx, chand->method_limit_table); -} - -const grpc_channel_filter grpc_message_size_filter = { - start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "message_size"}; - -static bool maybe_add_message_size_filter(grpc_exec_ctx* exec_ctx, - grpc_channel_stack_builder* builder, - void* arg) { - const grpc_channel_args* channel_args = - grpc_channel_stack_builder_get_channel_arguments(builder); - bool enable = false; - message_size_limits lim = get_message_size_limits(channel_args); - if (lim.max_send_size != -1 || lim.max_recv_size != -1) { - enable = true; - } - const grpc_arg* a = - grpc_channel_args_find(channel_args, GRPC_ARG_SERVICE_CONFIG); - if (a != NULL) { - enable = true; - } - if (enable) { - return grpc_channel_stack_builder_prepend_filter( - builder, &grpc_message_size_filter, NULL, NULL); - } else { - return true; - } -} - -void grpc_message_size_filter_init(void) { - grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_message_size_filter, NULL); - grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_message_size_filter, NULL); - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - maybe_add_message_size_filter, NULL); -} - -void grpc_message_size_filter_shutdown(void) {} diff --git a/src/core/ext/filters/message_size/message_size_filter.cc b/src/core/ext/filters/message_size/message_size_filter.cc new file mode 100644 index 0000000000..5dc131b9f6 --- /dev/null +++ b/src/core/ext/filters/message_size/message_size_filter.cc @@ -0,0 +1,303 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/ext/filters/message_size/message_size_filter.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/service_config.h" + +typedef struct message_size_limits { + int max_send_size; + int max_recv_size; +} message_size_limits; + +static void message_size_limits_free(grpc_exec_ctx* exec_ctx, void* value) { + gpr_free(value); +} + +static void* message_size_limits_create_from_json(const grpc_json* json) { + int max_request_message_bytes = -1; + int max_response_message_bytes = -1; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) continue; + if (strcmp(field->key, "maxRequestMessageBytes") == 0) { + if (max_request_message_bytes >= 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) { + return NULL; + } + max_request_message_bytes = gpr_parse_nonnegative_int(field->value); + if (max_request_message_bytes == -1) return NULL; + } else if (strcmp(field->key, "maxResponseMessageBytes") == 0) { + if (max_response_message_bytes >= 0) return NULL; // Duplicate. + if (field->type != GRPC_JSON_STRING && field->type != GRPC_JSON_NUMBER) { + return NULL; + } + max_response_message_bytes = gpr_parse_nonnegative_int(field->value); + if (max_response_message_bytes == -1) return NULL; + } + } + message_size_limits* value = + (message_size_limits*)gpr_malloc(sizeof(message_size_limits)); + value->max_send_size = max_request_message_bytes; + value->max_recv_size = max_response_message_bytes; + return value; +} + +typedef struct call_data { + grpc_call_combiner* call_combiner; + message_size_limits limits; + // Receive closures are chained: we inject this closure as the + // recv_message_ready up-call on transport_stream_op, and remember to + // call our next_recv_message_ready member after handling it. + grpc_closure recv_message_ready; + // Used by recv_message_ready. + grpc_byte_stream** recv_message; + // Original recv_message_ready callback, invoked after our own. + grpc_closure* next_recv_message_ready; +} call_data; + +typedef struct channel_data { + message_size_limits limits; + // Maps path names to message_size_limits structs. + grpc_slice_hash_table* method_limit_table; +} channel_data; + +// Callback invoked when we receive a message. Here we check the max +// receive message size. +static void recv_message_ready(grpc_exec_ctx* exec_ctx, void* user_data, + grpc_error* error) { + grpc_call_element* elem = (grpc_call_element*)user_data; + call_data* calld = (call_data*)elem->call_data; + if (*calld->recv_message != NULL && calld->limits.max_recv_size >= 0 && + (*calld->recv_message)->length > (size_t)calld->limits.max_recv_size) { + char* message_string; + gpr_asprintf(&message_string, + "Received message larger than max (%u vs. %d)", + (*calld->recv_message)->length, calld->limits.max_recv_size); + grpc_error* new_error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED); + if (error == GRPC_ERROR_NONE) { + error = new_error; + } else { + error = grpc_error_add_child(error, new_error); + GRPC_ERROR_UNREF(new_error); + } + gpr_free(message_string); + } else { + GRPC_ERROR_REF(error); + } + // Invoke the next callback. + GRPC_CLOSURE_RUN(exec_ctx, calld->next_recv_message_ready, error); +} + +// Start transport stream op. +static void start_transport_stream_op_batch( + grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + call_data* calld = (call_data*)elem->call_data; + // Check max send message size. + if (op->send_message && calld->limits.max_send_size >= 0 && + op->payload->send_message.send_message->length > + (size_t)calld->limits.max_send_size) { + char* message_string; + gpr_asprintf(&message_string, "Sent message larger than max (%u vs. %d)", + op->payload->send_message.send_message->length, + calld->limits.max_send_size); + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, op, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(message_string), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_RESOURCE_EXHAUSTED), + calld->call_combiner); + gpr_free(message_string); + return; + } + // Inject callback for receiving a message. + if (op->recv_message) { + calld->next_recv_message_ready = + op->payload->recv_message.recv_message_ready; + calld->recv_message = op->payload->recv_message.recv_message; + op->payload->recv_message.recv_message_ready = &calld->recv_message_ready; + } + // Chain to the next filter. + grpc_call_next_op(exec_ctx, elem, op); +} + +// Constructor for call_data. +static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem, + const grpc_call_element_args* args) { + channel_data* chand = (channel_data*)elem->channel_data; + call_data* calld = (call_data*)elem->call_data; + calld->call_combiner = args->call_combiner; + calld->next_recv_message_ready = NULL; + GRPC_CLOSURE_INIT(&calld->recv_message_ready, recv_message_ready, elem, + grpc_schedule_on_exec_ctx); + // Get max sizes from channel data, then merge in per-method config values. + // Note: Per-method config is only available on the client, so we + // apply the max request size to the send limit and the max response + // size to the receive limit. + calld->limits = chand->limits; + if (chand->method_limit_table != NULL) { + message_size_limits* limits = + (message_size_limits*)grpc_method_config_table_get( + exec_ctx, chand->method_limit_table, args->path); + if (limits != NULL) { + if (limits->max_send_size >= 0 && + (limits->max_send_size < calld->limits.max_send_size || + calld->limits.max_send_size < 0)) { + calld->limits.max_send_size = limits->max_send_size; + } + if (limits->max_recv_size >= 0 && + (limits->max_recv_size < calld->limits.max_recv_size || + calld->limits.max_recv_size < 0)) { + calld->limits.max_recv_size = limits->max_recv_size; + } + } + } + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. +static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) {} + +static int default_size(const grpc_channel_args* args, + int without_minimal_stack) { + if (grpc_channel_args_want_minimal_stack(args)) { + return -1; + } + return without_minimal_stack; +} + +message_size_limits get_message_size_limits( + const grpc_channel_args* channel_args) { + message_size_limits lim; + lim.max_send_size = + default_size(channel_args, GRPC_DEFAULT_MAX_SEND_MESSAGE_LENGTH); + lim.max_recv_size = + default_size(channel_args, GRPC_DEFAULT_MAX_RECV_MESSAGE_LENGTH); + for (size_t i = 0; i < channel_args->num_args; ++i) { + if (strcmp(channel_args->args[i].key, GRPC_ARG_MAX_SEND_MESSAGE_LENGTH) == + 0) { + const grpc_integer_options options = {lim.max_send_size, -1, INT_MAX}; + lim.max_send_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } + if (strcmp(channel_args->args[i].key, + GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH) == 0) { + const grpc_integer_options options = {lim.max_recv_size, -1, INT_MAX}; + lim.max_recv_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } + } + return lim; +} + +// Constructor for channel_data. +static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem, + grpc_channel_element_args* args) { + GPR_ASSERT(!args->is_last); + channel_data* chand = (channel_data*)elem->channel_data; + chand->limits = get_message_size_limits(args->channel_args); + // Get method config table from channel args. + const grpc_arg* channel_arg = + grpc_channel_args_find(args->channel_args, GRPC_ARG_SERVICE_CONFIG); + if (channel_arg != NULL) { + GPR_ASSERT(channel_arg->type == GRPC_ARG_STRING); + grpc_service_config* service_config = + grpc_service_config_create(channel_arg->value.string); + if (service_config != NULL) { + chand->method_limit_table = + grpc_service_config_create_method_config_table( + exec_ctx, service_config, message_size_limits_create_from_json, + message_size_limits_free); + grpc_service_config_destroy(service_config); + } + } + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. +static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem) { + channel_data* chand = (channel_data*)elem->channel_data; + grpc_slice_hash_table_unref(exec_ctx, chand->method_limit_table); +} + +const grpc_channel_filter grpc_message_size_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "message_size"}; + +static bool maybe_add_message_size_filter(grpc_exec_ctx* exec_ctx, + grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + bool enable = false; + message_size_limits lim = get_message_size_limits(channel_args); + if (lim.max_send_size != -1 || lim.max_recv_size != -1) { + enable = true; + } + const grpc_arg* a = + grpc_channel_args_find(channel_args, GRPC_ARG_SERVICE_CONFIG); + if (a != NULL) { + enable = true; + } + if (enable) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_message_size_filter, NULL, NULL); + } else { + return true; + } +} + +extern "C" void grpc_message_size_filter_init(void) { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_message_size_filter, NULL); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_message_size_filter, NULL); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + maybe_add_message_size_filter, NULL); +} + +extern "C" void grpc_message_size_filter_shutdown(void) {} diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c deleted file mode 100644 index c8b2fe5f99..0000000000 --- a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c +++ /dev/null @@ -1,207 +0,0 @@ -// -// Copyright 2017 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" - -#include - -#include - -#include "src/core/ext/filters/workarounds/workaround_utils.h" -#include "src/core/lib/channel/channel_stack_builder.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/metadata.h" - -typedef struct call_data { - // Receive closures are chained: we inject this closure as the - // recv_initial_metadata_ready up-call on transport_stream_op, and remember to - // call our next_recv_initial_metadata_ready member after handling it. - grpc_closure recv_initial_metadata_ready; - // Used by recv_initial_metadata_ready. - grpc_metadata_batch* recv_initial_metadata; - // Original recv_initial_metadata_ready callback, invoked after our own. - grpc_closure* next_recv_initial_metadata_ready; - - // Marks whether the workaround is active - bool workaround_active; -} call_data; - -// Find the user agent metadata element in the batch -static bool get_user_agent_mdelem(const grpc_metadata_batch* batch, - grpc_mdelem* md) { - if (batch->idx.named.user_agent != NULL) { - *md = batch->idx.named.user_agent->md; - return true; - } - return false; -} - -// Callback invoked when we receive an initial metadata. -static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, - void* user_data, grpc_error* error) { - grpc_call_element* elem = (grpc_call_element*)user_data; - call_data* calld = (call_data*)elem->call_data; - - if (GRPC_ERROR_NONE == error) { - grpc_mdelem md; - if (get_user_agent_mdelem(calld->recv_initial_metadata, &md)) { - grpc_workaround_user_agent_md* user_agent_md = grpc_parse_user_agent(md); - if (user_agent_md - ->workaround_active[GRPC_WORKAROUND_ID_CRONET_COMPRESSION]) { - calld->workaround_active = true; - } - } - } - - // Invoke the next callback. - GRPC_CLOSURE_RUN(exec_ctx, calld->next_recv_initial_metadata_ready, - GRPC_ERROR_REF(error)); -} - -// Start transport stream op. -static void start_transport_stream_op_batch( - grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - grpc_transport_stream_op_batch* op) { - call_data* calld = (call_data*)elem->call_data; - - // Inject callback for receiving initial metadata - if (op->recv_initial_metadata) { - calld->next_recv_initial_metadata_ready = - op->payload->recv_initial_metadata.recv_initial_metadata_ready; - op->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->recv_initial_metadata_ready; - calld->recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata; - } - - if (op->send_message) { - /* Send message happens after client's user-agent (initial metadata) is - * received, so workaround_active must be set already */ - if (calld->workaround_active) { - op->payload->send_message.send_message->flags |= GRPC_WRITE_NO_COMPRESS; - } - } - - // Chain to the next filter. - grpc_call_next_op(exec_ctx, elem, op); -} - -// Constructor for call_data. -static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, - grpc_call_element* elem, - const grpc_call_element_args* args) { - call_data* calld = (call_data*)elem->call_data; - calld->next_recv_initial_metadata_ready = NULL; - calld->workaround_active = false; - GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, - recv_initial_metadata_ready, elem, - grpc_schedule_on_exec_ctx); - return GRPC_ERROR_NONE; -} - -// Destructor for call_data. -static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, - const grpc_call_final_info* final_info, - grpc_closure* ignored) {} - -// Constructor for channel_data. -static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem, - grpc_channel_element_args* args) { - return GRPC_ERROR_NONE; -} - -// Destructor for channel_data. -static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, - grpc_channel_element* elem) {} - -// Parse the user agent -static bool parse_user_agent(grpc_mdelem md) { - const char grpc_objc_specifier[] = "grpc-objc/"; - const size_t grpc_objc_specifier_len = sizeof(grpc_objc_specifier) - 1; - const char cronet_specifier[] = "cronet_http"; - const size_t cronet_specifier_len = sizeof(cronet_specifier) - 1; - - char* user_agent_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - bool grpc_objc_specifier_seen = false; - bool cronet_specifier_seen = false; - char *major_version_str = user_agent_str, *minor_version_str; - long major_version, minor_version; - - char* head = strtok(user_agent_str, " "); - while (head != NULL) { - if (!grpc_objc_specifier_seen && - 0 == strncmp(head, grpc_objc_specifier, grpc_objc_specifier_len)) { - major_version_str = head + grpc_objc_specifier_len; - grpc_objc_specifier_seen = true; - } else if (grpc_objc_specifier_seen && - 0 == strncmp(head, cronet_specifier, cronet_specifier_len)) { - cronet_specifier_seen = true; - break; - } - - head = strtok(NULL, " "); - } - if (grpc_objc_specifier_seen) { - major_version_str = strtok(major_version_str, "."); - minor_version_str = strtok(NULL, "."); - major_version = atol(major_version_str); - minor_version = atol(minor_version_str); - } - - gpr_free(user_agent_str); - return (grpc_objc_specifier_seen && cronet_specifier_seen && - (major_version < 1 || (major_version == 1 && minor_version <= 3))); -} - -const grpc_channel_filter grpc_workaround_cronet_compression_filter = { - start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - 0, - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "workaround_cronet_compression"}; - -static bool register_workaround_cronet_compression( - grpc_exec_ctx* exec_ctx, grpc_channel_stack_builder* builder, void* arg) { - const grpc_channel_args* channel_args = - grpc_channel_stack_builder_get_channel_arguments(builder); - const grpc_arg* a = grpc_channel_args_find( - channel_args, GRPC_ARG_WORKAROUND_CRONET_COMPRESSION); - if (a == NULL) { - return true; - } - if (grpc_channel_arg_get_bool(a, false) == false) { - return true; - } - return grpc_channel_stack_builder_prepend_filter( - builder, &grpc_workaround_cronet_compression_filter, NULL, NULL); -} - -void grpc_workaround_cronet_compression_filter_init(void) { - grpc_channel_init_register_stage( - GRPC_SERVER_CHANNEL, GRPC_WORKAROUND_PRIORITY_HIGH, - register_workaround_cronet_compression, NULL); - grpc_register_workaround(GRPC_WORKAROUND_ID_CRONET_COMPRESSION, - parse_user_agent); -} - -void grpc_workaround_cronet_compression_filter_shutdown(void) {} diff --git a/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc new file mode 100644 index 0000000000..f77ed02421 --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc @@ -0,0 +1,207 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" + +#include + +#include + +#include "src/core/ext/filters/workarounds/workaround_utils.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/metadata.h" + +typedef struct call_data { + // Receive closures are chained: we inject this closure as the + // recv_initial_metadata_ready up-call on transport_stream_op, and remember to + // call our next_recv_initial_metadata_ready member after handling it. + grpc_closure recv_initial_metadata_ready; + // Used by recv_initial_metadata_ready. + grpc_metadata_batch* recv_initial_metadata; + // Original recv_initial_metadata_ready callback, invoked after our own. + grpc_closure* next_recv_initial_metadata_ready; + + // Marks whether the workaround is active + bool workaround_active; +} call_data; + +// Find the user agent metadata element in the batch +static bool get_user_agent_mdelem(const grpc_metadata_batch* batch, + grpc_mdelem* md) { + if (batch->idx.named.user_agent != NULL) { + *md = batch->idx.named.user_agent->md; + return true; + } + return false; +} + +// Callback invoked when we receive an initial metadata. +static void recv_initial_metadata_ready(grpc_exec_ctx* exec_ctx, + void* user_data, grpc_error* error) { + grpc_call_element* elem = (grpc_call_element*)user_data; + call_data* calld = (call_data*)elem->call_data; + + if (GRPC_ERROR_NONE == error) { + grpc_mdelem md; + if (get_user_agent_mdelem(calld->recv_initial_metadata, &md)) { + grpc_workaround_user_agent_md* user_agent_md = grpc_parse_user_agent(md); + if (user_agent_md + ->workaround_active[GRPC_WORKAROUND_ID_CRONET_COMPRESSION]) { + calld->workaround_active = true; + } + } + } + + // Invoke the next callback. + GRPC_CLOSURE_RUN(exec_ctx, calld->next_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +// Start transport stream op. +static void start_transport_stream_op_batch( + grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + grpc_transport_stream_op_batch* op) { + call_data* calld = (call_data*)elem->call_data; + + // Inject callback for receiving initial metadata + if (op->recv_initial_metadata) { + calld->next_recv_initial_metadata_ready = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + } + + if (op->send_message) { + /* Send message happens after client's user-agent (initial metadata) is + * received, so workaround_active must be set already */ + if (calld->workaround_active) { + op->payload->send_message.send_message->flags |= GRPC_WRITE_NO_COMPRESS; + } + } + + // Chain to the next filter. + grpc_call_next_op(exec_ctx, elem, op); +} + +// Constructor for call_data. +static grpc_error* init_call_elem(grpc_exec_ctx* exec_ctx, + grpc_call_element* elem, + const grpc_call_element_args* args) { + call_data* calld = (call_data*)elem->call_data; + calld->next_recv_initial_metadata_ready = NULL; + calld->workaround_active = false; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +// Destructor for call_data. +static void destroy_call_elem(grpc_exec_ctx* exec_ctx, grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) {} + +// Constructor for channel_data. +static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem, + grpc_channel_element_args* args) { + return GRPC_ERROR_NONE; +} + +// Destructor for channel_data. +static void destroy_channel_elem(grpc_exec_ctx* exec_ctx, + grpc_channel_element* elem) {} + +// Parse the user agent +static bool parse_user_agent(grpc_mdelem md) { + const char grpc_objc_specifier[] = "grpc-objc/"; + const size_t grpc_objc_specifier_len = sizeof(grpc_objc_specifier) - 1; + const char cronet_specifier[] = "cronet_http"; + const size_t cronet_specifier_len = sizeof(cronet_specifier) - 1; + + char* user_agent_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + bool grpc_objc_specifier_seen = false; + bool cronet_specifier_seen = false; + char *major_version_str = user_agent_str, *minor_version_str; + long major_version, minor_version; + + char* head = strtok(user_agent_str, " "); + while (head != NULL) { + if (!grpc_objc_specifier_seen && + 0 == strncmp(head, grpc_objc_specifier, grpc_objc_specifier_len)) { + major_version_str = head + grpc_objc_specifier_len; + grpc_objc_specifier_seen = true; + } else if (grpc_objc_specifier_seen && + 0 == strncmp(head, cronet_specifier, cronet_specifier_len)) { + cronet_specifier_seen = true; + break; + } + + head = strtok(NULL, " "); + } + if (grpc_objc_specifier_seen) { + major_version_str = strtok(major_version_str, "."); + minor_version_str = strtok(NULL, "."); + major_version = atol(major_version_str); + minor_version = atol(minor_version_str); + } + + gpr_free(user_agent_str); + return (grpc_objc_specifier_seen && cronet_specifier_seen && + (major_version < 1 || (major_version == 1 && minor_version <= 3))); +} + +const grpc_channel_filter grpc_workaround_cronet_compression_filter = { + start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + 0, + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "workaround_cronet_compression"}; + +static bool register_workaround_cronet_compression( + grpc_exec_ctx* exec_ctx, grpc_channel_stack_builder* builder, void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + const grpc_arg* a = grpc_channel_args_find( + channel_args, GRPC_ARG_WORKAROUND_CRONET_COMPRESSION); + if (a == NULL) { + return true; + } + if (grpc_channel_arg_get_bool(a, false) == false) { + return true; + } + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_workaround_cronet_compression_filter, NULL, NULL); +} + +extern "C" void grpc_workaround_cronet_compression_filter_init(void) { + grpc_channel_init_register_stage( + GRPC_SERVER_CHANNEL, GRPC_WORKAROUND_PRIORITY_HIGH, + register_workaround_cronet_compression, NULL); + grpc_register_workaround(GRPC_WORKAROUND_ID_CRONET_COMPRESSION, + parse_user_agent); +} + +extern "C" void grpc_workaround_cronet_compression_filter_shutdown(void) {} diff --git a/src/core/ext/filters/workarounds/workaround_utils.c b/src/core/ext/filters/workarounds/workaround_utils.c deleted file mode 100644 index e600fbee67..0000000000 --- a/src/core/ext/filters/workarounds/workaround_utils.c +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright 2017 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/ext/filters/workarounds/workaround_utils.h" - -#include -#include - -user_agent_parser ua_parser[GRPC_MAX_WORKAROUND_ID]; - -static void destroy_user_agent_md(void *user_agent_md) { - gpr_free(user_agent_md); -} - -grpc_workaround_user_agent_md *grpc_parse_user_agent(grpc_mdelem md) { - grpc_workaround_user_agent_md *user_agent_md = - (grpc_workaround_user_agent_md *)grpc_mdelem_get_user_data( - md, destroy_user_agent_md); - - if (NULL != user_agent_md) { - return user_agent_md; - } - user_agent_md = (grpc_workaround_user_agent_md *)gpr_malloc( - sizeof(grpc_workaround_user_agent_md)); - for (int i = 0; i < GRPC_MAX_WORKAROUND_ID; i++) { - if (ua_parser[i]) { - user_agent_md->workaround_active[i] = ua_parser[i](md); - } - } - grpc_mdelem_set_user_data(md, destroy_user_agent_md, (void *)user_agent_md); - - return user_agent_md; -} - -void grpc_register_workaround(uint32_t id, user_agent_parser parser) { - GPR_ASSERT(id < GRPC_MAX_WORKAROUND_ID); - ua_parser[id] = parser; -} diff --git a/src/core/ext/filters/workarounds/workaround_utils.cc b/src/core/ext/filters/workarounds/workaround_utils.cc new file mode 100644 index 0000000000..e600fbee67 --- /dev/null +++ b/src/core/ext/filters/workarounds/workaround_utils.cc @@ -0,0 +1,51 @@ +// +// Copyright 2017 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/ext/filters/workarounds/workaround_utils.h" + +#include +#include + +user_agent_parser ua_parser[GRPC_MAX_WORKAROUND_ID]; + +static void destroy_user_agent_md(void *user_agent_md) { + gpr_free(user_agent_md); +} + +grpc_workaround_user_agent_md *grpc_parse_user_agent(grpc_mdelem md) { + grpc_workaround_user_agent_md *user_agent_md = + (grpc_workaround_user_agent_md *)grpc_mdelem_get_user_data( + md, destroy_user_agent_md); + + if (NULL != user_agent_md) { + return user_agent_md; + } + user_agent_md = (grpc_workaround_user_agent_md *)gpr_malloc( + sizeof(grpc_workaround_user_agent_md)); + for (int i = 0; i < GRPC_MAX_WORKAROUND_ID; i++) { + if (ua_parser[i]) { + user_agent_md->workaround_active[i] = ua_parser[i](md); + } + } + grpc_mdelem_set_user_data(md, destroy_user_agent_md, (void *)user_agent_md); + + return user_agent_md; +} + +void grpc_register_workaround(uint32_t id, user_agent_parser parser) { + GPR_ASSERT(id < GRPC_MAX_WORKAROUND_ID); + ua_parser[id] = parser; +} diff --git a/src/core/ext/transport/chttp2/alpn/alpn.c b/src/core/ext/transport/chttp2/alpn/alpn.c deleted file mode 100644 index ca2e801ec8..0000000000 --- a/src/core/ext/transport/chttp2/alpn/alpn.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/alpn/alpn.h" -#include -#include - -/* in order of preference */ -static const char *const supported_versions[] = {"grpc-exp", "h2"}; - -int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) { - size_t i; - for (i = 0; i < GPR_ARRAY_SIZE(supported_versions); i++) { - if (!strncmp(version, supported_versions[i], size)) return 1; - } - return 0; -} - -size_t grpc_chttp2_num_alpn_versions(void) { - return GPR_ARRAY_SIZE(supported_versions); -} - -const char *grpc_chttp2_get_alpn_version_index(size_t i) { - GPR_ASSERT(i < GPR_ARRAY_SIZE(supported_versions)); - return supported_versions[i]; -} diff --git a/src/core/ext/transport/chttp2/alpn/alpn.cc b/src/core/ext/transport/chttp2/alpn/alpn.cc new file mode 100644 index 0000000000..ca2e801ec8 --- /dev/null +++ b/src/core/ext/transport/chttp2/alpn/alpn.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include +#include + +/* in order of preference */ +static const char *const supported_versions[] = {"grpc-exp", "h2"}; + +int grpc_chttp2_is_alpn_version_supported(const char *version, size_t size) { + size_t i; + for (i = 0; i < GPR_ARRAY_SIZE(supported_versions); i++) { + if (!strncmp(version, supported_versions[i], size)) return 1; + } + return 0; +} + +size_t grpc_chttp2_num_alpn_versions(void) { + return GPR_ARRAY_SIZE(supported_versions); +} + +const char *grpc_chttp2_get_alpn_version_index(size_t i) { + GPR_ASSERT(i < GPR_ARRAY_SIZE(supported_versions)); + return supported_versions[i]; +} diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.c b/src/core/ext/transport/chttp2/client/chttp2_connector.c deleted file mode 100644 index 202bcd47f5..0000000000 --- a/src/core/ext/transport/chttp2/client/chttp2_connector.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" - -#include - -#include - -#include -#include -#include - -#include "src/core/ext/filters/client_channel/connector.h" -#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" -#include "src/core/ext/filters/client_channel/subchannel.h" -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker.h" -#include "src/core/lib/channel/handshaker_registry.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/slice/slice_internal.h" - -typedef struct { - grpc_connector base; - - gpr_mu mu; - gpr_refcount refs; - - bool shutdown; - bool connecting; - - grpc_closure *notify; - grpc_connect_in_args args; - grpc_connect_out_args *result; - - grpc_endpoint *endpoint; // Non-NULL until handshaking starts. - - grpc_closure connected; - - grpc_handshake_manager *handshake_mgr; -} chttp2_connector; - -static void chttp2_connector_ref(grpc_connector *con) { - chttp2_connector *c = (chttp2_connector *)con; - gpr_ref(&c->refs); -} - -static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx, - grpc_connector *con) { - chttp2_connector *c = (chttp2_connector *)con; - if (gpr_unref(&c->refs)) { - gpr_mu_destroy(&c->mu); - // If handshaking is not yet in progress, destroy the endpoint. - // Otherwise, the handshaker will do this for us. - if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint); - gpr_free(c); - } -} - -static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx, - grpc_connector *con, grpc_error *why) { - chttp2_connector *c = (chttp2_connector *)con; - gpr_mu_lock(&c->mu); - c->shutdown = true; - if (c->handshake_mgr != NULL) { - grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr, - GRPC_ERROR_REF(why)); - } - // If handshaking is not yet in progress, shutdown the endpoint. - // Otherwise, the handshaker will do this for us. - if (!c->connecting && c->endpoint != NULL) { - grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(why)); - } - gpr_mu_unlock(&c->mu); - GRPC_ERROR_UNREF(why); -} - -static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_handshaker_args *args = (grpc_handshaker_args *)arg; - chttp2_connector *c = (chttp2_connector *)args->user_data; - gpr_mu_lock(&c->mu); - if (error != GRPC_ERROR_NONE || c->shutdown) { - if (error == GRPC_ERROR_NONE) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown"); - // We were shut down after handshaking completed successfully, so - // destroy the endpoint here. - // TODO(ctiller): It is currently necessary to shutdown endpoints - // before destroying them, even if we know that there are no - // pending read/write callbacks. This should be fixed, at which - // point this can be removed. - grpc_endpoint_shutdown(exec_ctx, args->endpoint, GRPC_ERROR_REF(error)); - grpc_endpoint_destroy(exec_ctx, args->endpoint); - grpc_channel_args_destroy(exec_ctx, args->args); - grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer); - gpr_free(args->read_buffer); - } else { - error = GRPC_ERROR_REF(error); - } - memset(c->result, 0, sizeof(*c->result)); - } else { - c->result->transport = - grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1); - GPR_ASSERT(c->result->transport); - grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, - args->read_buffer); - c->result->channel_args = args->args; - } - grpc_closure *notify = c->notify; - c->notify = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, notify, error); - grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr); - c->handshake_mgr = NULL; - gpr_mu_unlock(&c->mu); - chttp2_connector_unref(exec_ctx, (grpc_connector *)c); -} - -static void start_handshake_locked(grpc_exec_ctx *exec_ctx, - chttp2_connector *c) { - c->handshake_mgr = grpc_handshake_manager_create(); - grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, c->args.channel_args, - c->handshake_mgr); - grpc_handshake_manager_do_handshake( - exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args, - c->args.deadline, NULL /* acceptor */, on_handshake_done, c); - c->endpoint = NULL; // Endpoint handed off to handshake manager. -} - -static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - chttp2_connector *c = (chttp2_connector *)arg; - gpr_mu_lock(&c->mu); - GPR_ASSERT(c->connecting); - c->connecting = false; - if (error != GRPC_ERROR_NONE || c->shutdown) { - if (error == GRPC_ERROR_NONE) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown"); - } else { - error = GRPC_ERROR_REF(error); - } - memset(c->result, 0, sizeof(*c->result)); - grpc_closure *notify = c->notify; - c->notify = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, notify, error); - if (c->endpoint != NULL) { - grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(error)); - } - gpr_mu_unlock(&c->mu); - chttp2_connector_unref(exec_ctx, (grpc_connector *)arg); - } else { - GPR_ASSERT(c->endpoint != NULL); - start_handshake_locked(exec_ctx, c); - gpr_mu_unlock(&c->mu); - } -} - -static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx, - grpc_connector *con, - const grpc_connect_in_args *args, - grpc_connect_out_args *result, - grpc_closure *notify) { - chttp2_connector *c = (chttp2_connector *)con; - grpc_resolved_address addr; - grpc_get_subchannel_address_arg(exec_ctx, args->channel_args, &addr); - gpr_mu_lock(&c->mu); - GPR_ASSERT(c->notify == NULL); - c->notify = notify; - c->args = *args; - c->result = result; - GPR_ASSERT(c->endpoint == NULL); - chttp2_connector_ref(con); // Ref taken for callback. - GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx); - GPR_ASSERT(!c->connecting); - c->connecting = true; - grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint, - args->interested_parties, args->channel_args, &addr, - args->deadline); - gpr_mu_unlock(&c->mu); -} - -static const grpc_connector_vtable chttp2_connector_vtable = { - chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown, - chttp2_connector_connect}; - -grpc_connector *grpc_chttp2_connector_create() { - chttp2_connector *c = (chttp2_connector *)gpr_zalloc(sizeof(*c)); - c->base.vtable = &chttp2_connector_vtable; - gpr_mu_init(&c->mu); - gpr_ref_init(&c->refs, 1); - return &c->base; -} diff --git a/src/core/ext/transport/chttp2/client/chttp2_connector.cc b/src/core/ext/transport/chttp2/client/chttp2_connector.cc new file mode 100644 index 0000000000..202bcd47f5 --- /dev/null +++ b/src/core/ext/transport/chttp2/client/chttp2_connector.cc @@ -0,0 +1,206 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" + +#include + +#include + +#include +#include +#include + +#include "src/core/ext/filters/client_channel/connector.h" +#include "src/core/ext/filters/client_channel/http_connect_handshaker.h" +#include "src/core/ext/filters/client_channel/subchannel.h" +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/slice/slice_internal.h" + +typedef struct { + grpc_connector base; + + gpr_mu mu; + gpr_refcount refs; + + bool shutdown; + bool connecting; + + grpc_closure *notify; + grpc_connect_in_args args; + grpc_connect_out_args *result; + + grpc_endpoint *endpoint; // Non-NULL until handshaking starts. + + grpc_closure connected; + + grpc_handshake_manager *handshake_mgr; +} chttp2_connector; + +static void chttp2_connector_ref(grpc_connector *con) { + chttp2_connector *c = (chttp2_connector *)con; + gpr_ref(&c->refs); +} + +static void chttp2_connector_unref(grpc_exec_ctx *exec_ctx, + grpc_connector *con) { + chttp2_connector *c = (chttp2_connector *)con; + if (gpr_unref(&c->refs)) { + gpr_mu_destroy(&c->mu); + // If handshaking is not yet in progress, destroy the endpoint. + // Otherwise, the handshaker will do this for us. + if (c->endpoint != NULL) grpc_endpoint_destroy(exec_ctx, c->endpoint); + gpr_free(c); + } +} + +static void chttp2_connector_shutdown(grpc_exec_ctx *exec_ctx, + grpc_connector *con, grpc_error *why) { + chttp2_connector *c = (chttp2_connector *)con; + gpr_mu_lock(&c->mu); + c->shutdown = true; + if (c->handshake_mgr != NULL) { + grpc_handshake_manager_shutdown(exec_ctx, c->handshake_mgr, + GRPC_ERROR_REF(why)); + } + // If handshaking is not yet in progress, shutdown the endpoint. + // Otherwise, the handshaker will do this for us. + if (!c->connecting && c->endpoint != NULL) { + grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(why)); + } + gpr_mu_unlock(&c->mu); + GRPC_ERROR_UNREF(why); +} + +static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_handshaker_args *args = (grpc_handshaker_args *)arg; + chttp2_connector *c = (chttp2_connector *)args->user_data; + gpr_mu_lock(&c->mu); + if (error != GRPC_ERROR_NONE || c->shutdown) { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown"); + // We were shut down after handshaking completed successfully, so + // destroy the endpoint here. + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(exec_ctx, args->endpoint, GRPC_ERROR_REF(error)); + grpc_endpoint_destroy(exec_ctx, args->endpoint); + grpc_channel_args_destroy(exec_ctx, args->args); + grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer); + gpr_free(args->read_buffer); + } else { + error = GRPC_ERROR_REF(error); + } + memset(c->result, 0, sizeof(*c->result)); + } else { + c->result->transport = + grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 1); + GPR_ASSERT(c->result->transport); + grpc_chttp2_transport_start_reading(exec_ctx, c->result->transport, + args->read_buffer); + c->result->channel_args = args->args; + } + grpc_closure *notify = c->notify; + c->notify = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, notify, error); + grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr); + c->handshake_mgr = NULL; + gpr_mu_unlock(&c->mu); + chttp2_connector_unref(exec_ctx, (grpc_connector *)c); +} + +static void start_handshake_locked(grpc_exec_ctx *exec_ctx, + chttp2_connector *c) { + c->handshake_mgr = grpc_handshake_manager_create(); + grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, c->args.channel_args, + c->handshake_mgr); + grpc_handshake_manager_do_handshake( + exec_ctx, c->handshake_mgr, c->endpoint, c->args.channel_args, + c->args.deadline, NULL /* acceptor */, on_handshake_done, c); + c->endpoint = NULL; // Endpoint handed off to handshake manager. +} + +static void connected(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + chttp2_connector *c = (chttp2_connector *)arg; + gpr_mu_lock(&c->mu); + GPR_ASSERT(c->connecting); + c->connecting = false; + if (error != GRPC_ERROR_NONE || c->shutdown) { + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("connector shutdown"); + } else { + error = GRPC_ERROR_REF(error); + } + memset(c->result, 0, sizeof(*c->result)); + grpc_closure *notify = c->notify; + c->notify = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, notify, error); + if (c->endpoint != NULL) { + grpc_endpoint_shutdown(exec_ctx, c->endpoint, GRPC_ERROR_REF(error)); + } + gpr_mu_unlock(&c->mu); + chttp2_connector_unref(exec_ctx, (grpc_connector *)arg); + } else { + GPR_ASSERT(c->endpoint != NULL); + start_handshake_locked(exec_ctx, c); + gpr_mu_unlock(&c->mu); + } +} + +static void chttp2_connector_connect(grpc_exec_ctx *exec_ctx, + grpc_connector *con, + const grpc_connect_in_args *args, + grpc_connect_out_args *result, + grpc_closure *notify) { + chttp2_connector *c = (chttp2_connector *)con; + grpc_resolved_address addr; + grpc_get_subchannel_address_arg(exec_ctx, args->channel_args, &addr); + gpr_mu_lock(&c->mu); + GPR_ASSERT(c->notify == NULL); + c->notify = notify; + c->args = *args; + c->result = result; + GPR_ASSERT(c->endpoint == NULL); + chttp2_connector_ref(con); // Ref taken for callback. + GRPC_CLOSURE_INIT(&c->connected, connected, c, grpc_schedule_on_exec_ctx); + GPR_ASSERT(!c->connecting); + c->connecting = true; + grpc_tcp_client_connect(exec_ctx, &c->connected, &c->endpoint, + args->interested_parties, args->channel_args, &addr, + args->deadline); + gpr_mu_unlock(&c->mu); +} + +static const grpc_connector_vtable chttp2_connector_vtable = { + chttp2_connector_ref, chttp2_connector_unref, chttp2_connector_shutdown, + chttp2_connector_connect}; + +grpc_connector *grpc_chttp2_connector_create() { + chttp2_connector *c = (chttp2_connector *)gpr_zalloc(sizeof(*c)); + c->base.vtable = &chttp2_connector_vtable; + gpr_mu_init(&c->mu); + gpr_ref_init(&c->refs, 1); + return &c->base; +} diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.c b/src/core/ext/transport/chttp2/client/insecure/channel_create.c deleted file mode 100644 index 6410a6043d..0000000000 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/channel.h" - -static void client_channel_factory_ref( - grpc_client_channel_factory *cc_factory) {} - -static void client_channel_factory_unref( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {} - -static grpc_subchannel *client_channel_factory_create_subchannel( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - const grpc_subchannel_args *args) { - grpc_connector *connector = grpc_chttp2_connector_create(); - grpc_subchannel *s = grpc_subchannel_create(exec_ctx, connector, args); - grpc_connector_unref(exec_ctx, connector); - return s; -} - -static grpc_channel *client_channel_factory_create_channel( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - const char *target, grpc_client_channel_type type, - const grpc_channel_args *args) { - if (target == NULL) { - gpr_log(GPR_ERROR, "cannot create channel with NULL target name"); - return NULL; - } - // Add channel arg containing the server URI. - grpc_arg arg = grpc_channel_arg_string_create( - (char *)GRPC_ARG_SERVER_URI, - grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target)); - const char *to_remove[] = {GRPC_ARG_SERVER_URI}; - grpc_channel_args *new_args = - grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1); - gpr_free(arg.value.string); - grpc_channel *channel = grpc_channel_create(exec_ctx, target, new_args, - GRPC_CLIENT_CHANNEL, NULL); - grpc_channel_args_destroy(exec_ctx, new_args); - return channel; -} - -static const grpc_client_channel_factory_vtable client_channel_factory_vtable = - {client_channel_factory_ref, client_channel_factory_unref, - client_channel_factory_create_subchannel, - client_channel_factory_create_channel}; - -static grpc_client_channel_factory client_channel_factory = { - &client_channel_factory_vtable}; - -/* Create a client channel: - Asynchronously: - resolve target - - connect to it (trying alternatives as presented) - - perform handshakes */ -grpc_channel *grpc_insecure_channel_create(const char *target, - const grpc_channel_args *args, - void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( - "grpc_insecure_channel_create(target=%s, args=%p, reserved=%p)", 3, - (target, args, reserved)); - GPR_ASSERT(reserved == NULL); - // Add channel arg containing the client channel factory. - grpc_arg arg = - grpc_client_channel_factory_create_channel_arg(&client_channel_factory); - grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); - // Create channel. - grpc_channel *channel = client_channel_factory_create_channel( - &exec_ctx, &client_channel_factory, target, - GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); - // Clean up. - grpc_channel_args_destroy(&exec_ctx, new_args); - grpc_exec_ctx_finish(&exec_ctx); - return channel != NULL ? channel : grpc_lame_client_channel_create( - target, GRPC_STATUS_INTERNAL, - "Failed to create client channel"); -} diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create.cc b/src/core/ext/transport/chttp2/client/insecure/channel_create.cc new file mode 100644 index 0000000000..6410a6043d --- /dev/null +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create.cc @@ -0,0 +1,104 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" + +static void client_channel_factory_ref( + grpc_client_channel_factory *cc_factory) {} + +static void client_channel_factory_unref( + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {} + +static grpc_subchannel *client_channel_factory_create_subchannel( + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, + const grpc_subchannel_args *args) { + grpc_connector *connector = grpc_chttp2_connector_create(); + grpc_subchannel *s = grpc_subchannel_create(exec_ctx, connector, args); + grpc_connector_unref(exec_ctx, connector); + return s; +} + +static grpc_channel *client_channel_factory_create_channel( + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, + const char *target, grpc_client_channel_type type, + const grpc_channel_args *args) { + if (target == NULL) { + gpr_log(GPR_ERROR, "cannot create channel with NULL target name"); + return NULL; + } + // Add channel arg containing the server URI. + grpc_arg arg = grpc_channel_arg_string_create( + (char *)GRPC_ARG_SERVER_URI, + grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target)); + const char *to_remove[] = {GRPC_ARG_SERVER_URI}; + grpc_channel_args *new_args = + grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1); + gpr_free(arg.value.string); + grpc_channel *channel = grpc_channel_create(exec_ctx, target, new_args, + GRPC_CLIENT_CHANNEL, NULL); + grpc_channel_args_destroy(exec_ctx, new_args); + return channel; +} + +static const grpc_client_channel_factory_vtable client_channel_factory_vtable = + {client_channel_factory_ref, client_channel_factory_unref, + client_channel_factory_create_subchannel, + client_channel_factory_create_channel}; + +static grpc_client_channel_factory client_channel_factory = { + &client_channel_factory_vtable}; + +/* Create a client channel: + Asynchronously: - resolve target + - connect to it (trying alternatives as presented) + - perform handshakes */ +grpc_channel *grpc_insecure_channel_create(const char *target, + const grpc_channel_args *args, + void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE( + "grpc_insecure_channel_create(target=%s, args=%p, reserved=%p)", 3, + (target, args, reserved)); + GPR_ASSERT(reserved == NULL); + // Add channel arg containing the client channel factory. + grpc_arg arg = + grpc_client_channel_factory_create_channel_arg(&client_channel_factory); + grpc_channel_args *new_args = grpc_channel_args_copy_and_add(args, &arg, 1); + // Create channel. + grpc_channel *channel = client_channel_factory_create_channel( + &exec_ctx, &client_channel_factory, target, + GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); + // Clean up. + grpc_channel_args_destroy(&exec_ctx, new_args); + grpc_exec_ctx_finish(&exec_ctx); + return channel != NULL ? channel : grpc_lame_client_channel_create( + target, GRPC_STATUS_INTERNAL, + "Failed to create client channel"); +} diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c deleted file mode 100644 index dd88136f7b..0000000000 --- a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include - -#ifdef GPR_SUPPORT_CHANNELS_FROM_FD - -#include - -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/endpoint.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/tcp_client_posix.h" -#include "src/core/lib/iomgr/tcp_posix.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/transport/transport.h" - -grpc_channel *grpc_insecure_channel_create_from_fd( - const char *target, int fd, const grpc_channel_args *args) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE("grpc_insecure_channel_create(target=%p, fd=%d, args=%p)", 3, - (target, fd, args)); - - grpc_arg default_authority_arg = grpc_channel_arg_string_create( - (char *)GRPC_ARG_DEFAULT_AUTHORITY, (char *)"test.authority"); - grpc_channel_args *final_args = - grpc_channel_args_copy_and_add(args, &default_authority_arg, 1); - - int flags = fcntl(fd, F_GETFL, 0); - GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0); - - grpc_endpoint *client = grpc_tcp_client_create_from_fd( - &exec_ctx, grpc_fd_create(fd, "client"), args, "fd-client"); - - grpc_transport *transport = - grpc_create_chttp2_transport(&exec_ctx, final_args, client, 1); - GPR_ASSERT(transport); - grpc_channel *channel = grpc_channel_create( - &exec_ctx, target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport); - grpc_channel_args_destroy(&exec_ctx, final_args); - grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL); - - grpc_exec_ctx_finish(&exec_ctx); - - return channel != NULL ? channel : grpc_lame_client_channel_create( - target, GRPC_STATUS_INTERNAL, - "Failed to create client channel"); -} - -#else // !GPR_SUPPORT_CHANNELS_FROM_FD - -grpc_channel *grpc_insecure_channel_create_from_fd( - const char *target, int fd, const grpc_channel_args *args) { - GPR_ASSERT(0); - return NULL; -} - -#endif // GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc new file mode 100644 index 0000000000..dd88136f7b --- /dev/null +++ b/src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/tcp_client_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/transport.h" + +grpc_channel *grpc_insecure_channel_create_from_fd( + const char *target, int fd, const grpc_channel_args *args) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE("grpc_insecure_channel_create(target=%p, fd=%d, args=%p)", 3, + (target, fd, args)); + + grpc_arg default_authority_arg = grpc_channel_arg_string_create( + (char *)GRPC_ARG_DEFAULT_AUTHORITY, (char *)"test.authority"); + grpc_channel_args *final_args = + grpc_channel_args_copy_and_add(args, &default_authority_arg, 1); + + int flags = fcntl(fd, F_GETFL, 0); + GPR_ASSERT(fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0); + + grpc_endpoint *client = grpc_tcp_client_create_from_fd( + &exec_ctx, grpc_fd_create(fd, "client"), args, "fd-client"); + + grpc_transport *transport = + grpc_create_chttp2_transport(&exec_ctx, final_args, client, 1); + GPR_ASSERT(transport); + grpc_channel *channel = grpc_channel_create( + &exec_ctx, target, final_args, GRPC_CLIENT_DIRECT_CHANNEL, transport); + grpc_channel_args_destroy(&exec_ctx, final_args); + grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL); + + grpc_exec_ctx_finish(&exec_ctx); + + return channel != NULL ? channel : grpc_lame_client_channel_create( + target, GRPC_STATUS_INTERNAL, + "Failed to create client channel"); +} + +#else // !GPR_SUPPORT_CHANNELS_FROM_FD + +grpc_channel *grpc_insecure_channel_create_from_fd( + const char *target, int fd, const grpc_channel_args *args) { + GPR_ASSERT(0); + return NULL; +} + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c deleted file mode 100644 index fe296cf4ff..0000000000 --- a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include -#include - -#include "src/core/ext/filters/client_channel/client_channel.h" -#include "src/core/ext/filters/client_channel/resolver_registry.h" -#include "src/core/ext/filters/client_channel/uri_parser.h" -#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/transport/lb_targets_info.h" -#include "src/core/lib/security/transport/security_connector.h" -#include "src/core/lib/slice/slice_hash_table.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/channel.h" - -static void client_channel_factory_ref( - grpc_client_channel_factory *cc_factory) {} - -static void client_channel_factory_unref( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {} - -static grpc_subchannel_args *get_secure_naming_subchannel_args( - grpc_exec_ctx *exec_ctx, const grpc_subchannel_args *args) { - grpc_channel_credentials *channel_credentials = - grpc_channel_credentials_find_in_args(args->args); - if (channel_credentials == NULL) { - gpr_log(GPR_ERROR, - "Can't create subchannel: channel credentials missing for secure " - "channel."); - return NULL; - } - // Make sure security connector does not already exist in args. - if (grpc_security_connector_find_in_args(args->args) != NULL) { - gpr_log(GPR_ERROR, - "Can't create subchannel: security connector already present in " - "channel args."); - return NULL; - } - // To which address are we connecting? By default, use the server URI. - const grpc_arg *server_uri_arg = - grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI); - GPR_ASSERT(server_uri_arg != NULL); - GPR_ASSERT(server_uri_arg->type == GRPC_ARG_STRING); - const char *server_uri_str = server_uri_arg->value.string; - GPR_ASSERT(server_uri_str != NULL); - grpc_uri *server_uri = - grpc_uri_parse(exec_ctx, server_uri_str, true /* supress errors */); - GPR_ASSERT(server_uri != NULL); - const char *server_uri_path; - server_uri_path = - server_uri->path[0] == '/' ? server_uri->path + 1 : server_uri->path; - const grpc_slice_hash_table *targets_info = - grpc_lb_targets_info_find_in_args(args->args); - char *target_name_to_check = NULL; - if (targets_info != NULL) { // LB channel - // Find the balancer name for the target. - const char *target_uri_str = - grpc_get_subchannel_address_uri_arg(args->args); - grpc_uri *target_uri = - grpc_uri_parse(exec_ctx, target_uri_str, false /* suppress errors */); - GPR_ASSERT(target_uri != NULL); - if (target_uri->path[0] != '\0') { // "path" may be empty - const grpc_slice key = grpc_slice_from_static_string( - target_uri->path[0] == '/' ? target_uri->path + 1 : target_uri->path); - const char *value = - (const char *)grpc_slice_hash_table_get(targets_info, key); - if (value != NULL) target_name_to_check = gpr_strdup(value); - grpc_slice_unref_internal(exec_ctx, key); - } - if (target_name_to_check == NULL) { - // If the target name to check hasn't already been set, fall back to using - // SERVER_URI - target_name_to_check = gpr_strdup(server_uri_path); - } - grpc_uri_destroy(target_uri); - } else { // regular channel: the secure name is the original server URI. - target_name_to_check = gpr_strdup(server_uri_path); - } - grpc_uri_destroy(server_uri); - GPR_ASSERT(target_name_to_check != NULL); - grpc_channel_security_connector *subchannel_security_connector = NULL; - // Create the security connector using the credentials and target name. - grpc_channel_args *new_args_from_connector = NULL; - const grpc_security_status security_status = - grpc_channel_credentials_create_security_connector( - exec_ctx, channel_credentials, target_name_to_check, args->args, - &subchannel_security_connector, &new_args_from_connector); - if (security_status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, - "Failed to create secure subchannel for secure name '%s'", - target_name_to_check); - gpr_free(target_name_to_check); - return NULL; - } - gpr_free(target_name_to_check); - grpc_arg new_security_connector_arg = - grpc_security_connector_to_arg(&subchannel_security_connector->base); - - grpc_channel_args *new_args = grpc_channel_args_copy_and_add( - new_args_from_connector != NULL ? new_args_from_connector : args->args, - &new_security_connector_arg, 1); - GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &subchannel_security_connector->base, - "lb_channel_create"); - if (new_args_from_connector != NULL) { - grpc_channel_args_destroy(exec_ctx, new_args_from_connector); - } - grpc_subchannel_args *final_sc_args = - (grpc_subchannel_args *)gpr_malloc(sizeof(*final_sc_args)); - memcpy(final_sc_args, args, sizeof(*args)); - final_sc_args->args = new_args; - return final_sc_args; -} - -static grpc_subchannel *client_channel_factory_create_subchannel( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - const grpc_subchannel_args *args) { - grpc_subchannel_args *subchannel_args = - get_secure_naming_subchannel_args(exec_ctx, args); - if (subchannel_args == NULL) { - gpr_log( - GPR_ERROR, - "Failed to create subchannel arguments during subchannel creation."); - return NULL; - } - grpc_connector *connector = grpc_chttp2_connector_create(); - grpc_subchannel *s = - grpc_subchannel_create(exec_ctx, connector, subchannel_args); - grpc_connector_unref(exec_ctx, connector); - grpc_channel_args_destroy(exec_ctx, - (grpc_channel_args *)subchannel_args->args); - gpr_free(subchannel_args); - return s; -} - -static grpc_channel *client_channel_factory_create_channel( - grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, - const char *target, grpc_client_channel_type type, - const grpc_channel_args *args) { - if (target == NULL) { - gpr_log(GPR_ERROR, "cannot create channel with NULL target name"); - return NULL; - } - // Add channel arg containing the server URI. - grpc_arg arg = grpc_channel_arg_string_create( - (char *)GRPC_ARG_SERVER_URI, - grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target)); - const char *to_remove[] = {GRPC_ARG_SERVER_URI}; - grpc_channel_args *new_args = - grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1); - gpr_free(arg.value.string); - grpc_channel *channel = grpc_channel_create(exec_ctx, target, new_args, - GRPC_CLIENT_CHANNEL, NULL); - grpc_channel_args_destroy(exec_ctx, new_args); - return channel; -} - -static const grpc_client_channel_factory_vtable client_channel_factory_vtable = - {client_channel_factory_ref, client_channel_factory_unref, - client_channel_factory_create_subchannel, - client_channel_factory_create_channel}; - -static grpc_client_channel_factory client_channel_factory = { - &client_channel_factory_vtable}; - -// Create a secure client channel: -// Asynchronously: - resolve target -// - connect to it (trying alternatives as presented) -// - perform handshakes -grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, - const char *target, - const grpc_channel_args *args, - void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( - "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " - "reserved=%p)", - 4, ((void *)creds, target, (void *)args, (void *)reserved)); - GPR_ASSERT(reserved == NULL); - grpc_channel *channel = NULL; - if (creds != NULL) { - // Add channel args containing the client channel factory and channel - // credentials. - grpc_arg args_to_add[] = { - grpc_client_channel_factory_create_channel_arg(&client_channel_factory), - grpc_channel_credentials_to_arg(creds)}; - grpc_channel_args *new_args = grpc_channel_args_copy_and_add( - args, args_to_add, GPR_ARRAY_SIZE(args_to_add)); - // Create channel. - channel = client_channel_factory_create_channel( - &exec_ctx, &client_channel_factory, target, - GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); - // Clean up. - grpc_channel_args_destroy(&exec_ctx, new_args); - grpc_exec_ctx_finish(&exec_ctx); - } - return channel != NULL ? channel - : grpc_lame_client_channel_create( - target, GRPC_STATUS_INTERNAL, - "Failed to create secure client channel"); -} diff --git a/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc new file mode 100644 index 0000000000..fe296cf4ff --- /dev/null +++ b/src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc @@ -0,0 +1,224 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include + +#include "src/core/ext/filters/client_channel/client_channel.h" +#include "src/core/ext/filters/client_channel/resolver_registry.h" +#include "src/core/ext/filters/client_channel/uri_parser.h" +#include "src/core/ext/transport/chttp2/client/chttp2_connector.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/lb_targets_info.h" +#include "src/core/lib/security/transport/security_connector.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" + +static void client_channel_factory_ref( + grpc_client_channel_factory *cc_factory) {} + +static void client_channel_factory_unref( + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory) {} + +static grpc_subchannel_args *get_secure_naming_subchannel_args( + grpc_exec_ctx *exec_ctx, const grpc_subchannel_args *args) { + grpc_channel_credentials *channel_credentials = + grpc_channel_credentials_find_in_args(args->args); + if (channel_credentials == NULL) { + gpr_log(GPR_ERROR, + "Can't create subchannel: channel credentials missing for secure " + "channel."); + return NULL; + } + // Make sure security connector does not already exist in args. + if (grpc_security_connector_find_in_args(args->args) != NULL) { + gpr_log(GPR_ERROR, + "Can't create subchannel: security connector already present in " + "channel args."); + return NULL; + } + // To which address are we connecting? By default, use the server URI. + const grpc_arg *server_uri_arg = + grpc_channel_args_find(args->args, GRPC_ARG_SERVER_URI); + GPR_ASSERT(server_uri_arg != NULL); + GPR_ASSERT(server_uri_arg->type == GRPC_ARG_STRING); + const char *server_uri_str = server_uri_arg->value.string; + GPR_ASSERT(server_uri_str != NULL); + grpc_uri *server_uri = + grpc_uri_parse(exec_ctx, server_uri_str, true /* supress errors */); + GPR_ASSERT(server_uri != NULL); + const char *server_uri_path; + server_uri_path = + server_uri->path[0] == '/' ? server_uri->path + 1 : server_uri->path; + const grpc_slice_hash_table *targets_info = + grpc_lb_targets_info_find_in_args(args->args); + char *target_name_to_check = NULL; + if (targets_info != NULL) { // LB channel + // Find the balancer name for the target. + const char *target_uri_str = + grpc_get_subchannel_address_uri_arg(args->args); + grpc_uri *target_uri = + grpc_uri_parse(exec_ctx, target_uri_str, false /* suppress errors */); + GPR_ASSERT(target_uri != NULL); + if (target_uri->path[0] != '\0') { // "path" may be empty + const grpc_slice key = grpc_slice_from_static_string( + target_uri->path[0] == '/' ? target_uri->path + 1 : target_uri->path); + const char *value = + (const char *)grpc_slice_hash_table_get(targets_info, key); + if (value != NULL) target_name_to_check = gpr_strdup(value); + grpc_slice_unref_internal(exec_ctx, key); + } + if (target_name_to_check == NULL) { + // If the target name to check hasn't already been set, fall back to using + // SERVER_URI + target_name_to_check = gpr_strdup(server_uri_path); + } + grpc_uri_destroy(target_uri); + } else { // regular channel: the secure name is the original server URI. + target_name_to_check = gpr_strdup(server_uri_path); + } + grpc_uri_destroy(server_uri); + GPR_ASSERT(target_name_to_check != NULL); + grpc_channel_security_connector *subchannel_security_connector = NULL; + // Create the security connector using the credentials and target name. + grpc_channel_args *new_args_from_connector = NULL; + const grpc_security_status security_status = + grpc_channel_credentials_create_security_connector( + exec_ctx, channel_credentials, target_name_to_check, args->args, + &subchannel_security_connector, &new_args_from_connector); + if (security_status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, + "Failed to create secure subchannel for secure name '%s'", + target_name_to_check); + gpr_free(target_name_to_check); + return NULL; + } + gpr_free(target_name_to_check); + grpc_arg new_security_connector_arg = + grpc_security_connector_to_arg(&subchannel_security_connector->base); + + grpc_channel_args *new_args = grpc_channel_args_copy_and_add( + new_args_from_connector != NULL ? new_args_from_connector : args->args, + &new_security_connector_arg, 1); + GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &subchannel_security_connector->base, + "lb_channel_create"); + if (new_args_from_connector != NULL) { + grpc_channel_args_destroy(exec_ctx, new_args_from_connector); + } + grpc_subchannel_args *final_sc_args = + (grpc_subchannel_args *)gpr_malloc(sizeof(*final_sc_args)); + memcpy(final_sc_args, args, sizeof(*args)); + final_sc_args->args = new_args; + return final_sc_args; +} + +static grpc_subchannel *client_channel_factory_create_subchannel( + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, + const grpc_subchannel_args *args) { + grpc_subchannel_args *subchannel_args = + get_secure_naming_subchannel_args(exec_ctx, args); + if (subchannel_args == NULL) { + gpr_log( + GPR_ERROR, + "Failed to create subchannel arguments during subchannel creation."); + return NULL; + } + grpc_connector *connector = grpc_chttp2_connector_create(); + grpc_subchannel *s = + grpc_subchannel_create(exec_ctx, connector, subchannel_args); + grpc_connector_unref(exec_ctx, connector); + grpc_channel_args_destroy(exec_ctx, + (grpc_channel_args *)subchannel_args->args); + gpr_free(subchannel_args); + return s; +} + +static grpc_channel *client_channel_factory_create_channel( + grpc_exec_ctx *exec_ctx, grpc_client_channel_factory *cc_factory, + const char *target, grpc_client_channel_type type, + const grpc_channel_args *args) { + if (target == NULL) { + gpr_log(GPR_ERROR, "cannot create channel with NULL target name"); + return NULL; + } + // Add channel arg containing the server URI. + grpc_arg arg = grpc_channel_arg_string_create( + (char *)GRPC_ARG_SERVER_URI, + grpc_resolver_factory_add_default_prefix_if_needed(exec_ctx, target)); + const char *to_remove[] = {GRPC_ARG_SERVER_URI}; + grpc_channel_args *new_args = + grpc_channel_args_copy_and_add_and_remove(args, to_remove, 1, &arg, 1); + gpr_free(arg.value.string); + grpc_channel *channel = grpc_channel_create(exec_ctx, target, new_args, + GRPC_CLIENT_CHANNEL, NULL); + grpc_channel_args_destroy(exec_ctx, new_args); + return channel; +} + +static const grpc_client_channel_factory_vtable client_channel_factory_vtable = + {client_channel_factory_ref, client_channel_factory_unref, + client_channel_factory_create_subchannel, + client_channel_factory_create_channel}; + +static grpc_client_channel_factory client_channel_factory = { + &client_channel_factory_vtable}; + +// Create a secure client channel: +// Asynchronously: - resolve target +// - connect to it (trying alternatives as presented) +// - perform handshakes +grpc_channel *grpc_secure_channel_create(grpc_channel_credentials *creds, + const char *target, + const grpc_channel_args *args, + void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE( + "grpc_secure_channel_create(creds=%p, target=%s, args=%p, " + "reserved=%p)", + 4, ((void *)creds, target, (void *)args, (void *)reserved)); + GPR_ASSERT(reserved == NULL); + grpc_channel *channel = NULL; + if (creds != NULL) { + // Add channel args containing the client channel factory and channel + // credentials. + grpc_arg args_to_add[] = { + grpc_client_channel_factory_create_channel_arg(&client_channel_factory), + grpc_channel_credentials_to_arg(creds)}; + grpc_channel_args *new_args = grpc_channel_args_copy_and_add( + args, args_to_add, GPR_ARRAY_SIZE(args_to_add)); + // Create channel. + channel = client_channel_factory_create_channel( + &exec_ctx, &client_channel_factory, target, + GRPC_CLIENT_CHANNEL_TYPE_REGULAR, new_args); + // Clean up. + grpc_channel_args_destroy(&exec_ctx, new_args); + grpc_exec_ctx_finish(&exec_ctx); + } + return channel != NULL ? channel + : grpc_lame_client_channel_create( + target, GRPC_STATUS_INTERNAL, + "Failed to create secure client channel"); +} diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.c b/src/core/ext/transport/chttp2/server/chttp2_server.c deleted file mode 100644 index 60244e163b..0000000000 --- a/src/core/ext/transport/chttp2/server/chttp2_server.c +++ /dev/null @@ -1,292 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/server/chttp2_server.h" - -#include - -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/filters/http/server/http_server_filter.h" -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker.h" -#include "src/core/lib/channel/handshaker_registry.h" -#include "src/core/lib/iomgr/endpoint.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/tcp_server.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/server.h" - -typedef struct { - grpc_server *server; - grpc_tcp_server *tcp_server; - grpc_channel_args *args; - gpr_mu mu; - bool shutdown; - grpc_closure tcp_server_shutdown_complete; - grpc_closure *server_destroy_listener_done; - grpc_handshake_manager *pending_handshake_mgrs; -} server_state; - -typedef struct { - server_state *svr_state; - grpc_pollset *accepting_pollset; - grpc_tcp_server_acceptor *acceptor; - grpc_handshake_manager *handshake_mgr; -} server_connection_state; - -static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_handshaker_args *args = (grpc_handshaker_args *)arg; - server_connection_state *connection_state = - (server_connection_state *)args->user_data; - gpr_mu_lock(&connection_state->svr_state->mu); - if (error != GRPC_ERROR_NONE || connection_state->svr_state->shutdown) { - const char *error_str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str); - - if (error == GRPC_ERROR_NONE && args->endpoint != NULL) { - // We were shut down after handshaking completed successfully, so - // destroy the endpoint here. - // TODO(ctiller): It is currently necessary to shutdown endpoints - // before destroying them, even if we know that there are no - // pending read/write callbacks. This should be fixed, at which - // point this can be removed. - grpc_endpoint_shutdown(exec_ctx, args->endpoint, GRPC_ERROR_NONE); - grpc_endpoint_destroy(exec_ctx, args->endpoint); - grpc_channel_args_destroy(exec_ctx, args->args); - grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer); - gpr_free(args->read_buffer); - } - } else { - // If the handshaking succeeded but there is no endpoint, then the - // handshaker may have handed off the connection to some external - // code, so we can just clean up here without creating a transport. - if (args->endpoint != NULL) { - grpc_transport *transport = - grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0); - grpc_server_setup_transport( - exec_ctx, connection_state->svr_state->server, transport, - connection_state->accepting_pollset, args->args); - grpc_chttp2_transport_start_reading(exec_ctx, transport, - args->read_buffer); - grpc_channel_args_destroy(exec_ctx, args->args); - } - } - grpc_handshake_manager_pending_list_remove( - &connection_state->svr_state->pending_handshake_mgrs, - connection_state->handshake_mgr); - gpr_mu_unlock(&connection_state->svr_state->mu); - grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr); - grpc_tcp_server_unref(exec_ctx, connection_state->svr_state->tcp_server); - gpr_free(connection_state->acceptor); - gpr_free(connection_state); -} - -static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, - grpc_pollset *accepting_pollset, - grpc_tcp_server_acceptor *acceptor) { - server_state *state = (server_state *)arg; - gpr_mu_lock(&state->mu); - if (state->shutdown) { - gpr_mu_unlock(&state->mu); - grpc_endpoint_shutdown(exec_ctx, tcp, GRPC_ERROR_NONE); - grpc_endpoint_destroy(exec_ctx, tcp); - gpr_free(acceptor); - return; - } - grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create(); - grpc_handshake_manager_pending_list_add(&state->pending_handshake_mgrs, - handshake_mgr); - gpr_mu_unlock(&state->mu); - grpc_tcp_server_ref(state->tcp_server); - server_connection_state *connection_state = - (server_connection_state *)gpr_malloc(sizeof(*connection_state)); - connection_state->svr_state = state; - connection_state->accepting_pollset = accepting_pollset; - connection_state->acceptor = acceptor; - connection_state->handshake_mgr = handshake_mgr; - grpc_handshakers_add(exec_ctx, HANDSHAKER_SERVER, state->args, - connection_state->handshake_mgr); - // TODO(roth): We should really get this timeout value from channel - // args instead of hard-coding it. - const gpr_timespec deadline = gpr_time_add( - gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN)); - grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr, - tcp, state->args, deadline, acceptor, - on_handshake_done, connection_state); -} - -/* Server callback: start listening on our ports */ -static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server, - void *arg, grpc_pollset **pollsets, - size_t pollset_count) { - server_state *state = (server_state *)arg; - gpr_mu_lock(&state->mu); - state->shutdown = false; - gpr_mu_unlock(&state->mu); - grpc_tcp_server_start(exec_ctx, state->tcp_server, pollsets, pollset_count, - on_accept, state); -} - -static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - server_state *state = (server_state *)arg; - /* ensure all threads have unlocked */ - gpr_mu_lock(&state->mu); - grpc_closure *destroy_done = state->server_destroy_listener_done; - GPR_ASSERT(state->shutdown); - grpc_handshake_manager_pending_list_shutdown_all( - exec_ctx, state->pending_handshake_mgrs, GRPC_ERROR_REF(error)); - gpr_mu_unlock(&state->mu); - // Flush queued work before destroying handshaker factory, since that - // may do a synchronous unref. - grpc_exec_ctx_flush(exec_ctx); - if (destroy_done != NULL) { - destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error)); - grpc_exec_ctx_flush(exec_ctx); - } - grpc_channel_args_destroy(exec_ctx, state->args); - gpr_mu_destroy(&state->mu); - gpr_free(state); -} - -/* Server callback: destroy the tcp listener (so we don't generate further - callbacks) */ -static void server_destroy_listener(grpc_exec_ctx *exec_ctx, - grpc_server *server, void *arg, - grpc_closure *destroy_done) { - server_state *state = (server_state *)arg; - gpr_mu_lock(&state->mu); - state->shutdown = true; - state->server_destroy_listener_done = destroy_done; - grpc_tcp_server *tcp_server = state->tcp_server; - gpr_mu_unlock(&state->mu); - grpc_tcp_server_shutdown_listeners(exec_ctx, tcp_server); - grpc_tcp_server_unref(exec_ctx, tcp_server); -} - -grpc_error *grpc_chttp2_server_add_port(grpc_exec_ctx *exec_ctx, - grpc_server *server, const char *addr, - grpc_channel_args *args, - int *port_num) { - grpc_resolved_addresses *resolved = NULL; - grpc_tcp_server *tcp_server = NULL; - size_t i; - size_t count = 0; - int port_temp; - grpc_error *err = GRPC_ERROR_NONE; - server_state *state = NULL; - grpc_error **errors = NULL; - size_t naddrs = 0; - - *port_num = -1; - - /* resolve address */ - err = grpc_blocking_resolve_address(addr, "https", &resolved); - if (err != GRPC_ERROR_NONE) { - goto error; - } - state = (server_state *)gpr_zalloc(sizeof(*state)); - GRPC_CLOSURE_INIT(&state->tcp_server_shutdown_complete, - tcp_server_shutdown_complete, state, - grpc_schedule_on_exec_ctx); - err = grpc_tcp_server_create(exec_ctx, &state->tcp_server_shutdown_complete, - args, &tcp_server); - if (err != GRPC_ERROR_NONE) { - goto error; - } - - state->server = server; - state->tcp_server = tcp_server; - state->args = args; - state->shutdown = true; - gpr_mu_init(&state->mu); - - naddrs = resolved->naddrs; - errors = (grpc_error **)gpr_malloc(sizeof(*errors) * naddrs); - for (i = 0; i < naddrs; i++) { - errors[i] = - grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp); - if (errors[i] == GRPC_ERROR_NONE) { - if (*port_num == -1) { - *port_num = port_temp; - } else { - GPR_ASSERT(*port_num == port_temp); - } - count++; - } - } - if (count == 0) { - char *msg; - gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved", - naddrs); - err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs); - gpr_free(msg); - goto error; - } else if (count != naddrs) { - char *msg; - gpr_asprintf(&msg, "Only %" PRIuPTR - " addresses added out of total %" PRIuPTR " resolved", - count, naddrs); - err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs); - gpr_free(msg); - - const char *warning_message = grpc_error_string(err); - gpr_log(GPR_INFO, "WARNING: %s", warning_message); - - /* we managed to bind some addresses: continue */ - } - grpc_resolved_addresses_destroy(resolved); - - /* Register with the server only upon success */ - grpc_server_add_listener(exec_ctx, server, state, server_start_listener, - server_destroy_listener); - goto done; - -/* Error path: cleanup and return */ -error: - GPR_ASSERT(err != GRPC_ERROR_NONE); - if (resolved) { - grpc_resolved_addresses_destroy(resolved); - } - if (tcp_server) { - grpc_tcp_server_unref(exec_ctx, tcp_server); - } else { - grpc_channel_args_destroy(exec_ctx, args); - gpr_free(state); - } - *port_num = 0; - -done: - if (errors != NULL) { - for (i = 0; i < naddrs; i++) { - GRPC_ERROR_UNREF(errors[i]); - } - gpr_free(errors); - } - return err; -} diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc new file mode 100644 index 0000000000..60244e163b --- /dev/null +++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc @@ -0,0 +1,292 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/server/chttp2_server.h" + +#include + +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/filters/http/server/http_server_filter.h" +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/server.h" + +typedef struct { + grpc_server *server; + grpc_tcp_server *tcp_server; + grpc_channel_args *args; + gpr_mu mu; + bool shutdown; + grpc_closure tcp_server_shutdown_complete; + grpc_closure *server_destroy_listener_done; + grpc_handshake_manager *pending_handshake_mgrs; +} server_state; + +typedef struct { + server_state *svr_state; + grpc_pollset *accepting_pollset; + grpc_tcp_server_acceptor *acceptor; + grpc_handshake_manager *handshake_mgr; +} server_connection_state; + +static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_handshaker_args *args = (grpc_handshaker_args *)arg; + server_connection_state *connection_state = + (server_connection_state *)args->user_data; + gpr_mu_lock(&connection_state->svr_state->mu); + if (error != GRPC_ERROR_NONE || connection_state->svr_state->shutdown) { + const char *error_str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Handshaking failed: %s", error_str); + + if (error == GRPC_ERROR_NONE && args->endpoint != NULL) { + // We were shut down after handshaking completed successfully, so + // destroy the endpoint here. + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(exec_ctx, args->endpoint, GRPC_ERROR_NONE); + grpc_endpoint_destroy(exec_ctx, args->endpoint); + grpc_channel_args_destroy(exec_ctx, args->args); + grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer); + gpr_free(args->read_buffer); + } + } else { + // If the handshaking succeeded but there is no endpoint, then the + // handshaker may have handed off the connection to some external + // code, so we can just clean up here without creating a transport. + if (args->endpoint != NULL) { + grpc_transport *transport = + grpc_create_chttp2_transport(exec_ctx, args->args, args->endpoint, 0); + grpc_server_setup_transport( + exec_ctx, connection_state->svr_state->server, transport, + connection_state->accepting_pollset, args->args); + grpc_chttp2_transport_start_reading(exec_ctx, transport, + args->read_buffer); + grpc_channel_args_destroy(exec_ctx, args->args); + } + } + grpc_handshake_manager_pending_list_remove( + &connection_state->svr_state->pending_handshake_mgrs, + connection_state->handshake_mgr); + gpr_mu_unlock(&connection_state->svr_state->mu); + grpc_handshake_manager_destroy(exec_ctx, connection_state->handshake_mgr); + grpc_tcp_server_unref(exec_ctx, connection_state->svr_state->tcp_server); + gpr_free(connection_state->acceptor); + gpr_free(connection_state); +} + +static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *tcp, + grpc_pollset *accepting_pollset, + grpc_tcp_server_acceptor *acceptor) { + server_state *state = (server_state *)arg; + gpr_mu_lock(&state->mu); + if (state->shutdown) { + gpr_mu_unlock(&state->mu); + grpc_endpoint_shutdown(exec_ctx, tcp, GRPC_ERROR_NONE); + grpc_endpoint_destroy(exec_ctx, tcp); + gpr_free(acceptor); + return; + } + grpc_handshake_manager *handshake_mgr = grpc_handshake_manager_create(); + grpc_handshake_manager_pending_list_add(&state->pending_handshake_mgrs, + handshake_mgr); + gpr_mu_unlock(&state->mu); + grpc_tcp_server_ref(state->tcp_server); + server_connection_state *connection_state = + (server_connection_state *)gpr_malloc(sizeof(*connection_state)); + connection_state->svr_state = state; + connection_state->accepting_pollset = accepting_pollset; + connection_state->acceptor = acceptor; + connection_state->handshake_mgr = handshake_mgr; + grpc_handshakers_add(exec_ctx, HANDSHAKER_SERVER, state->args, + connection_state->handshake_mgr); + // TODO(roth): We should really get this timeout value from channel + // args instead of hard-coding it. + const gpr_timespec deadline = gpr_time_add( + gpr_now(GPR_CLOCK_MONOTONIC), gpr_time_from_seconds(120, GPR_TIMESPAN)); + grpc_handshake_manager_do_handshake(exec_ctx, connection_state->handshake_mgr, + tcp, state->args, deadline, acceptor, + on_handshake_done, connection_state); +} + +/* Server callback: start listening on our ports */ +static void server_start_listener(grpc_exec_ctx *exec_ctx, grpc_server *server, + void *arg, grpc_pollset **pollsets, + size_t pollset_count) { + server_state *state = (server_state *)arg; + gpr_mu_lock(&state->mu); + state->shutdown = false; + gpr_mu_unlock(&state->mu); + grpc_tcp_server_start(exec_ctx, state->tcp_server, pollsets, pollset_count, + on_accept, state); +} + +static void tcp_server_shutdown_complete(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + server_state *state = (server_state *)arg; + /* ensure all threads have unlocked */ + gpr_mu_lock(&state->mu); + grpc_closure *destroy_done = state->server_destroy_listener_done; + GPR_ASSERT(state->shutdown); + grpc_handshake_manager_pending_list_shutdown_all( + exec_ctx, state->pending_handshake_mgrs, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&state->mu); + // Flush queued work before destroying handshaker factory, since that + // may do a synchronous unref. + grpc_exec_ctx_flush(exec_ctx); + if (destroy_done != NULL) { + destroy_done->cb(exec_ctx, destroy_done->cb_arg, GRPC_ERROR_REF(error)); + grpc_exec_ctx_flush(exec_ctx); + } + grpc_channel_args_destroy(exec_ctx, state->args); + gpr_mu_destroy(&state->mu); + gpr_free(state); +} + +/* Server callback: destroy the tcp listener (so we don't generate further + callbacks) */ +static void server_destroy_listener(grpc_exec_ctx *exec_ctx, + grpc_server *server, void *arg, + grpc_closure *destroy_done) { + server_state *state = (server_state *)arg; + gpr_mu_lock(&state->mu); + state->shutdown = true; + state->server_destroy_listener_done = destroy_done; + grpc_tcp_server *tcp_server = state->tcp_server; + gpr_mu_unlock(&state->mu); + grpc_tcp_server_shutdown_listeners(exec_ctx, tcp_server); + grpc_tcp_server_unref(exec_ctx, tcp_server); +} + +grpc_error *grpc_chttp2_server_add_port(grpc_exec_ctx *exec_ctx, + grpc_server *server, const char *addr, + grpc_channel_args *args, + int *port_num) { + grpc_resolved_addresses *resolved = NULL; + grpc_tcp_server *tcp_server = NULL; + size_t i; + size_t count = 0; + int port_temp; + grpc_error *err = GRPC_ERROR_NONE; + server_state *state = NULL; + grpc_error **errors = NULL; + size_t naddrs = 0; + + *port_num = -1; + + /* resolve address */ + err = grpc_blocking_resolve_address(addr, "https", &resolved); + if (err != GRPC_ERROR_NONE) { + goto error; + } + state = (server_state *)gpr_zalloc(sizeof(*state)); + GRPC_CLOSURE_INIT(&state->tcp_server_shutdown_complete, + tcp_server_shutdown_complete, state, + grpc_schedule_on_exec_ctx); + err = grpc_tcp_server_create(exec_ctx, &state->tcp_server_shutdown_complete, + args, &tcp_server); + if (err != GRPC_ERROR_NONE) { + goto error; + } + + state->server = server; + state->tcp_server = tcp_server; + state->args = args; + state->shutdown = true; + gpr_mu_init(&state->mu); + + naddrs = resolved->naddrs; + errors = (grpc_error **)gpr_malloc(sizeof(*errors) * naddrs); + for (i = 0; i < naddrs; i++) { + errors[i] = + grpc_tcp_server_add_port(tcp_server, &resolved->addrs[i], &port_temp); + if (errors[i] == GRPC_ERROR_NONE) { + if (*port_num == -1) { + *port_num = port_temp; + } else { + GPR_ASSERT(*port_num == port_temp); + } + count++; + } + } + if (count == 0) { + char *msg; + gpr_asprintf(&msg, "No address added out of total %" PRIuPTR " resolved", + naddrs); + err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs); + gpr_free(msg); + goto error; + } else if (count != naddrs) { + char *msg; + gpr_asprintf(&msg, "Only %" PRIuPTR + " addresses added out of total %" PRIuPTR " resolved", + count, naddrs); + err = GRPC_ERROR_CREATE_REFERENCING_FROM_COPIED_STRING(msg, errors, naddrs); + gpr_free(msg); + + const char *warning_message = grpc_error_string(err); + gpr_log(GPR_INFO, "WARNING: %s", warning_message); + + /* we managed to bind some addresses: continue */ + } + grpc_resolved_addresses_destroy(resolved); + + /* Register with the server only upon success */ + grpc_server_add_listener(exec_ctx, server, state, server_start_listener, + server_destroy_listener); + goto done; + +/* Error path: cleanup and return */ +error: + GPR_ASSERT(err != GRPC_ERROR_NONE); + if (resolved) { + grpc_resolved_addresses_destroy(resolved); + } + if (tcp_server) { + grpc_tcp_server_unref(exec_ctx, tcp_server); + } else { + grpc_channel_args_destroy(exec_ctx, args); + gpr_free(state); + } + *port_num = 0; + +done: + if (errors != NULL) { + for (i = 0; i < naddrs; i++) { + GRPC_ERROR_UNREF(errors[i]); + } + gpr_free(errors); + } + return err; +} diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c deleted file mode 100644 index d42b2d123e..0000000000 --- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include "src/core/ext/transport/chttp2/server/chttp2_server.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/server.h" - -int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - int port_num = 0; - GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2, - (server, addr)); - grpc_error *err = grpc_chttp2_server_add_port( - &exec_ctx, server, addr, - grpc_channel_args_copy(grpc_server_get_channel_args(server)), &port_num); - if (err != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(err); - gpr_log(GPR_ERROR, "%s", msg); - - GRPC_ERROR_UNREF(err); - } - grpc_exec_ctx_finish(&exec_ctx); - return port_num; -} diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc new file mode 100644 index 0000000000..d42b2d123e --- /dev/null +++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc @@ -0,0 +1,44 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/ext/transport/chttp2/server/chttp2_server.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/server.h" + +int grpc_server_add_insecure_http2_port(grpc_server *server, const char *addr) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + int port_num = 0; + GRPC_API_TRACE("grpc_server_add_insecure_http2_port(server=%p, addr=%s)", 2, + (server, addr)); + grpc_error *err = grpc_chttp2_server_add_port( + &exec_ctx, server, addr, + grpc_channel_args_copy(grpc_server_get_channel_args(server)), &port_num); + if (err != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "%s", msg); + + GRPC_ERROR_UNREF(err); + } + grpc_exec_ctx_finish(&exec_ctx); + return port_num; +} diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c deleted file mode 100644 index e647067f73..0000000000 --- a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include - -#ifdef GPR_SUPPORT_CHANNELS_FROM_FD - -#include -#include - -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/endpoint.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/tcp_posix.h" -#include "src/core/lib/surface/completion_queue.h" -#include "src/core/lib/surface/server.h" - -void grpc_server_add_insecure_channel_from_fd(grpc_server *server, - void *reserved, int fd) { - GPR_ASSERT(reserved == NULL); - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - char *name; - gpr_asprintf(&name, "fd:%d", fd); - - grpc_endpoint *server_endpoint = - grpc_tcp_create(&exec_ctx, grpc_fd_create(fd, name), - grpc_server_get_channel_args(server), name); - - gpr_free(name); - - const grpc_channel_args *server_args = grpc_server_get_channel_args(server); - grpc_transport *transport = grpc_create_chttp2_transport( - &exec_ctx, server_args, server_endpoint, 0 /* is_client */); - - grpc_pollset **pollsets; - size_t num_pollsets = 0; - grpc_server_get_pollsets(server, &pollsets, &num_pollsets); - - for (size_t i = 0; i < num_pollsets; i++) { - grpc_endpoint_add_to_pollset(&exec_ctx, server_endpoint, pollsets[i]); - } - - grpc_server_setup_transport(&exec_ctx, server, transport, NULL, server_args); - grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL); - grpc_exec_ctx_finish(&exec_ctx); -} - -#else // !GPR_SUPPORT_CHANNELS_FROM_FD - -void grpc_server_add_insecure_channel_from_fd(grpc_server *server, - void *reserved, int fd) { - GPR_ASSERT(0); -} - -#endif // GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc new file mode 100644 index 0000000000..e647067f73 --- /dev/null +++ b/src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc @@ -0,0 +1,75 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#ifdef GPR_SUPPORT_CHANNELS_FROM_FD + +#include +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/server.h" + +void grpc_server_add_insecure_channel_from_fd(grpc_server *server, + void *reserved, int fd) { + GPR_ASSERT(reserved == NULL); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + char *name; + gpr_asprintf(&name, "fd:%d", fd); + + grpc_endpoint *server_endpoint = + grpc_tcp_create(&exec_ctx, grpc_fd_create(fd, name), + grpc_server_get_channel_args(server), name); + + gpr_free(name); + + const grpc_channel_args *server_args = grpc_server_get_channel_args(server); + grpc_transport *transport = grpc_create_chttp2_transport( + &exec_ctx, server_args, server_endpoint, 0 /* is_client */); + + grpc_pollset **pollsets; + size_t num_pollsets = 0; + grpc_server_get_pollsets(server, &pollsets, &num_pollsets); + + for (size_t i = 0; i < num_pollsets; i++) { + grpc_endpoint_add_to_pollset(&exec_ctx, server_endpoint, pollsets[i]); + } + + grpc_server_setup_transport(&exec_ctx, server, transport, NULL, server_args); + grpc_chttp2_transport_start_reading(&exec_ctx, transport, NULL); + grpc_exec_ctx_finish(&exec_ctx); +} + +#else // !GPR_SUPPORT_CHANNELS_FROM_FD + +void grpc_server_add_insecure_channel_from_fd(grpc_server *server, + void *reserved, int fd) { + GPR_ASSERT(0); +} + +#endif // GPR_SUPPORT_CHANNELS_FROM_FD diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c deleted file mode 100644 index e74a138d23..0000000000 --- a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include -#include -#include - -#include "src/core/ext/transport/chttp2/server/chttp2_server.h" - -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker.h" -#include "src/core/lib/security/context/security_context.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/server.h" - -int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, - grpc_server_credentials *creds) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_error *err = GRPC_ERROR_NONE; - grpc_server_security_connector *sc = NULL; - int port_num = 0; - grpc_security_status status; - grpc_channel_args *args = NULL; - GRPC_API_TRACE( - "grpc_server_add_secure_http2_port(" - "server=%p, addr=%s, creds=%p)", - 3, (server, addr, creds)); - // Create security context. - if (creds == NULL) { - err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "No credentials specified for secure server port (creds==NULL)"); - goto done; - } - status = - grpc_server_credentials_create_security_connector(&exec_ctx, creds, &sc); - if (status != GRPC_SECURITY_OK) { - char *msg; - gpr_asprintf(&msg, - "Unable to create secure server with credentials of type %s.", - creds->type); - err = grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg), - GRPC_ERROR_INT_SECURITY_STATUS, status); - gpr_free(msg); - goto done; - } - // Create channel args. - grpc_arg args_to_add[2]; - args_to_add[0] = grpc_server_credentials_to_arg(creds); - args_to_add[1] = grpc_security_connector_to_arg(&sc->base); - args = - grpc_channel_args_copy_and_add(grpc_server_get_channel_args(server), - args_to_add, GPR_ARRAY_SIZE(args_to_add)); - // Add server port. - err = grpc_chttp2_server_add_port(&exec_ctx, server, addr, args, &port_num); -done: - if (sc != NULL) { - GRPC_SECURITY_CONNECTOR_UNREF(&exec_ctx, &sc->base, "server"); - } - grpc_exec_ctx_finish(&exec_ctx); - if (err != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(err); - gpr_log(GPR_ERROR, "%s", msg); - - GRPC_ERROR_UNREF(err); - } - return port_num; -} diff --git a/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc new file mode 100644 index 0000000000..e74a138d23 --- /dev/null +++ b/src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/server/chttp2_server.h" + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/server.h" + +int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_error *err = GRPC_ERROR_NONE; + grpc_server_security_connector *sc = NULL; + int port_num = 0; + grpc_security_status status; + grpc_channel_args *args = NULL; + GRPC_API_TRACE( + "grpc_server_add_secure_http2_port(" + "server=%p, addr=%s, creds=%p)", + 3, (server, addr, creds)); + // Create security context. + if (creds == NULL) { + err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No credentials specified for secure server port (creds==NULL)"); + goto done; + } + status = + grpc_server_credentials_create_security_connector(&exec_ctx, creds, &sc); + if (status != GRPC_SECURITY_OK) { + char *msg; + gpr_asprintf(&msg, + "Unable to create secure server with credentials of type %s.", + creds->type); + err = grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg), + GRPC_ERROR_INT_SECURITY_STATUS, status); + gpr_free(msg); + goto done; + } + // Create channel args. + grpc_arg args_to_add[2]; + args_to_add[0] = grpc_server_credentials_to_arg(creds); + args_to_add[1] = grpc_security_connector_to_arg(&sc->base); + args = + grpc_channel_args_copy_and_add(grpc_server_get_channel_args(server), + args_to_add, GPR_ARRAY_SIZE(args_to_add)); + // Add server port. + err = grpc_chttp2_server_add_port(&exec_ctx, server, addr, args, &port_num); +done: + if (sc != NULL) { + GRPC_SECURITY_CONNECTOR_UNREF(&exec_ctx, &sc->base, "server"); + } + grpc_exec_ctx_finish(&exec_ctx); + if (err != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "%s", msg); + + GRPC_ERROR_UNREF(err); + } + return port_num; +} diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.c b/src/core/ext/transport/chttp2/transport/bin_decoder.c deleted file mode 100644 index 5a99cbeffc..0000000000 --- a/src/core/ext/transport/chttp2/transport/bin_decoder.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/bin_decoder.h" -#include -#include -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -static uint8_t decode_table[] = { - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 62, 0x40, 0x40, 0x40, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 23, 24, 25, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, - 0x40, 0x40, 0x40, 0x40}; - -static const uint8_t tail_xtra[4] = {0, 0, 1, 2}; - -static bool input_is_valid(uint8_t *input_ptr, size_t length) { - size_t i; - - for (i = 0; i < length; ++i) { - if ((decode_table[input_ptr[i]] & 0xC0) != 0) { - gpr_log(GPR_ERROR, - "Base64 decoding failed, invalid character '%c' in base64 " - "input.\n", - (char)(*input_ptr)); - return false; - } - } - return true; -} - -#define COMPOSE_OUTPUT_BYTE_0(input_ptr) \ - (uint8_t)((decode_table[input_ptr[0]] << 2) | \ - (decode_table[input_ptr[1]] >> 4)) - -#define COMPOSE_OUTPUT_BYTE_1(input_ptr) \ - (uint8_t)((decode_table[input_ptr[1]] << 4) | \ - (decode_table[input_ptr[2]] >> 2)) - -#define COMPOSE_OUTPUT_BYTE_2(input_ptr) \ - (uint8_t)((decode_table[input_ptr[2]] << 6) | decode_table[input_ptr[3]]) - -bool grpc_base64_decode_partial(struct grpc_base64_decode_context *ctx) { - size_t input_tail; - - if (ctx->input_cur > ctx->input_end || ctx->output_cur > ctx->output_end) { - return false; - } - - // Process a block of 4 input characters and 3 output bytes - while (ctx->input_end >= ctx->input_cur + 4 && - ctx->output_end >= ctx->output_cur + 3) { - if (!input_is_valid(ctx->input_cur, 4)) return false; - ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); - ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); - ctx->output_cur[2] = COMPOSE_OUTPUT_BYTE_2(ctx->input_cur); - ctx->output_cur += 3; - ctx->input_cur += 4; - } - - // Process the tail of input data - input_tail = (size_t)(ctx->input_end - ctx->input_cur); - if (input_tail == 4) { - // Process the input data with pad chars - if (ctx->input_cur[3] == '=') { - if (ctx->input_cur[2] == '=' && ctx->output_end >= ctx->output_cur + 1) { - if (!input_is_valid(ctx->input_cur, 2)) return false; - *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); - ctx->input_cur += 4; - } else if (ctx->output_end >= ctx->output_cur + 2) { - if (!input_is_valid(ctx->input_cur, 3)) return false; - *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); - *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); - ; - ctx->input_cur += 4; - } - } - - } else if (ctx->contains_tail && input_tail > 1) { - // Process the input data without pad chars, but constains_tail is set - if (ctx->output_end >= ctx->output_cur + tail_xtra[input_tail]) { - if (!input_is_valid(ctx->input_cur, input_tail)) return false; - switch (input_tail) { - case 3: - ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); - /* fallthrough */ - case 2: - ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); - } - ctx->output_cur += tail_xtra[input_tail]; - ctx->input_cur += input_tail; - } - } - - return true; -} - -grpc_slice grpc_chttp2_base64_decode(grpc_exec_ctx *exec_ctx, - grpc_slice input) { - size_t input_length = GRPC_SLICE_LENGTH(input); - size_t output_length = input_length / 4 * 3; - struct grpc_base64_decode_context ctx; - grpc_slice output; - - if (input_length % 4 != 0) { - gpr_log(GPR_ERROR, - "Base64 decoding failed, input of " - "grpc_chttp2_base64_decode has a length of %d, which is not a " - "multiple of 4.\n", - (int)input_length); - return grpc_empty_slice(); - } - - if (input_length > 0) { - uint8_t *input_end = GRPC_SLICE_END_PTR(input); - if (*(--input_end) == '=') { - output_length--; - if (*(--input_end) == '=') { - output_length--; - } - } - } - output = GRPC_SLICE_MALLOC(output_length); - - ctx.input_cur = GRPC_SLICE_START_PTR(input); - ctx.input_end = GRPC_SLICE_END_PTR(input); - ctx.output_cur = GRPC_SLICE_START_PTR(output); - ctx.output_end = GRPC_SLICE_END_PTR(output); - ctx.contains_tail = false; - - if (!grpc_base64_decode_partial(&ctx)) { - char *s = grpc_slice_to_c_string(input); - gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s); - gpr_free(s); - grpc_slice_unref_internal(exec_ctx, output); - return grpc_empty_slice(); - } - GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output)); - GPR_ASSERT(ctx.input_cur == GRPC_SLICE_END_PTR(input)); - return output; -} - -grpc_slice grpc_chttp2_base64_decode_with_length(grpc_exec_ctx *exec_ctx, - grpc_slice input, - size_t output_length) { - size_t input_length = GRPC_SLICE_LENGTH(input); - grpc_slice output = GRPC_SLICE_MALLOC(output_length); - struct grpc_base64_decode_context ctx; - - // The length of a base64 string cannot be 4 * n + 1 - if (input_length % 4 == 1) { - gpr_log(GPR_ERROR, - "Base64 decoding failed, input of " - "grpc_chttp2_base64_decode_with_length has a length of %d, which " - "has a tail of 1 byte.\n", - (int)input_length); - grpc_slice_unref_internal(exec_ctx, output); - return grpc_empty_slice(); - } - - if (output_length > input_length / 4 * 3 + tail_xtra[input_length % 4]) { - gpr_log(GPR_ERROR, - "Base64 decoding failed, output_length %d is longer " - "than the max possible output length %d.\n", - (int)output_length, - (int)(input_length / 4 * 3 + tail_xtra[input_length % 4])); - grpc_slice_unref_internal(exec_ctx, output); - return grpc_empty_slice(); - } - - ctx.input_cur = GRPC_SLICE_START_PTR(input); - ctx.input_end = GRPC_SLICE_END_PTR(input); - ctx.output_cur = GRPC_SLICE_START_PTR(output); - ctx.output_end = GRPC_SLICE_END_PTR(output); - ctx.contains_tail = true; - - if (!grpc_base64_decode_partial(&ctx)) { - char *s = grpc_slice_to_c_string(input); - gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s); - gpr_free(s); - grpc_slice_unref_internal(exec_ctx, output); - return grpc_empty_slice(); - } - GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output)); - GPR_ASSERT(ctx.input_cur <= GRPC_SLICE_END_PTR(input)); - return output; -} diff --git a/src/core/ext/transport/chttp2/transport/bin_decoder.cc b/src/core/ext/transport/chttp2/transport/bin_decoder.cc new file mode 100644 index 0000000000..5a99cbeffc --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/bin_decoder.cc @@ -0,0 +1,222 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/bin_decoder.h" +#include +#include +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +static uint8_t decode_table[] = { + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 62, 0x40, 0x40, 0x40, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x40, 0x40}; + +static const uint8_t tail_xtra[4] = {0, 0, 1, 2}; + +static bool input_is_valid(uint8_t *input_ptr, size_t length) { + size_t i; + + for (i = 0; i < length; ++i) { + if ((decode_table[input_ptr[i]] & 0xC0) != 0) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, invalid character '%c' in base64 " + "input.\n", + (char)(*input_ptr)); + return false; + } + } + return true; +} + +#define COMPOSE_OUTPUT_BYTE_0(input_ptr) \ + (uint8_t)((decode_table[input_ptr[0]] << 2) | \ + (decode_table[input_ptr[1]] >> 4)) + +#define COMPOSE_OUTPUT_BYTE_1(input_ptr) \ + (uint8_t)((decode_table[input_ptr[1]] << 4) | \ + (decode_table[input_ptr[2]] >> 2)) + +#define COMPOSE_OUTPUT_BYTE_2(input_ptr) \ + (uint8_t)((decode_table[input_ptr[2]] << 6) | decode_table[input_ptr[3]]) + +bool grpc_base64_decode_partial(struct grpc_base64_decode_context *ctx) { + size_t input_tail; + + if (ctx->input_cur > ctx->input_end || ctx->output_cur > ctx->output_end) { + return false; + } + + // Process a block of 4 input characters and 3 output bytes + while (ctx->input_end >= ctx->input_cur + 4 && + ctx->output_end >= ctx->output_cur + 3) { + if (!input_is_valid(ctx->input_cur, 4)) return false; + ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); + ctx->output_cur[2] = COMPOSE_OUTPUT_BYTE_2(ctx->input_cur); + ctx->output_cur += 3; + ctx->input_cur += 4; + } + + // Process the tail of input data + input_tail = (size_t)(ctx->input_end - ctx->input_cur); + if (input_tail == 4) { + // Process the input data with pad chars + if (ctx->input_cur[3] == '=') { + if (ctx->input_cur[2] == '=' && ctx->output_end >= ctx->output_cur + 1) { + if (!input_is_valid(ctx->input_cur, 2)) return false; + *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + ctx->input_cur += 4; + } else if (ctx->output_end >= ctx->output_cur + 2) { + if (!input_is_valid(ctx->input_cur, 3)) return false; + *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + *(ctx->output_cur++) = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); + ; + ctx->input_cur += 4; + } + } + + } else if (ctx->contains_tail && input_tail > 1) { + // Process the input data without pad chars, but constains_tail is set + if (ctx->output_end >= ctx->output_cur + tail_xtra[input_tail]) { + if (!input_is_valid(ctx->input_cur, input_tail)) return false; + switch (input_tail) { + case 3: + ctx->output_cur[1] = COMPOSE_OUTPUT_BYTE_1(ctx->input_cur); + /* fallthrough */ + case 2: + ctx->output_cur[0] = COMPOSE_OUTPUT_BYTE_0(ctx->input_cur); + } + ctx->output_cur += tail_xtra[input_tail]; + ctx->input_cur += input_tail; + } + } + + return true; +} + +grpc_slice grpc_chttp2_base64_decode(grpc_exec_ctx *exec_ctx, + grpc_slice input) { + size_t input_length = GRPC_SLICE_LENGTH(input); + size_t output_length = input_length / 4 * 3; + struct grpc_base64_decode_context ctx; + grpc_slice output; + + if (input_length % 4 != 0) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, input of " + "grpc_chttp2_base64_decode has a length of %d, which is not a " + "multiple of 4.\n", + (int)input_length); + return grpc_empty_slice(); + } + + if (input_length > 0) { + uint8_t *input_end = GRPC_SLICE_END_PTR(input); + if (*(--input_end) == '=') { + output_length--; + if (*(--input_end) == '=') { + output_length--; + } + } + } + output = GRPC_SLICE_MALLOC(output_length); + + ctx.input_cur = GRPC_SLICE_START_PTR(input); + ctx.input_end = GRPC_SLICE_END_PTR(input); + ctx.output_cur = GRPC_SLICE_START_PTR(output); + ctx.output_end = GRPC_SLICE_END_PTR(output); + ctx.contains_tail = false; + + if (!grpc_base64_decode_partial(&ctx)) { + char *s = grpc_slice_to_c_string(input); + gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s); + gpr_free(s); + grpc_slice_unref_internal(exec_ctx, output); + return grpc_empty_slice(); + } + GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output)); + GPR_ASSERT(ctx.input_cur == GRPC_SLICE_END_PTR(input)); + return output; +} + +grpc_slice grpc_chttp2_base64_decode_with_length(grpc_exec_ctx *exec_ctx, + grpc_slice input, + size_t output_length) { + size_t input_length = GRPC_SLICE_LENGTH(input); + grpc_slice output = GRPC_SLICE_MALLOC(output_length); + struct grpc_base64_decode_context ctx; + + // The length of a base64 string cannot be 4 * n + 1 + if (input_length % 4 == 1) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, input of " + "grpc_chttp2_base64_decode_with_length has a length of %d, which " + "has a tail of 1 byte.\n", + (int)input_length); + grpc_slice_unref_internal(exec_ctx, output); + return grpc_empty_slice(); + } + + if (output_length > input_length / 4 * 3 + tail_xtra[input_length % 4]) { + gpr_log(GPR_ERROR, + "Base64 decoding failed, output_length %d is longer " + "than the max possible output length %d.\n", + (int)output_length, + (int)(input_length / 4 * 3 + tail_xtra[input_length % 4])); + grpc_slice_unref_internal(exec_ctx, output); + return grpc_empty_slice(); + } + + ctx.input_cur = GRPC_SLICE_START_PTR(input); + ctx.input_end = GRPC_SLICE_END_PTR(input); + ctx.output_cur = GRPC_SLICE_START_PTR(output); + ctx.output_end = GRPC_SLICE_END_PTR(output); + ctx.contains_tail = true; + + if (!grpc_base64_decode_partial(&ctx)) { + char *s = grpc_slice_to_c_string(input); + gpr_log(GPR_ERROR, "Base64 decoding failed, input string:\n%s\n", s); + gpr_free(s); + grpc_slice_unref_internal(exec_ctx, output); + return grpc_empty_slice(); + } + GPR_ASSERT(ctx.output_cur == GRPC_SLICE_END_PTR(output)); + GPR_ASSERT(ctx.input_cur <= GRPC_SLICE_END_PTR(input)); + return output; +} diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.c b/src/core/ext/transport/chttp2/transport/bin_encoder.c deleted file mode 100644 index 42d481b3c0..0000000000 --- a/src/core/ext/transport/chttp2/transport/bin_encoder.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" - -#include - -#include -#include "src/core/ext/transport/chttp2/transport/huffsyms.h" - -static const char alphabet[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - -typedef struct { - uint16_t bits; - uint8_t length; -} b64_huff_sym; - -static const b64_huff_sym huff_alphabet[64] = { - {0x21, 6}, {0x5d, 7}, {0x5e, 7}, {0x5f, 7}, {0x60, 7}, {0x61, 7}, - {0x62, 7}, {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, {0x67, 7}, - {0x68, 7}, {0x69, 7}, {0x6a, 7}, {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, - {0x6e, 7}, {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, {0xfc, 8}, - {0x73, 7}, {0xfd, 8}, {0x3, 5}, {0x23, 6}, {0x4, 5}, {0x24, 6}, - {0x5, 5}, {0x25, 6}, {0x26, 6}, {0x27, 6}, {0x6, 5}, {0x74, 7}, - {0x75, 7}, {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, {0x2b, 6}, - {0x76, 7}, {0x2c, 6}, {0x8, 5}, {0x9, 5}, {0x2d, 6}, {0x77, 7}, - {0x78, 7}, {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x0, 5}, {0x1, 5}, - {0x2, 5}, {0x19, 6}, {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, - {0x1e, 6}, {0x1f, 6}, {0x7fb, 11}, {0x18, 6}}; - -static const uint8_t tail_xtra[3] = {0, 2, 3}; - -grpc_slice grpc_chttp2_base64_encode(grpc_slice input) { - size_t input_length = GRPC_SLICE_LENGTH(input); - size_t input_triplets = input_length / 3; - size_t tail_case = input_length % 3; - size_t output_length = input_triplets * 4 + tail_xtra[tail_case]; - grpc_slice output = GRPC_SLICE_MALLOC(output_length); - uint8_t *in = GRPC_SLICE_START_PTR(input); - char *out = (char *)GRPC_SLICE_START_PTR(output); - size_t i; - - /* encode full triplets */ - for (i = 0; i < input_triplets; i++) { - out[0] = alphabet[in[0] >> 2]; - out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; - out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)]; - out[3] = alphabet[in[2] & 0x3f]; - out += 4; - in += 3; - } - - /* encode the remaining bytes */ - switch (tail_case) { - case 0: - break; - case 1: - out[0] = alphabet[in[0] >> 2]; - out[1] = alphabet[(in[0] & 0x3) << 4]; - out += 2; - in += 1; - break; - case 2: - out[0] = alphabet[in[0] >> 2]; - out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; - out[2] = alphabet[(in[1] & 0xf) << 2]; - out += 3; - in += 2; - break; - } - - GPR_ASSERT(out == (char *)GRPC_SLICE_END_PTR(output)); - GPR_ASSERT(in == GRPC_SLICE_END_PTR(input)); - return output; -} - -grpc_slice grpc_chttp2_huffman_compress(grpc_slice input) { - size_t nbits; - uint8_t *in; - uint8_t *out; - grpc_slice output; - uint32_t temp = 0; - uint32_t temp_length = 0; - - nbits = 0; - for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input); - ++in) { - nbits += grpc_chttp2_huffsyms[*in].length; - } - - output = GRPC_SLICE_MALLOC(nbits / 8 + (nbits % 8 != 0)); - out = GRPC_SLICE_START_PTR(output); - for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input); - ++in) { - int sym = *in; - temp <<= grpc_chttp2_huffsyms[sym].length; - temp |= grpc_chttp2_huffsyms[sym].bits; - temp_length += grpc_chttp2_huffsyms[sym].length; - - while (temp_length > 8) { - temp_length -= 8; - *out++ = (uint8_t)(temp >> temp_length); - } - } - - if (temp_length) { - /* NB: the following integer arithmetic operation needs to be in its - * expanded form due to the "integral promotion" performed (see section - * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type - * is then required to avoid the compiler warning */ - *out++ = (uint8_t)((uint8_t)(temp << (8u - temp_length)) | - (uint8_t)(0xffu >> temp_length)); - } - - GPR_ASSERT(out == GRPC_SLICE_END_PTR(output)); - - return output; -} - -typedef struct { - uint32_t temp; - uint32_t temp_length; - uint8_t *out; -} huff_out; - -static void enc_flush_some(huff_out *out) { - while (out->temp_length > 8) { - out->temp_length -= 8; - *out->out++ = (uint8_t)(out->temp >> out->temp_length); - } -} - -static void enc_add2(huff_out *out, uint8_t a, uint8_t b) { - b64_huff_sym sa = huff_alphabet[a]; - b64_huff_sym sb = huff_alphabet[b]; - out->temp = (out->temp << (sa.length + sb.length)) | - ((uint32_t)sa.bits << sb.length) | sb.bits; - out->temp_length += (uint32_t)sa.length + (uint32_t)sb.length; - enc_flush_some(out); -} - -static void enc_add1(huff_out *out, uint8_t a) { - b64_huff_sym sa = huff_alphabet[a]; - out->temp = (out->temp << sa.length) | sa.bits; - out->temp_length += sa.length; - enc_flush_some(out); -} - -grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input) { - size_t input_length = GRPC_SLICE_LENGTH(input); - size_t input_triplets = input_length / 3; - size_t tail_case = input_length % 3; - size_t output_syms = input_triplets * 4 + tail_xtra[tail_case]; - size_t max_output_bits = 11 * output_syms; - size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0); - grpc_slice output = GRPC_SLICE_MALLOC(max_output_length); - uint8_t *in = GRPC_SLICE_START_PTR(input); - uint8_t *start_out = GRPC_SLICE_START_PTR(output); - huff_out out; - size_t i; - - out.temp = 0; - out.temp_length = 0; - out.out = start_out; - - /* encode full triplets */ - for (i = 0; i < input_triplets; i++) { - const uint8_t low_to_high = (uint8_t)((in[0] & 0x3) << 4); - const uint8_t high_to_low = in[1] >> 4; - enc_add2(&out, in[0] >> 2, low_to_high | high_to_low); - - const uint8_t a = (uint8_t)((in[1] & 0xf) << 2); - const uint8_t b = (in[2] >> 6); - enc_add2(&out, a | b, in[2] & 0x3f); - in += 3; - } - - /* encode the remaining bytes */ - switch (tail_case) { - case 0: - break; - case 1: - enc_add2(&out, in[0] >> 2, (uint8_t)((in[0] & 0x3) << 4)); - in += 1; - break; - case 2: { - const uint8_t low_to_high = (uint8_t)((in[0] & 0x3) << 4); - const uint8_t high_to_low = in[1] >> 4; - enc_add2(&out, in[0] >> 2, low_to_high | high_to_low); - enc_add1(&out, (uint8_t)((in[1] & 0xf) << 2)); - in += 2; - break; - } - } - - if (out.temp_length) { - /* NB: the following integer arithmetic operation needs to be in its - * expanded form due to the "integral promotion" performed (see section - * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type - * is then required to avoid the compiler warning */ - *out.out++ = (uint8_t)((uint8_t)(out.temp << (8u - out.temp_length)) | - (uint8_t)(0xffu >> out.temp_length)); - } - - GPR_ASSERT(out.out <= GRPC_SLICE_END_PTR(output)); - GRPC_SLICE_SET_LENGTH(output, out.out - start_out); - - GPR_ASSERT(in == GRPC_SLICE_END_PTR(input)); - return output; -} diff --git a/src/core/ext/transport/chttp2/transport/bin_encoder.cc b/src/core/ext/transport/chttp2/transport/bin_encoder.cc new file mode 100644 index 0000000000..42d481b3c0 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/bin_encoder.cc @@ -0,0 +1,226 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" + +#include + +#include +#include "src/core/ext/transport/chttp2/transport/huffsyms.h" + +static const char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +typedef struct { + uint16_t bits; + uint8_t length; +} b64_huff_sym; + +static const b64_huff_sym huff_alphabet[64] = { + {0x21, 6}, {0x5d, 7}, {0x5e, 7}, {0x5f, 7}, {0x60, 7}, {0x61, 7}, + {0x62, 7}, {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, {0x67, 7}, + {0x68, 7}, {0x69, 7}, {0x6a, 7}, {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, + {0x6e, 7}, {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, {0xfc, 8}, + {0x73, 7}, {0xfd, 8}, {0x3, 5}, {0x23, 6}, {0x4, 5}, {0x24, 6}, + {0x5, 5}, {0x25, 6}, {0x26, 6}, {0x27, 6}, {0x6, 5}, {0x74, 7}, + {0x75, 7}, {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, {0x2b, 6}, + {0x76, 7}, {0x2c, 6}, {0x8, 5}, {0x9, 5}, {0x2d, 6}, {0x77, 7}, + {0x78, 7}, {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x0, 5}, {0x1, 5}, + {0x2, 5}, {0x19, 6}, {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, + {0x1e, 6}, {0x1f, 6}, {0x7fb, 11}, {0x18, 6}}; + +static const uint8_t tail_xtra[3] = {0, 2, 3}; + +grpc_slice grpc_chttp2_base64_encode(grpc_slice input) { + size_t input_length = GRPC_SLICE_LENGTH(input); + size_t input_triplets = input_length / 3; + size_t tail_case = input_length % 3; + size_t output_length = input_triplets * 4 + tail_xtra[tail_case]; + grpc_slice output = GRPC_SLICE_MALLOC(output_length); + uint8_t *in = GRPC_SLICE_START_PTR(input); + char *out = (char *)GRPC_SLICE_START_PTR(output); + size_t i; + + /* encode full triplets */ + for (i = 0; i < input_triplets; i++) { + out[0] = alphabet[in[0] >> 2]; + out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; + out[2] = alphabet[((in[1] & 0xf) << 2) | (in[2] >> 6)]; + out[3] = alphabet[in[2] & 0x3f]; + out += 4; + in += 3; + } + + /* encode the remaining bytes */ + switch (tail_case) { + case 0: + break; + case 1: + out[0] = alphabet[in[0] >> 2]; + out[1] = alphabet[(in[0] & 0x3) << 4]; + out += 2; + in += 1; + break; + case 2: + out[0] = alphabet[in[0] >> 2]; + out[1] = alphabet[((in[0] & 0x3) << 4) | (in[1] >> 4)]; + out[2] = alphabet[(in[1] & 0xf) << 2]; + out += 3; + in += 2; + break; + } + + GPR_ASSERT(out == (char *)GRPC_SLICE_END_PTR(output)); + GPR_ASSERT(in == GRPC_SLICE_END_PTR(input)); + return output; +} + +grpc_slice grpc_chttp2_huffman_compress(grpc_slice input) { + size_t nbits; + uint8_t *in; + uint8_t *out; + grpc_slice output; + uint32_t temp = 0; + uint32_t temp_length = 0; + + nbits = 0; + for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input); + ++in) { + nbits += grpc_chttp2_huffsyms[*in].length; + } + + output = GRPC_SLICE_MALLOC(nbits / 8 + (nbits % 8 != 0)); + out = GRPC_SLICE_START_PTR(output); + for (in = GRPC_SLICE_START_PTR(input); in != GRPC_SLICE_END_PTR(input); + ++in) { + int sym = *in; + temp <<= grpc_chttp2_huffsyms[sym].length; + temp |= grpc_chttp2_huffsyms[sym].bits; + temp_length += grpc_chttp2_huffsyms[sym].length; + + while (temp_length > 8) { + temp_length -= 8; + *out++ = (uint8_t)(temp >> temp_length); + } + } + + if (temp_length) { + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + *out++ = (uint8_t)((uint8_t)(temp << (8u - temp_length)) | + (uint8_t)(0xffu >> temp_length)); + } + + GPR_ASSERT(out == GRPC_SLICE_END_PTR(output)); + + return output; +} + +typedef struct { + uint32_t temp; + uint32_t temp_length; + uint8_t *out; +} huff_out; + +static void enc_flush_some(huff_out *out) { + while (out->temp_length > 8) { + out->temp_length -= 8; + *out->out++ = (uint8_t)(out->temp >> out->temp_length); + } +} + +static void enc_add2(huff_out *out, uint8_t a, uint8_t b) { + b64_huff_sym sa = huff_alphabet[a]; + b64_huff_sym sb = huff_alphabet[b]; + out->temp = (out->temp << (sa.length + sb.length)) | + ((uint32_t)sa.bits << sb.length) | sb.bits; + out->temp_length += (uint32_t)sa.length + (uint32_t)sb.length; + enc_flush_some(out); +} + +static void enc_add1(huff_out *out, uint8_t a) { + b64_huff_sym sa = huff_alphabet[a]; + out->temp = (out->temp << sa.length) | sa.bits; + out->temp_length += sa.length; + enc_flush_some(out); +} + +grpc_slice grpc_chttp2_base64_encode_and_huffman_compress(grpc_slice input) { + size_t input_length = GRPC_SLICE_LENGTH(input); + size_t input_triplets = input_length / 3; + size_t tail_case = input_length % 3; + size_t output_syms = input_triplets * 4 + tail_xtra[tail_case]; + size_t max_output_bits = 11 * output_syms; + size_t max_output_length = max_output_bits / 8 + (max_output_bits % 8 != 0); + grpc_slice output = GRPC_SLICE_MALLOC(max_output_length); + uint8_t *in = GRPC_SLICE_START_PTR(input); + uint8_t *start_out = GRPC_SLICE_START_PTR(output); + huff_out out; + size_t i; + + out.temp = 0; + out.temp_length = 0; + out.out = start_out; + + /* encode full triplets */ + for (i = 0; i < input_triplets; i++) { + const uint8_t low_to_high = (uint8_t)((in[0] & 0x3) << 4); + const uint8_t high_to_low = in[1] >> 4; + enc_add2(&out, in[0] >> 2, low_to_high | high_to_low); + + const uint8_t a = (uint8_t)((in[1] & 0xf) << 2); + const uint8_t b = (in[2] >> 6); + enc_add2(&out, a | b, in[2] & 0x3f); + in += 3; + } + + /* encode the remaining bytes */ + switch (tail_case) { + case 0: + break; + case 1: + enc_add2(&out, in[0] >> 2, (uint8_t)((in[0] & 0x3) << 4)); + in += 1; + break; + case 2: { + const uint8_t low_to_high = (uint8_t)((in[0] & 0x3) << 4); + const uint8_t high_to_low = in[1] >> 4; + enc_add2(&out, in[0] >> 2, low_to_high | high_to_low); + enc_add1(&out, (uint8_t)((in[1] & 0xf) << 2)); + in += 2; + break; + } + } + + if (out.temp_length) { + /* NB: the following integer arithmetic operation needs to be in its + * expanded form due to the "integral promotion" performed (see section + * 3.2.1.1 of the C89 draft standard). A cast to the smaller container type + * is then required to avoid the compiler warning */ + *out.out++ = (uint8_t)((uint8_t)(out.temp << (8u - out.temp_length)) | + (uint8_t)(0xffu >> out.temp_length)); + } + + GPR_ASSERT(out.out <= GRPC_SLICE_END_PTR(output)); + GRPC_SLICE_SET_LENGTH(output, out.out - start_out); + + GPR_ASSERT(in == GRPC_SLICE_END_PTR(input)); + return output; +} diff --git a/src/core/ext/transport/chttp2/transport/chttp2_plugin.c b/src/core/ext/transport/chttp2/transport/chttp2_plugin.c deleted file mode 100644 index 6d09953830..0000000000 --- a/src/core/ext/transport/chttp2/transport/chttp2_plugin.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/transport/metadata.h" - -void grpc_chttp2_plugin_init(void) { - grpc_register_tracer(&grpc_http_trace); - grpc_register_tracer(&grpc_flowctl_trace); - grpc_register_tracer(&grpc_trace_http2_stream_state); -#ifndef NDEBUG - grpc_register_tracer(&grpc_trace_chttp2_refcount); -#endif -} - -void grpc_chttp2_plugin_shutdown(void) {} diff --git a/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc new file mode 100644 index 0000000000..ac9ae5c395 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/chttp2_plugin.cc @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/transport/metadata.h" + +extern "C" void grpc_chttp2_plugin_init(void) { + grpc_register_tracer(&grpc_http_trace); + grpc_register_tracer(&grpc_flowctl_trace); + grpc_register_tracer(&grpc_trace_http2_stream_state); +#ifndef NDEBUG + grpc_register_tracer(&grpc_trace_chttp2_refcount); +#endif +} + +extern "C" void grpc_chttp2_plugin_shutdown(void) {} diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.c b/src/core/ext/transport/chttp2/transport/chttp2_transport.c deleted file mode 100644 index acf49632ff..0000000000 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.c +++ /dev/null @@ -1,3252 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/transport/chttp2/transport/frame_data.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" -#include "src/core/ext/transport/chttp2/transport/varint.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/compression/stream_compression.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/http/parser.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/error_utils.h" -#include "src/core/lib/transport/http2_errors.h" -#include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/status_conversion.h" -#include "src/core/lib/transport/timeout_encoding.h" -#include "src/core/lib/transport/transport.h" -#include "src/core/lib/transport/transport_impl.h" - -#define DEFAULT_WINDOW 65535 -#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024) -#define MAX_WINDOW 0x7fffffffu -#define MAX_WRITE_BUFFER_SIZE (64 * 1024 * 1024) -#define DEFAULT_MAX_HEADER_LIST_SIZE (8 * 1024) - -#define DEFAULT_CLIENT_KEEPALIVE_TIME_MS INT_MAX -#define DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */ -#define DEFAULT_SERVER_KEEPALIVE_TIME_MS 7200000 /* 2 hours */ -#define DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */ -#define DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS false -#define KEEPALIVE_TIME_BACKOFF_MULTIPLIER 2 - -#define DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */ -#define DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */ -#define DEFAULT_MAX_PINGS_BETWEEN_DATA 0 /* unlimited */ -#define DEFAULT_MAX_PING_STRIKES 2 - -static int g_default_client_keepalive_time_ms = - DEFAULT_CLIENT_KEEPALIVE_TIME_MS; -static int g_default_client_keepalive_timeout_ms = - DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS; -static int g_default_server_keepalive_time_ms = - DEFAULT_SERVER_KEEPALIVE_TIME_MS; -static int g_default_server_keepalive_timeout_ms = - DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS; -static bool g_default_keepalive_permit_without_calls = - DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS; - -static int g_default_min_sent_ping_interval_without_data_ms = - DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS; -static int g_default_min_recv_ping_interval_without_data_ms = - DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS; -static int g_default_max_pings_without_data = DEFAULT_MAX_PINGS_BETWEEN_DATA; -static int g_default_max_ping_strikes = DEFAULT_MAX_PING_STRIKES; - -#define MAX_CLIENT_STREAM_ID 0x7fffffffu -grpc_tracer_flag grpc_http_trace = GRPC_TRACER_INITIALIZER(false, "http"); -grpc_tracer_flag grpc_flowctl_trace = GRPC_TRACER_INITIALIZER(false, "flowctl"); - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_chttp2_refcount = - GRPC_TRACER_INITIALIZER(false, "chttp2_refcount"); -#endif - -/* forward declarations of various callbacks that we'll build closures around */ -static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *t, - grpc_error *error); -static void write_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error); -static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *t, - grpc_error *error); - -static void read_action_locked(grpc_exec_ctx *exec_ctx, void *t, - grpc_error *error); - -static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs, - grpc_error *error); -/** Set a transport level setting, and push it to our peer */ -static void queue_setting_update(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_setting_id id, uint32_t value); - -static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_error *error); - -/** Start new streams that have been created if we can */ -static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); - -static void connectivity_state_set(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_connectivity_state state, - grpc_error *error, const char *reason); - -static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, - void *byte_stream, - grpc_error *error_ignored); -static void incoming_byte_stream_publish_error( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, - grpc_error *error); -static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_chttp2_incoming_byte_stream *bs); - -static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t, - grpc_error *error); -static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t, - grpc_error *error); - -static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); - -static void close_transport_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, grpc_error *error); -static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_error *error); - -static void start_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error); -static void finish_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error); - -static void cancel_pings(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_error *error); -static void send_ping_locked( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_ping_type ping_type, grpc_closure *on_initiate, - grpc_closure *on_complete, - grpc_chttp2_initiate_write_reason initiate_write_reason); -static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error); - -/** keepalive-relevant functions */ -static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); -static void start_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); -static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); -static void keepalive_watchdog_fired_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); - -static void reset_byte_stream(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); - -/******************************************************************************* - * CONSTRUCTION/DESTRUCTION/REFCOUNTING - */ - -static void destruct_transport(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - size_t i; - - grpc_endpoint_destroy(exec_ctx, t->ep); - - grpc_slice_buffer_destroy_internal(exec_ctx, &t->qbuf); - - grpc_slice_buffer_destroy_internal(exec_ctx, &t->outbuf); - grpc_chttp2_hpack_compressor_destroy(exec_ctx, &t->hpack_compressor); - - grpc_slice_buffer_destroy_internal(exec_ctx, &t->read_buffer); - grpc_chttp2_hpack_parser_destroy(exec_ctx, &t->hpack_parser); - grpc_chttp2_goaway_parser_destroy(&t->goaway_parser); - - for (i = 0; i < STREAM_LIST_COUNT; i++) { - GPR_ASSERT(t->lists[i].head == NULL); - GPR_ASSERT(t->lists[i].tail == NULL); - } - - GPR_ASSERT(grpc_chttp2_stream_map_size(&t->stream_map) == 0); - - grpc_chttp2_stream_map_destroy(&t->stream_map); - grpc_connectivity_state_destroy(exec_ctx, &t->channel_callback.state_tracker); - - GRPC_COMBINER_UNREF(exec_ctx, t->combiner, "chttp2_transport"); - - cancel_pings(exec_ctx, t, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed")); - - while (t->write_cb_pool) { - grpc_chttp2_write_cb *next = t->write_cb_pool->next; - gpr_free(t->write_cb_pool); - t->write_cb_pool = next; - } - - gpr_free(t->ping_acks); - gpr_free(t->peer_string); - gpr_free(t); -} - -#ifndef NDEBUG -void grpc_chttp2_unref_transport(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count); - gpr_log(GPR_DEBUG, "chttp2:unref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]", - t, val, val - 1, reason, file, line); - } - if (!gpr_unref(&t->refs)) return; - destruct_transport(exec_ctx, t); -} - -void grpc_chttp2_ref_transport(grpc_chttp2_transport *t, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count); - gpr_log(GPR_DEBUG, "chttp2: ref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]", - t, val, val + 1, reason, file, line); - } - gpr_ref(&t->refs); -} -#else -void grpc_chttp2_unref_transport(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (!gpr_unref(&t->refs)) return; - destruct_transport(exec_ctx, t); -} - -void grpc_chttp2_ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } -#endif - -static const grpc_transport_vtable *get_vtable(void); - -static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - const grpc_channel_args *channel_args, - grpc_endpoint *ep, bool is_client) { - size_t i; - int j; - - GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == - GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); - - t->base.vtable = get_vtable(); - t->ep = ep; - /* one ref is for destroy */ - gpr_ref_init(&t->refs, 1); - t->combiner = grpc_combiner_create(); - t->peer_string = grpc_endpoint_get_peer(ep); - t->endpoint_reading = 1; - t->next_stream_id = is_client ? 1 : 2; - t->is_client = is_client; - t->flow_control.remote_window = DEFAULT_WINDOW; - t->flow_control.announced_window = DEFAULT_WINDOW; - t->flow_control.t = t; - t->deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0; - t->is_first_frame = true; - grpc_connectivity_state_init( - &t->channel_callback.state_tracker, GRPC_CHANNEL_READY, - is_client ? "client_transport" : "server_transport"); - - grpc_slice_buffer_init(&t->qbuf); - - grpc_slice_buffer_init(&t->outbuf); - grpc_chttp2_hpack_compressor_init(&t->hpack_compressor); - - GRPC_CLOSURE_INIT(&t->read_action_locked, read_action_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->benign_reclaimer_locked, benign_reclaimer_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->destructive_reclaimer_locked, - destructive_reclaimer_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->retry_initiate_ping_locked, retry_initiate_ping_locked, - t, grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->start_bdp_ping_locked, start_bdp_ping_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->finish_bdp_ping_locked, finish_bdp_ping_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->init_keepalive_ping_locked, init_keepalive_ping_locked, - t, grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->start_keepalive_ping_locked, - start_keepalive_ping_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->finish_keepalive_ping_locked, - finish_keepalive_ping_locked, t, - grpc_combiner_scheduler(t->combiner)); - GRPC_CLOSURE_INIT(&t->keepalive_watchdog_fired_locked, - keepalive_watchdog_fired_locked, t, - grpc_combiner_scheduler(t->combiner)); - - grpc_bdp_estimator_init(&t->flow_control.bdp_estimator, t->peer_string); - t->flow_control.last_pid_update = gpr_now(GPR_CLOCK_MONOTONIC); - grpc_pid_controller_init( - &t->flow_control.pid_controller, - (grpc_pid_controller_args){.gain_p = 4, - .gain_i = 8, - .gain_d = 0, - .initial_control_value = log2(DEFAULT_WINDOW), - .min_control_value = -1, - .max_control_value = 25, - .integral_range = 10}); - - grpc_chttp2_goaway_parser_init(&t->goaway_parser); - grpc_chttp2_hpack_parser_init(exec_ctx, &t->hpack_parser); - - grpc_slice_buffer_init(&t->read_buffer); - - /* 8 is a random stab in the dark as to a good initial size: it's small enough - that it shouldn't waste memory for infrequently used connections, yet - large enough that the exponential growth should happen nicely when it's - needed. - TODO(ctiller): tune this */ - grpc_chttp2_stream_map_init(&t->stream_map, 8); - - /* copy in initial settings to all setting sets */ - for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) { - for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) { - t->settings[j][i] = grpc_chttp2_settings_parameters[i].default_value; - } - } - t->dirtied_local_settings = 1; - /* Hack: it's common for implementations to assume 65536 bytes initial send - window -- this should by rights be 0 */ - t->force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - t->sent_local_settings = 0; - t->write_buffer_size = DEFAULT_WINDOW; - t->flow_control.enable_bdp_probe = true; - - if (is_client) { - grpc_slice_buffer_add(&t->outbuf, grpc_slice_from_copied_string( - GRPC_CHTTP2_CLIENT_CONNECT_STRING)); - } - - /* configure http2 the way we like it */ - if (is_client) { - queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0); - queue_setting_update(exec_ctx, t, - GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0); - } - queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, - DEFAULT_WINDOW); - queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, - DEFAULT_MAX_HEADER_LIST_SIZE); - queue_setting_update(exec_ctx, t, - GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1); - - t->ping_policy.max_pings_without_data = g_default_max_pings_without_data; - t->ping_policy.min_sent_ping_interval_without_data = gpr_time_from_millis( - g_default_min_sent_ping_interval_without_data_ms, GPR_TIMESPAN); - t->ping_policy.max_ping_strikes = g_default_max_ping_strikes; - t->ping_policy.min_recv_ping_interval_without_data = gpr_time_from_millis( - g_default_min_recv_ping_interval_without_data_ms, GPR_TIMESPAN); - - /* Keepalive setting */ - if (t->is_client) { - t->keepalive_time = - g_default_client_keepalive_time_ms == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(g_default_client_keepalive_time_ms, - GPR_TIMESPAN); - t->keepalive_timeout = - g_default_client_keepalive_timeout_ms == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(g_default_client_keepalive_timeout_ms, - GPR_TIMESPAN); - } else { - t->keepalive_time = - g_default_server_keepalive_time_ms == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(g_default_server_keepalive_time_ms, - GPR_TIMESPAN); - t->keepalive_timeout = - g_default_server_keepalive_timeout_ms == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(g_default_server_keepalive_timeout_ms, - GPR_TIMESPAN); - } - t->keepalive_permit_without_calls = g_default_keepalive_permit_without_calls; - - t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; - - if (channel_args) { - for (i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) { - const grpc_integer_options options = {-1, 0, INT_MAX}; - const int value = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - if (value >= 0) { - if ((t->next_stream_id & 1) != (value & 1)) { - gpr_log(GPR_ERROR, "%s: low bit must be %d on %s", - GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER, - t->next_stream_id & 1, is_client ? "client" : "server"); - } else { - t->next_stream_id = (uint32_t)value; - } - } - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) { - const grpc_integer_options options = {-1, 0, INT_MAX}; - const int value = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - if (value >= 0) { - grpc_chttp2_hpack_compressor_set_max_usable_size(&t->hpack_compressor, - (uint32_t)value); - } - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { - t->ping_policy.max_pings_without_data = grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){g_default_max_pings_without_data, 0, - INT_MAX}); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { - t->ping_policy.max_ping_strikes = grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){g_default_max_ping_strikes, 0, INT_MAX}); - } else if (0 == - strcmp( - channel_args->args[i].key, - GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) { - t->ping_policy.min_sent_ping_interval_without_data = - gpr_time_from_millis( - grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){ - g_default_min_sent_ping_interval_without_data_ms, 0, - INT_MAX}), - GPR_TIMESPAN); - } else if (0 == - strcmp( - channel_args->args[i].key, - GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) { - t->ping_policy.min_recv_ping_interval_without_data = - gpr_time_from_millis( - grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){ - g_default_min_recv_ping_interval_without_data_ms, 0, - INT_MAX}), - GPR_TIMESPAN); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE)) { - t->write_buffer_size = (uint32_t)grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){0, 0, MAX_WRITE_BUFFER_SIZE}); - } else if (0 == - strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_BDP_PROBE)) { - t->flow_control.enable_bdp_probe = grpc_channel_arg_get_integer( - &channel_args->args[i], (grpc_integer_options){1, 0, 1}); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_KEEPALIVE_TIME_MS)) { - const int value = grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){t->is_client - ? g_default_client_keepalive_time_ms - : g_default_server_keepalive_time_ms, - 1, INT_MAX}); - t->keepalive_time = value == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(value, GPR_TIMESPAN); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { - const int value = grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){t->is_client - ? g_default_client_keepalive_timeout_ms - : g_default_server_keepalive_timeout_ms, - 0, INT_MAX}); - t->keepalive_timeout = value == INT_MAX - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis(value, GPR_TIMESPAN); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { - t->keepalive_permit_without_calls = - (uint32_t)grpc_channel_arg_get_integer( - &channel_args->args[i], (grpc_integer_options){0, 0, 1}); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_OPTIMIZATION_TARGET)) { - if (channel_args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "%s should be a string", - GRPC_ARG_OPTIMIZATION_TARGET); - } else if (0 == strcmp(channel_args->args[i].value.string, "blend")) { - t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; - } else if (0 == strcmp(channel_args->args[i].value.string, "latency")) { - t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; - } else if (0 == - strcmp(channel_args->args[i].value.string, "throughput")) { - t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT; - } else { - gpr_log(GPR_ERROR, "%s value '%s' unknown, assuming 'blend'", - GRPC_ARG_OPTIMIZATION_TARGET, - channel_args->args[i].value.string); - } - } else { - static const struct { - const char *channel_arg_name; - grpc_chttp2_setting_id setting_id; - grpc_integer_options integer_options; - bool availability[2] /* server, client */; - } settings_map[] = { - {GRPC_ARG_MAX_CONCURRENT_STREAMS, - GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, - {-1, 0, INT32_MAX}, - {true, false}}, - {GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER, - GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE, - {-1, 0, INT32_MAX}, - {true, true}}, - {GRPC_ARG_MAX_METADATA_SIZE, - GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, - {-1, 0, INT32_MAX}, - {true, true}}, - {GRPC_ARG_HTTP2_MAX_FRAME_SIZE, - GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, - {-1, 16384, 16777215}, - {true, true}}, - {GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY, - GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, - {1, 0, 1}, - {true, true}}, - {GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES, - GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, - {-1, 5, INT32_MAX}, - {true, true}}}; - for (j = 0; j < (int)GPR_ARRAY_SIZE(settings_map); j++) { - if (0 == strcmp(channel_args->args[i].key, - settings_map[j].channel_arg_name)) { - if (!settings_map[j].availability[is_client]) { - gpr_log(GPR_DEBUG, "%s is not available on %s", - settings_map[j].channel_arg_name, - is_client ? "clients" : "servers"); - } else { - int value = grpc_channel_arg_get_integer( - &channel_args->args[i], settings_map[j].integer_options); - if (value >= 0) { - queue_setting_update(exec_ctx, t, settings_map[j].setting_id, - (uint32_t)value); - } - } - break; - } - } - } - } - } - - /* No pings allowed before receiving a header or data frame. */ - t->ping_state.pings_before_data_required = 0; - t->ping_state.is_delayed_ping_timer_set = false; - - t->ping_recv_state.last_ping_recv_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); - t->ping_recv_state.ping_strikes = 0; - - /* Start keepalive pings */ - if (gpr_time_cmp(t->keepalive_time, gpr_inf_future(GPR_TIMESPAN)) != 0) { - t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING; - GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); - grpc_timer_init( - exec_ctx, &t->keepalive_ping_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), - &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); - } else { - /* Use GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED to indicate there are no - inflight keeaplive timers */ - t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED; - } - - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE); - post_benign_reclaimer(exec_ctx, t); -} - -static void destroy_transport_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - t->destroying = 1; - close_transport_locked( - exec_ctx, t, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"), - GRPC_ERROR_INT_OCCURRED_DURING_WRITE, t->write_state)); - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "destroy"); -} - -static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - GRPC_CLOSURE_SCHED(exec_ctx, - GRPC_CLOSURE_CREATE(destroy_transport_locked, t, - grpc_combiner_scheduler(t->combiner)), - GRPC_ERROR_NONE); -} - -static void close_transport_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_error *error) { - if (!t->closed) { - if (!grpc_error_has_clear_grpc_status(error)) { - error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNAVAILABLE); - } - if (t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE) { - if (t->close_transport_on_writes_finished == NULL) { - t->close_transport_on_writes_finished = - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Delayed close due to in-progress write"); - } - t->close_transport_on_writes_finished = - grpc_error_add_child(t->close_transport_on_writes_finished, error); - return; - } - t->closed = 1; - connectivity_state_set(exec_ctx, t, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_REF(error), "close_transport"); - grpc_endpoint_shutdown(exec_ctx, t->ep, GRPC_ERROR_REF(error)); - if (t->ping_state.is_delayed_ping_timer_set) { - grpc_timer_cancel(exec_ctx, &t->ping_state.delayed_ping_timer); - } - switch (t->keepalive_state) { - case GRPC_CHTTP2_KEEPALIVE_STATE_WAITING: - grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer); - break; - case GRPC_CHTTP2_KEEPALIVE_STATE_PINGING: - grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer); - grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer); - break; - case GRPC_CHTTP2_KEEPALIVE_STATE_DYING: - case GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED: - /* keepalive timers are not set in these two states */ - break; - } - - /* flush writable stream list to avoid dangling references */ - grpc_chttp2_stream *s; - while (grpc_chttp2_list_pop_writable_stream(t, &s)) { - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:close"); - } - end_all_the_calls(exec_ctx, t, GRPC_ERROR_REF(error)); - cancel_pings(exec_ctx, t, GRPC_ERROR_REF(error)); - } - GRPC_ERROR_UNREF(error); -} - -#ifndef NDEBUG -void grpc_chttp2_stream_ref(grpc_chttp2_stream *s, const char *reason) { - grpc_stream_ref(s->refcount, reason); -} -void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s, - const char *reason) { - grpc_stream_unref(exec_ctx, s->refcount, reason); -} -#else -void grpc_chttp2_stream_ref(grpc_chttp2_stream *s) { - grpc_stream_ref(s->refcount); -} -void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s) { - grpc_stream_unref(exec_ctx, s->refcount); -} -#endif - -static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_stream_refcount *refcount, - const void *server_data, gpr_arena *arena) { - GPR_TIMER_BEGIN("init_stream", 0); - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; - - s->t = t; - s->refcount = refcount; - /* We reserve one 'active stream' that's dropped when the stream is - read-closed. The others are for incoming_byte_streams that are actively - reading */ - GRPC_CHTTP2_STREAM_REF(s, "chttp2"); - - grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[0], arena); - grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[1], arena); - grpc_chttp2_data_parser_init(&s->data_parser); - grpc_slice_buffer_init(&s->flow_controlled_buffer); - s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - GRPC_CLOSURE_INIT(&s->complete_fetch_locked, complete_fetch_locked, s, - grpc_schedule_on_exec_ctx); - grpc_slice_buffer_init(&s->unprocessed_incoming_frames_buffer); - grpc_slice_buffer_init(&s->frame_storage); - grpc_slice_buffer_init(&s->compressed_data_buffer); - grpc_slice_buffer_init(&s->decompressed_data_buffer); - s->pending_byte_stream = false; - s->decompressed_header_bytes = 0; - GRPC_CLOSURE_INIT(&s->reset_byte_stream, reset_byte_stream, s, - grpc_combiner_scheduler(t->combiner)); - - GRPC_CHTTP2_REF_TRANSPORT(t, "stream"); - - if (server_data) { - s->id = (uint32_t)(uintptr_t)server_data; - *t->accepting_stream = s; - grpc_chttp2_stream_map_add(&t->stream_map, s->id, s); - post_destructive_reclaimer(exec_ctx, t); - } - - s->flow_control.s = s; - GPR_TIMER_END("init_stream", 0); - - return 0; -} - -static void destroy_stream_locked(grpc_exec_ctx *exec_ctx, void *sp, - grpc_error *error) { - grpc_chttp2_stream *s = (grpc_chttp2_stream *)sp; - grpc_chttp2_transport *t = s->t; - - GPR_TIMER_BEGIN("destroy_stream", 0); - - GPR_ASSERT((s->write_closed && s->read_closed) || s->id == 0); - if (s->id != 0) { - GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, s->id) == NULL); - } - - grpc_slice_buffer_destroy_internal(exec_ctx, - &s->unprocessed_incoming_frames_buffer); - grpc_slice_buffer_destroy_internal(exec_ctx, &s->frame_storage); - grpc_slice_buffer_destroy_internal(exec_ctx, &s->compressed_data_buffer); - grpc_slice_buffer_destroy_internal(exec_ctx, &s->decompressed_data_buffer); - - grpc_chttp2_list_remove_stalled_by_transport(t, s); - grpc_chttp2_list_remove_stalled_by_stream(t, s); - - for (int i = 0; i < STREAM_LIST_COUNT; i++) { - if (s->included[i]) { - gpr_log(GPR_ERROR, "%s stream %d still included in list %d", - t->is_client ? "client" : "server", s->id, i); - abort(); - } - } - - GPR_ASSERT(s->send_initial_metadata_finished == NULL); - GPR_ASSERT(s->fetching_send_message == NULL); - GPR_ASSERT(s->send_trailing_metadata_finished == NULL); - GPR_ASSERT(s->recv_initial_metadata_ready == NULL); - GPR_ASSERT(s->recv_message_ready == NULL); - GPR_ASSERT(s->recv_trailing_metadata_finished == NULL); - grpc_chttp2_data_parser_destroy(exec_ctx, &s->data_parser); - grpc_chttp2_incoming_metadata_buffer_destroy(exec_ctx, - &s->metadata_buffer[0]); - grpc_chttp2_incoming_metadata_buffer_destroy(exec_ctx, - &s->metadata_buffer[1]); - grpc_slice_buffer_destroy_internal(exec_ctx, &s->flow_controlled_buffer); - GRPC_ERROR_UNREF(s->read_closed_error); - GRPC_ERROR_UNREF(s->write_closed_error); - GRPC_ERROR_UNREF(s->byte_stream_error); - - grpc_chttp2_flowctl_destroy_stream(&t->flow_control, &s->flow_control); - - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "stream"); - - GPR_TIMER_END("destroy_stream", 0); - - GRPC_CLOSURE_SCHED(exec_ctx, s->destroy_stream_arg, GRPC_ERROR_NONE); -} - -static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, - grpc_closure *then_schedule_closure) { - GPR_TIMER_BEGIN("destroy_stream", 0); - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; - - if (s->stream_compression_ctx != NULL) { - grpc_stream_compression_context_destroy(s->stream_compression_ctx); - s->stream_compression_ctx = NULL; - } - if (s->stream_decompression_ctx != NULL) { - grpc_stream_compression_context_destroy(s->stream_decompression_ctx); - s->stream_decompression_ctx = NULL; - } - - s->destroy_stream_arg = then_schedule_closure; - GRPC_CLOSURE_SCHED( - exec_ctx, GRPC_CLOSURE_INIT(&s->destroy_stream, destroy_stream_locked, s, - grpc_combiner_scheduler(t->combiner)), - GRPC_ERROR_NONE); - GPR_TIMER_END("destroy_stream", 0); -} - -grpc_chttp2_stream *grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport *t, - uint32_t id) { - return (grpc_chttp2_stream *)grpc_chttp2_stream_map_find(&t->stream_map, id); -} - -grpc_chttp2_stream *grpc_chttp2_parsing_accept_stream(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - uint32_t id) { - if (t->channel_callback.accept_stream == NULL) { - return NULL; - } - grpc_chttp2_stream *accepting; - GPR_ASSERT(t->accepting_stream == NULL); - t->accepting_stream = &accepting; - t->channel_callback.accept_stream(exec_ctx, - t->channel_callback.accept_stream_user_data, - &t->base, (void *)(uintptr_t)id); - t->accepting_stream = NULL; - return accepting; -} - -/******************************************************************************* - * OUTPUT PROCESSING - */ - -static const char *write_state_name(grpc_chttp2_write_state st) { - switch (st) { - case GRPC_CHTTP2_WRITE_STATE_IDLE: - return "IDLE"; - case GRPC_CHTTP2_WRITE_STATE_WRITING: - return "WRITING"; - case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: - return "WRITING+MORE"; - } - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -static void set_write_state(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_write_state st, const char *reason) { - GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_DEBUG, "W:%p %s state %s -> %s [%s]", t, - t->is_client ? "CLIENT" : "SERVER", - write_state_name(t->write_state), - write_state_name(st), reason)); - t->write_state = st; - if (st == GRPC_CHTTP2_WRITE_STATE_IDLE) { - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &t->run_after_write); - if (t->close_transport_on_writes_finished != NULL) { - grpc_error *err = t->close_transport_on_writes_finished; - t->close_transport_on_writes_finished = NULL; - close_transport_locked(exec_ctx, t, err); - } - } -} - -static void inc_initiate_write_reason( - grpc_exec_ctx *exec_ctx, grpc_chttp2_initiate_write_reason reason) { - switch (reason) { - case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA( - exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA( - exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL( - exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_BDP_ESTIMATOR_PING: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING( - exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE( - exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED( - exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE(exec_ctx); - break; - case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM: - GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM(exec_ctx); - break; - } -} - -void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_initiate_write_reason reason) { - GPR_TIMER_BEGIN("grpc_chttp2_initiate_write", 0); - - switch (t->write_state) { - case GRPC_CHTTP2_WRITE_STATE_IDLE: - inc_initiate_write_reason(exec_ctx, reason); - set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING, - grpc_chttp2_initiate_write_reason_string(reason)); - t->is_first_write_in_batch = true; - GRPC_CHTTP2_REF_TRANSPORT(t, "writing"); - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&t->write_action_begin_locked, - write_action_begin_locked, t, - grpc_combiner_finally_scheduler(t->combiner)), - GRPC_ERROR_NONE); - break; - case GRPC_CHTTP2_WRITE_STATE_WRITING: - set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE, - grpc_chttp2_initiate_write_reason_string(reason)); - break; - case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: - break; - } - GPR_TIMER_END("grpc_chttp2_initiate_write", 0); -} - -void grpc_chttp2_mark_stream_writable(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - if (!t->closed && grpc_chttp2_list_add_writable_stream(t, s)) { - GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:become"); - } -} - -static grpc_closure_scheduler *write_scheduler(grpc_chttp2_transport *t, - bool early_results_scheduled, - bool partial_write) { - /* if it's not the first write in a batch, always offload to the executor: - we'll probably end up queuing against the kernel anyway, so we'll likely - get better latency overall if we switch writing work elsewhere and continue - with application work above */ - if (!t->is_first_write_in_batch) { - return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); - } - /* equivalently, if it's a partial write, we *know* we're going to be taking a - thread jump to write it because of the above, may as well do so - immediately */ - if (partial_write) { - return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); - } - switch (t->opt_target) { - case GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT: - /* executor gives us the largest probability of being able to batch a - * write with others on this transport */ - return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); - case GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY: - return grpc_schedule_on_exec_ctx; - } - GPR_UNREACHABLE_CODE(return NULL); -} - -#define WRITE_STATE_TUPLE_TO_INT(p, i) (2 * (int)(p) + (int)(i)) -static const char *begin_writing_desc(bool partial, bool inlined) { - switch (WRITE_STATE_TUPLE_TO_INT(partial, inlined)) { - case WRITE_STATE_TUPLE_TO_INT(false, false): - return "begin write in background"; - case WRITE_STATE_TUPLE_TO_INT(false, true): - return "begin write in current thread"; - case WRITE_STATE_TUPLE_TO_INT(true, false): - return "begin partial write in background"; - case WRITE_STATE_TUPLE_TO_INT(true, true): - return "begin partial write in current thread"; - } - GPR_UNREACHABLE_CODE(return "bad state tuple"); -} - -static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *gt, - grpc_error *error_ignored) { - GPR_TIMER_BEGIN("write_action_begin_locked", 0); - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - GPR_ASSERT(t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE); - grpc_chttp2_begin_write_result r; - if (t->closed) { - r.writing = false; - } else { - r = grpc_chttp2_begin_write(exec_ctx, t); - } - if (r.writing) { - if (r.partial) { - GRPC_STATS_INC_HTTP2_PARTIAL_WRITES(exec_ctx); - } - if (!t->is_first_write_in_batch) { - GRPC_STATS_INC_HTTP2_WRITES_CONTINUED(exec_ctx); - } - grpc_closure_scheduler *scheduler = - write_scheduler(t, r.early_results_scheduled, r.partial); - if (scheduler != grpc_schedule_on_exec_ctx) { - GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED(exec_ctx); - } - set_write_state( - exec_ctx, t, r.partial ? GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE - : GRPC_CHTTP2_WRITE_STATE_WRITING, - begin_writing_desc(r.partial, scheduler == grpc_schedule_on_exec_ctx)); - GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_INIT(&t->write_action, - write_action, t, scheduler), - GRPC_ERROR_NONE); - } else { - set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE, - "begin writing nothing"); - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing"); - } - GPR_TIMER_END("write_action_begin_locked", 0); -} - -static void write_action(grpc_exec_ctx *exec_ctx, void *gt, grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - GPR_TIMER_BEGIN("write_action", 0); - grpc_endpoint_write( - exec_ctx, t->ep, &t->outbuf, - GRPC_CLOSURE_INIT(&t->write_action_end_locked, write_action_end_locked, t, - grpc_combiner_scheduler(t->combiner))); - GPR_TIMER_END("write_action", 0); -} - -static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error) { - GPR_TIMER_BEGIN("terminate_writing_with_lock", 0); - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - - if (error != GRPC_ERROR_NONE) { - close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error)); - } - - if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED) { - t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SENT; - if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) { - close_transport_locked( - exec_ctx, t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("goaway sent")); - } - } - - switch (t->write_state) { - case GRPC_CHTTP2_WRITE_STATE_IDLE: - GPR_UNREACHABLE_CODE(break); - case GRPC_CHTTP2_WRITE_STATE_WRITING: - GPR_TIMER_MARK("state=writing", 0); - set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE, - "finish writing"); - break; - case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: - GPR_TIMER_MARK("state=writing_stale_no_poller", 0); - set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING, - "continue writing"); - t->is_first_write_in_batch = false; - GRPC_CHTTP2_REF_TRANSPORT(t, "writing"); - GRPC_CLOSURE_RUN( - exec_ctx, - GRPC_CLOSURE_INIT(&t->write_action_begin_locked, - write_action_begin_locked, t, - grpc_combiner_finally_scheduler(t->combiner)), - GRPC_ERROR_NONE); - break; - } - - grpc_chttp2_end_write(exec_ctx, t, GRPC_ERROR_REF(error)); - - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing"); - GPR_TIMER_END("terminate_writing_with_lock", 0); -} - -// Dirties an HTTP2 setting to be sent out next time a writing path occurs. -// If the change needs to occur immediately, manually initiate a write. -static void queue_setting_update(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_setting_id id, uint32_t value) { - const grpc_chttp2_setting_parameters *sp = - &grpc_chttp2_settings_parameters[id]; - uint32_t use_value = GPR_CLAMP(value, sp->min_value, sp->max_value); - if (use_value != value) { - gpr_log(GPR_INFO, "Requested parameter %s clamped from %d to %d", sp->name, - value, use_value); - } - if (use_value != t->settings[GRPC_LOCAL_SETTINGS][id]) { - t->settings[GRPC_LOCAL_SETTINGS][id] = use_value; - t->dirtied_local_settings = 1; - } -} - -void grpc_chttp2_add_incoming_goaway(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - uint32_t goaway_error, - grpc_slice goaway_text) { - // GRPC_CHTTP2_IF_TRACING( - // gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg)); - t->seen_goaway = 1; - - /* When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug - * data equal to "too_many_pings", it should log the occurrence at a log level - * that is enabled by default and double the configured KEEPALIVE_TIME used - * for new connections on that channel. */ - if (t->is_client && goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM && - grpc_slice_str_cmp(goaway_text, "too_many_pings") == 0) { - gpr_log(GPR_ERROR, - "Received a GOAWAY with error code ENHANCE_YOUR_CALM and debug " - "data equal to \"too_many_pings\""); - double current_keepalive_time_ms = - gpr_timespec_to_micros(t->keepalive_time) / 1000; - t->keepalive_time = - current_keepalive_time_ms > INT_MAX / KEEPALIVE_TIME_BACKOFF_MULTIPLIER - ? gpr_inf_future(GPR_TIMESPAN) - : gpr_time_from_millis((int64_t)(current_keepalive_time_ms * - KEEPALIVE_TIME_BACKOFF_MULTIPLIER), - GPR_TIMESPAN); - } - - /* lie: use transient failure from the transport to indicate goaway has been - * received */ - connectivity_state_set( - exec_ctx, t, GRPC_CHANNEL_TRANSIENT_FAILURE, - grpc_error_set_str( - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("GOAWAY received"), - GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)goaway_error), - GRPC_ERROR_STR_RAW_BYTES, goaway_text), - "got_goaway"); -} - -static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_chttp2_stream *s; - /* start streams where we have free grpc_chttp2_stream ids and free - * concurrency */ - while (t->next_stream_id <= MAX_CLIENT_STREAM_ID && - grpc_chttp2_stream_map_size(&t->stream_map) < - t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] && - grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) { - /* safe since we can't (legally) be parsing this stream yet */ - GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_DEBUG, "HTTP:%s: Allocating new grpc_chttp2_stream %p to id %d", - t->is_client ? "CLI" : "SVR", s, t->next_stream_id)); - - GPR_ASSERT(s->id == 0); - s->id = t->next_stream_id; - t->next_stream_id += 2; - - if (t->next_stream_id >= MAX_CLIENT_STREAM_ID) { - connectivity_state_set( - exec_ctx, t, GRPC_CHANNEL_TRANSIENT_FAILURE, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"), - "no_more_stream_ids"); - } - - grpc_chttp2_stream_map_add(&t->stream_map, s->id, s); - post_destructive_reclaimer(exec_ctx, t); - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM); - } - /* cancel out streams that will never be started */ - while (t->next_stream_id >= MAX_CLIENT_STREAM_ID && - grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) { - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); - } -} - -/* Flag that this closure barrier wants stats to be updated before finishing */ -#define CLOSURE_BARRIER_STATS_BIT (1 << 0) -/* Flag that this closure barrier may be covering a write in a pollset, and so - we should not complete this closure until we can prove that the write got - scheduled */ -#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 1) -/* First bit of the reference count, stored in the high order bits (with the low - bits being used for flags defined above) */ -#define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16) - -static grpc_closure *add_closure_barrier(grpc_closure *closure) { - closure->next_data.scratch += CLOSURE_BARRIER_FIRST_REF_BIT; - return closure; -} - -static void null_then_run_closure(grpc_exec_ctx *exec_ctx, - grpc_closure **closure, grpc_error *error) { - grpc_closure *c = *closure; - *closure = NULL; - GRPC_CLOSURE_RUN(exec_ctx, c, error); -} - -void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_closure **pclosure, - grpc_error *error, const char *desc) { - grpc_closure *closure = *pclosure; - *pclosure = NULL; - if (closure == NULL) { - GRPC_ERROR_UNREF(error); - return; - } - closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT; - if (GRPC_TRACER_ON(grpc_http_trace)) { - const char *errstr = grpc_error_string(error); - gpr_log( - GPR_DEBUG, - "complete_closure_step: t=%p %p refs=%d flags=0x%04x desc=%s err=%s " - "write_state=%s", - t, closure, - (int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT), - (int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT), desc, - errstr, write_state_name(t->write_state)); - } - if (error != GRPC_ERROR_NONE) { - if (closure->error_data.error == GRPC_ERROR_NONE) { - closure->error_data.error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Error in HTTP transport completing operation"); - closure->error_data.error = grpc_error_set_str( - closure->error_data.error, GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(t->peer_string)); - } - closure->error_data.error = - grpc_error_add_child(closure->error_data.error, error); - } - if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) { - if (closure->next_data.scratch & CLOSURE_BARRIER_STATS_BIT) { - grpc_transport_move_stats(&s->stats, s->collecting_stats); - s->collecting_stats = NULL; - } - if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) || - !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) { - GRPC_CLOSURE_RUN(exec_ctx, closure, closure->error_data.error); - } else { - grpc_closure_list_append(&t->run_after_write, closure, - closure->error_data.error); - } - } -} - -static bool contains_non_ok_status(grpc_metadata_batch *batch) { - if (batch->idx.named.grpc_status != NULL) { - return !grpc_mdelem_eq(batch->idx.named.grpc_status->md, - GRPC_MDELEM_GRPC_STATUS_0); - } - return false; -} - -static void maybe_become_writable_due_to_send_msg(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - if (s->id != 0 && (!s->write_buffering || - s->flow_controlled_buffer.length > t->write_buffer_size)) { - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE); - } -} - -static void add_fetched_slice_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - s->fetched_send_message_length += - (uint32_t)GRPC_SLICE_LENGTH(s->fetching_slice); - grpc_slice_buffer_add(&s->flow_controlled_buffer, s->fetching_slice); - maybe_become_writable_due_to_send_msg(exec_ctx, t, s); -} - -static void continue_fetching_send_locked(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - for (;;) { - if (s->fetching_send_message == NULL) { - /* Stream was cancelled before message fetch completed */ - abort(); /* TODO(ctiller): what cleanup here? */ - return; /* early out */ - } - if (s->fetched_send_message_length == s->fetching_send_message->length) { - grpc_byte_stream_destroy(exec_ctx, s->fetching_send_message); - int64_t notify_offset = s->next_message_end_offset; - if (notify_offset <= s->flow_controlled_bytes_written) { - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_NONE, - "fetching_send_message_finished"); - } else { - grpc_chttp2_write_cb *cb = t->write_cb_pool; - if (cb == NULL) { - cb = (grpc_chttp2_write_cb *)gpr_malloc(sizeof(*cb)); - } else { - t->write_cb_pool = cb->next; - } - cb->call_at_byte = notify_offset; - cb->closure = s->fetching_send_message_finished; - s->fetching_send_message_finished = NULL; - grpc_chttp2_write_cb **list = - s->fetching_send_message->flags & GRPC_WRITE_THROUGH - ? &s->on_write_finished_cbs - : &s->on_flow_controlled_cbs; - cb->next = *list; - *list = cb; - } - s->fetching_send_message = NULL; - return; /* early out */ - } else if (grpc_byte_stream_next(exec_ctx, s->fetching_send_message, - UINT32_MAX, &s->complete_fetch_locked)) { - grpc_error *error = grpc_byte_stream_pull( - exec_ctx, s->fetching_send_message, &s->fetching_slice); - if (error != GRPC_ERROR_NONE) { - grpc_byte_stream_destroy(exec_ctx, s->fetching_send_message); - grpc_chttp2_cancel_stream(exec_ctx, t, s, error); - } else { - add_fetched_slice_locked(exec_ctx, t, s); - } - } - } -} - -static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs, - grpc_error *error) { - grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; - grpc_chttp2_transport *t = s->t; - if (error == GRPC_ERROR_NONE) { - error = grpc_byte_stream_pull(exec_ctx, s->fetching_send_message, - &s->fetching_slice); - if (error == GRPC_ERROR_NONE) { - add_fetched_slice_locked(exec_ctx, t, s); - continue_fetching_send_locked(exec_ctx, t, s); - } - } - if (error != GRPC_ERROR_NONE) { - grpc_byte_stream_destroy(exec_ctx, s->fetching_send_message); - grpc_chttp2_cancel_stream(exec_ctx, t, s, error); - } -} - -static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {} - -static void log_metadata(const grpc_metadata_batch *md_batch, uint32_t id, - bool is_client, bool is_initial) { - for (grpc_linked_mdelem *md = md_batch->list.head; md != NULL; - md = md->next) { - char *key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); - char *value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); - gpr_log(GPR_INFO, "HTTP:%d:%s:%s: %s: %s", id, is_initial ? "HDR" : "TRL", - is_client ? "CLI" : "SVR", key, value); - gpr_free(key); - gpr_free(value); - } -} - -static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op, - grpc_error *error_ignored) { - GPR_TIMER_BEGIN("perform_stream_op_locked", 0); - - grpc_transport_stream_op_batch *op = - (grpc_transport_stream_op_batch *)stream_op; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)op->handler_private.extra_arg; - grpc_transport_stream_op_batch_payload *op_payload = op->payload; - grpc_chttp2_transport *t = s->t; - - GRPC_STATS_INC_HTTP2_OP_BATCHES(exec_ctx); - - if (GRPC_TRACER_ON(grpc_http_trace)) { - char *str = grpc_transport_stream_op_batch_string(op); - gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s; on_complete = %p", str, - op->on_complete); - gpr_free(str); - if (op->send_initial_metadata) { - log_metadata(op_payload->send_initial_metadata.send_initial_metadata, - s->id, t->is_client, true); - } - if (op->send_trailing_metadata) { - log_metadata(op_payload->send_trailing_metadata.send_trailing_metadata, - s->id, t->is_client, false); - } - } - - grpc_closure *on_complete = op->on_complete; - if (on_complete == NULL) { - on_complete = - GRPC_CLOSURE_CREATE(do_nothing, NULL, grpc_schedule_on_exec_ctx); - } - - /* use final_data as a barrier until enqueue time; the inital counter is - dropped at the end of this function */ - on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT; - on_complete->error_data.error = GRPC_ERROR_NONE; - - if (op->collect_stats) { - GPR_ASSERT(s->collecting_stats == NULL); - s->collecting_stats = op_payload->collect_stats.collect_stats; - on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT; - } - - if (op->cancel_stream) { - GRPC_STATS_INC_HTTP2_OP_CANCEL(exec_ctx); - grpc_chttp2_cancel_stream(exec_ctx, t, s, - op_payload->cancel_stream.cancel_error); - } - - if (op->send_initial_metadata) { - GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA(exec_ctx); - GPR_ASSERT(s->send_initial_metadata_finished == NULL); - on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; - - /* Identify stream compression */ - if (op_payload->send_initial_metadata.send_initial_metadata->idx.named - .content_encoding == NULL || - grpc_stream_compression_method_parse( - GRPC_MDVALUE( - op_payload->send_initial_metadata.send_initial_metadata->idx - .named.content_encoding->md), - true, &s->stream_compression_method) == 0) { - s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS; - } - - s->send_initial_metadata_finished = add_closure_barrier(on_complete); - s->send_initial_metadata = - op_payload->send_initial_metadata.send_initial_metadata; - const size_t metadata_size = - grpc_metadata_batch_size(s->send_initial_metadata); - const size_t metadata_peer_limit = - t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; - if (t->is_client) { - s->deadline = - gpr_time_min(s->deadline, s->send_initial_metadata->deadline); - } - if (metadata_size > metadata_peer_limit) { - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int( - grpc_error_set_int( - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "to-be-sent initial metadata size " - "exceeds peer limit"), - GRPC_ERROR_INT_SIZE, - (intptr_t)metadata_size), - GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); - } else { - if (contains_non_ok_status(s->send_initial_metadata)) { - s->seen_error = true; - } - if (!s->write_closed) { - if (t->is_client) { - if (!t->closed) { - GPR_ASSERT(s->id == 0); - grpc_chttp2_list_add_waiting_for_concurrency(t, s); - maybe_start_some_streams(exec_ctx, t); - } else { - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); - } - } else { - GPR_ASSERT(s->id != 0); - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - if (!(op->send_message && - (op->payload->send_message.send_message->flags & - GRPC_WRITE_BUFFER_HINT))) { - grpc_chttp2_initiate_write( - exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA); - } - } - } else { - s->send_initial_metadata = NULL; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->send_initial_metadata_finished, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Attempt to send initial metadata after stream was closed", - &s->write_closed_error, 1), - "send_initial_metadata_finished"); - } - } - if (op_payload->send_initial_metadata.peer_string != NULL) { - gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string, - (gpr_atm)gpr_strdup(t->peer_string)); - } - } - - if (op->send_message) { - GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE(exec_ctx); - GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE( - exec_ctx, op->payload->send_message.send_message->length); - on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; - s->fetching_send_message_finished = add_closure_barrier(op->on_complete); - if (s->write_closed) { - // Return an error unless the client has already received trailing - // metadata from the server, since an application using a - // streaming call might send another message before getting a - // recv_message failure, breaking out of its loop, and then - // starting recv_trailing_metadata. - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->fetching_send_message_finished, - t->is_client && s->received_trailing_metadata - ? GRPC_ERROR_NONE - : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Attempt to send message after stream was closed", - &s->write_closed_error, 1), - "fetching_send_message_finished"); - } else { - GPR_ASSERT(s->fetching_send_message == NULL); - uint8_t *frame_hdr = grpc_slice_buffer_tiny_add( - &s->flow_controlled_buffer, GRPC_HEADER_SIZE_IN_BYTES); - uint32_t flags = op_payload->send_message.send_message->flags; - frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0; - size_t len = op_payload->send_message.send_message->length; - frame_hdr[1] = (uint8_t)(len >> 24); - frame_hdr[2] = (uint8_t)(len >> 16); - frame_hdr[3] = (uint8_t)(len >> 8); - frame_hdr[4] = (uint8_t)(len); - s->fetching_send_message = op_payload->send_message.send_message; - s->fetched_send_message_length = 0; - s->next_message_end_offset = s->flow_controlled_bytes_written + - (int64_t)s->flow_controlled_buffer.length + - (int64_t)len; - if (flags & GRPC_WRITE_BUFFER_HINT) { - s->next_message_end_offset -= t->write_buffer_size; - s->write_buffering = true; - } else { - s->write_buffering = false; - } - continue_fetching_send_locked(exec_ctx, t, s); - maybe_become_writable_due_to_send_msg(exec_ctx, t, s); - } - } - - if (op->send_trailing_metadata) { - GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA(exec_ctx); - GPR_ASSERT(s->send_trailing_metadata_finished == NULL); - on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; - s->send_trailing_metadata_finished = add_closure_barrier(on_complete); - s->send_trailing_metadata = - op_payload->send_trailing_metadata.send_trailing_metadata; - s->write_buffering = false; - const size_t metadata_size = - grpc_metadata_batch_size(s->send_trailing_metadata); - const size_t metadata_peer_limit = - t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; - if (metadata_size > metadata_peer_limit) { - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int( - grpc_error_set_int( - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "to-be-sent trailing metadata size " - "exceeds peer limit"), - GRPC_ERROR_INT_SIZE, - (intptr_t)metadata_size), - GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); - } else { - if (contains_non_ok_status(s->send_trailing_metadata)) { - s->seen_error = true; - } - if (s->write_closed) { - s->send_trailing_metadata = NULL; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->send_trailing_metadata_finished, - grpc_metadata_batch_is_empty( - op->payload->send_trailing_metadata.send_trailing_metadata) - ? GRPC_ERROR_NONE - : GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Attempt to send trailing metadata after " - "stream was closed"), - "send_trailing_metadata_finished"); - } else if (s->id != 0) { - /* TODO(ctiller): check if there's flow control for any outstanding - bytes before going writable */ - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - grpc_chttp2_initiate_write( - exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA); - } - } - } - - if (op->recv_initial_metadata) { - GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA(exec_ctx); - GPR_ASSERT(s->recv_initial_metadata_ready == NULL); - s->recv_initial_metadata_ready = - op_payload->recv_initial_metadata.recv_initial_metadata_ready; - s->recv_initial_metadata = - op_payload->recv_initial_metadata.recv_initial_metadata; - s->trailing_metadata_available = - op_payload->recv_initial_metadata.trailing_metadata_available; - if (op_payload->recv_initial_metadata.peer_string != NULL) { - gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string, - (gpr_atm)gpr_strdup(t->peer_string)); - } - grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); - } - - if (op->recv_message) { - GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE(exec_ctx); - size_t already_received; - GPR_ASSERT(s->recv_message_ready == NULL); - GPR_ASSERT(!s->pending_byte_stream); - s->recv_message_ready = op_payload->recv_message.recv_message_ready; - s->recv_message = op_payload->recv_message.recv_message; - if (s->id != 0) { - if (!s->read_closed) { - already_received = s->frame_storage.length; - grpc_chttp2_flowctl_incoming_bs_update( - &t->flow_control, &s->flow_control, GRPC_HEADER_SIZE_IN_BYTES, - already_received); - grpc_chttp2_act_on_flowctl_action( - exec_ctx, - grpc_chttp2_flowctl_get_action(&t->flow_control, &s->flow_control), - t, s); - } - } - grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); - } - - if (op->recv_trailing_metadata) { - GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA(exec_ctx); - GPR_ASSERT(s->recv_trailing_metadata_finished == NULL); - s->recv_trailing_metadata_finished = add_closure_barrier(on_complete); - s->recv_trailing_metadata = - op_payload->recv_trailing_metadata.recv_trailing_metadata; - s->final_metadata_requested = true; - grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); - } - - grpc_chttp2_complete_closure_step(exec_ctx, t, s, &on_complete, - GRPC_ERROR_NONE, "op->on_complete"); - - GPR_TIMER_END("perform_stream_op_locked", 0); - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "perform_stream_op"); -} - -static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, - grpc_transport_stream_op_batch *op) { - GPR_TIMER_BEGIN("perform_stream_op", 0); - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; - - if (!t->is_client) { - if (op->send_initial_metadata) { - gpr_timespec deadline = - op->payload->send_initial_metadata.send_initial_metadata->deadline; - GPR_ASSERT(0 == - gpr_time_cmp(gpr_inf_future(deadline.clock_type), deadline)); - } - if (op->send_trailing_metadata) { - gpr_timespec deadline = - op->payload->send_trailing_metadata.send_trailing_metadata->deadline; - GPR_ASSERT(0 == - gpr_time_cmp(gpr_inf_future(deadline.clock_type), deadline)); - } - } - - if (GRPC_TRACER_ON(grpc_http_trace)) { - char *str = grpc_transport_stream_op_batch_string(op); - gpr_log(GPR_DEBUG, "perform_stream_op[s=%p]: %s", s, str); - gpr_free(str); - } - - op->handler_private.extra_arg = gs; - GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op"); - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&op->handler_private.closure, perform_stream_op_locked, - op, grpc_combiner_scheduler(t->combiner)), - GRPC_ERROR_NONE); - GPR_TIMER_END("perform_stream_op", 0); -} - -static void cancel_pings(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_error *error) { - /* callback remaining pings: they're not allowed to call into the transpot, - and maybe they hold resources that need to be freed */ - for (size_t i = 0; i < GRPC_CHTTP2_PING_TYPE_COUNT; i++) { - grpc_chttp2_ping_queue *pq = &t->ping_queues[i]; - for (size_t j = 0; j < GRPC_CHTTP2_PCL_COUNT; j++) { - grpc_closure_list_fail_all(&pq->lists[j], GRPC_ERROR_REF(error)); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pq->lists[j]); - } - } - GRPC_ERROR_UNREF(error); -} - -static void send_ping_locked( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_ping_type ping_type, grpc_closure *on_initiate, - grpc_closure *on_ack, - grpc_chttp2_initiate_write_reason initiate_write_reason) { - grpc_chttp2_ping_queue *pq = &t->ping_queues[ping_type]; - grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE], on_initiate, - GRPC_ERROR_NONE); - if (grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT], on_ack, - GRPC_ERROR_NONE)) { - grpc_chttp2_initiate_write(exec_ctx, t, initiate_write_reason); - } -} - -static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - t->ping_state.is_delayed_ping_timer_set = false; - if (error == GRPC_ERROR_NONE) { - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING); - } -} - -void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - uint64_t id) { - grpc_chttp2_ping_queue *pq = - &t->ping_queues[id % GRPC_CHTTP2_PING_TYPE_COUNT]; - if (pq->inflight_id != id) { - char *from = grpc_endpoint_get_peer(t->ep); - gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64, from, id); - gpr_free(from); - return; - } - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]); - if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) { - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS); - } -} - -static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_error *error) { - t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED; - grpc_http2_error_code http_error; - grpc_slice slice; - grpc_error_get_status(error, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL, - &slice, &http_error); - grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error, - grpc_slice_ref_internal(slice), &t->qbuf); - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT); - GRPC_ERROR_UNREF(error); -} - -void grpc_chttp2_add_ping_strike(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - gpr_log(GPR_DEBUG, "PING strike"); - if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes && - t->ping_policy.max_ping_strikes != 0) { - send_goaway(exec_ctx, t, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("too_many_pings"), - GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM)); - /*The transport will be closed after the write is done */ - close_transport_locked( - exec_ctx, t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many pings")); - } -} - -static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx, - void *stream_op, - grpc_error *error_ignored) { - grpc_transport_op *op = (grpc_transport_op *)stream_op; - grpc_chttp2_transport *t = - (grpc_chttp2_transport *)op->handler_private.extra_arg; - grpc_error *close_transport = op->disconnect_with_error; - - if (op->goaway_error) { - send_goaway(exec_ctx, t, op->goaway_error); - } - - if (op->set_accept_stream) { - t->channel_callback.accept_stream = op->set_accept_stream_fn; - t->channel_callback.accept_stream_user_data = - op->set_accept_stream_user_data; - } - - if (op->bind_pollset) { - grpc_endpoint_add_to_pollset(exec_ctx, t->ep, op->bind_pollset); - } - - if (op->bind_pollset_set) { - grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, op->bind_pollset_set); - } - - if (op->send_ping) { - send_ping_locked(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE, NULL, - op->send_ping, - GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING); - } - - if (op->on_connectivity_state_change != NULL) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &t->channel_callback.state_tracker, op->connectivity_state, - op->on_connectivity_state_change); - } - - if (close_transport != GRPC_ERROR_NONE) { - close_transport_locked(exec_ctx, t, close_transport); - } - - GRPC_CLOSURE_RUN(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); - - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "transport_op"); -} - -static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_transport_op *op) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - char *msg = grpc_transport_op_string(op); - gpr_free(msg); - op->handler_private.extra_arg = gt; - GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op"); - GRPC_CLOSURE_SCHED(exec_ctx, - GRPC_CLOSURE_INIT(&op->handler_private.closure, - perform_transport_op_locked, op, - grpc_combiner_scheduler(t->combiner)), - GRPC_ERROR_NONE); -} - -/******************************************************************************* - * INPUT PROCESSING - GENERAL - */ - -void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - if (s->recv_initial_metadata_ready != NULL && - s->published_metadata[0] != GRPC_METADATA_NOT_PUBLISHED) { - if (s->seen_error) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); - if (!s->pending_byte_stream) { - grpc_slice_buffer_reset_and_unref_internal( - exec_ctx, &s->unprocessed_incoming_frames_buffer); - } - } - grpc_chttp2_incoming_metadata_buffer_publish( - exec_ctx, &s->metadata_buffer[0], s->recv_initial_metadata); - null_then_run_closure(exec_ctx, &s->recv_initial_metadata_ready, - GRPC_ERROR_NONE); - } -} - -void grpc_chttp2_maybe_complete_recv_message(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - grpc_error *error = GRPC_ERROR_NONE; - if (s->recv_message_ready != NULL) { - *s->recv_message = NULL; - if (s->final_metadata_requested && s->seen_error) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); - if (!s->pending_byte_stream) { - grpc_slice_buffer_reset_and_unref_internal( - exec_ctx, &s->unprocessed_incoming_frames_buffer); - } - } - if (!s->pending_byte_stream) { - while (s->unprocessed_incoming_frames_buffer.length > 0 || - s->frame_storage.length > 0) { - if (s->unprocessed_incoming_frames_buffer.length == 0) { - grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer, - &s->frame_storage); - s->unprocessed_incoming_frames_decompressed = false; - } - if (!s->unprocessed_incoming_frames_decompressed) { - GPR_ASSERT(s->decompressed_data_buffer.length == 0); - bool end_of_context; - if (!s->stream_decompression_ctx) { - s->stream_decompression_ctx = - grpc_stream_compression_context_create( - s->stream_decompression_method); - } - if (!grpc_stream_decompress( - s->stream_decompression_ctx, - &s->unprocessed_incoming_frames_buffer, - &s->decompressed_data_buffer, NULL, - GRPC_HEADER_SIZE_IN_BYTES - s->decompressed_header_bytes, - &end_of_context)) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, - &s->frame_storage); - grpc_slice_buffer_reset_and_unref_internal( - exec_ctx, &s->unprocessed_incoming_frames_buffer); - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Stream decompression error."); - } else { - s->decompressed_header_bytes += s->decompressed_data_buffer.length; - if (s->decompressed_header_bytes == GRPC_HEADER_SIZE_IN_BYTES) { - s->decompressed_header_bytes = 0; - } - error = grpc_deframe_unprocessed_incoming_frames( - exec_ctx, &s->data_parser, s, &s->decompressed_data_buffer, - NULL, s->recv_message); - if (end_of_context) { - grpc_stream_compression_context_destroy( - s->stream_decompression_ctx); - s->stream_decompression_ctx = NULL; - } - } - } else { - error = grpc_deframe_unprocessed_incoming_frames( - exec_ctx, &s->data_parser, s, - &s->unprocessed_incoming_frames_buffer, NULL, s->recv_message); - } - if (error != GRPC_ERROR_NONE) { - s->seen_error = true; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, - &s->frame_storage); - grpc_slice_buffer_reset_and_unref_internal( - exec_ctx, &s->unprocessed_incoming_frames_buffer); - break; - } else if (*s->recv_message != NULL) { - break; - } - } - } - if (error == GRPC_ERROR_NONE && *s->recv_message != NULL) { - null_then_run_closure(exec_ctx, &s->recv_message_ready, GRPC_ERROR_NONE); - } else if (s->published_metadata[1] != GRPC_METADATA_NOT_PUBLISHED) { - *s->recv_message = NULL; - null_then_run_closure(exec_ctx, &s->recv_message_ready, GRPC_ERROR_NONE); - } - GRPC_ERROR_UNREF(error); - } -} - -void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); - if (s->recv_trailing_metadata_finished != NULL && s->read_closed && - s->write_closed) { - if (s->seen_error) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); - if (!s->pending_byte_stream) { - grpc_slice_buffer_reset_and_unref_internal( - exec_ctx, &s->unprocessed_incoming_frames_buffer); - } - } - bool pending_data = s->pending_byte_stream || - s->unprocessed_incoming_frames_buffer.length > 0; - if (s->read_closed && s->frame_storage.length > 0 && !pending_data && - !s->seen_error && s->recv_trailing_metadata_finished != NULL) { - /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and - * maybe decompress the next 5 bytes in the stream. */ - bool end_of_context; - if (!s->stream_decompression_ctx) { - s->stream_decompression_ctx = grpc_stream_compression_context_create( - s->stream_decompression_method); - } - if (!grpc_stream_decompress(s->stream_decompression_ctx, - &s->frame_storage, - &s->unprocessed_incoming_frames_buffer, NULL, - GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); - grpc_slice_buffer_reset_and_unref_internal( - exec_ctx, &s->unprocessed_incoming_frames_buffer); - s->seen_error = true; - } else { - if (s->unprocessed_incoming_frames_buffer.length > 0) { - s->unprocessed_incoming_frames_decompressed = true; - pending_data = true; - } - if (end_of_context) { - grpc_stream_compression_context_destroy(s->stream_decompression_ctx); - s->stream_decompression_ctx = NULL; - } - } - } - if (s->read_closed && s->frame_storage.length == 0 && !pending_data && - s->recv_trailing_metadata_finished != NULL) { - grpc_chttp2_incoming_metadata_buffer_publish( - exec_ctx, &s->metadata_buffer[1], s->recv_trailing_metadata); - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->recv_trailing_metadata_finished, GRPC_ERROR_NONE, - "recv_trailing_metadata_finished"); - } - } -} - -static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - uint32_t id, grpc_error *error) { - grpc_chttp2_stream *s = - (grpc_chttp2_stream *)grpc_chttp2_stream_map_delete(&t->stream_map, id); - GPR_ASSERT(s); - if (t->incoming_stream == s) { - t->incoming_stream = NULL; - grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); - } - if (s->pending_byte_stream) { - if (s->on_next != NULL) { - grpc_chttp2_incoming_byte_stream *bs = s->data_parser.parsing_frame; - if (error == GRPC_ERROR_NONE) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); - } - incoming_byte_stream_publish_error(exec_ctx, bs, error); - incoming_byte_stream_unref(exec_ctx, bs); - s->data_parser.parsing_frame = NULL; - } else { - GRPC_ERROR_UNREF(s->byte_stream_error); - s->byte_stream_error = GRPC_ERROR_REF(error); - } - } - - if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) { - post_benign_reclaimer(exec_ctx, t); - if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SENT) { - close_transport_locked( - exec_ctx, t, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Last stream closed after sending GOAWAY", &error, 1)); - } - } - if (grpc_chttp2_list_remove_writable_stream(t, s)) { - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:remove_stream"); - } - - GRPC_ERROR_UNREF(error); - - maybe_start_some_streams(exec_ctx, t); -} - -void grpc_chttp2_cancel_stream(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, grpc_chttp2_stream *s, - grpc_error *due_to_error) { - if (!t->is_client && !s->sent_trailing_metadata && - grpc_error_has_clear_grpc_status(due_to_error)) { - close_from_api(exec_ctx, t, s, due_to_error); - return; - } - - if (!s->read_closed || !s->write_closed) { - if (s->id != 0) { - grpc_http2_error_code http_error; - grpc_error_get_status(due_to_error, s->deadline, NULL, NULL, &http_error); - grpc_slice_buffer_add( - &t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error, - &s->stats.outgoing)); - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM); - } - } - if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) { - s->seen_error = true; - } - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, due_to_error); -} - -void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_error *error) { - grpc_status_code status; - grpc_slice slice; - grpc_error_get_status(error, s->deadline, &status, &slice, NULL); - - if (status != GRPC_STATUS_OK) { - s->seen_error = true; - } - /* stream_global->recv_trailing_metadata_finished gives us a - last chance replacement: we've received trailing metadata, - but something more important has become available to signal - to the upper layers - drop what we've got, and then publish - what we want - which is safe because we haven't told anyone - about the metadata yet */ - if (s->published_metadata[1] == GRPC_METADATA_NOT_PUBLISHED || - s->recv_trailing_metadata_finished != NULL) { - char status_string[GPR_LTOA_MIN_BUFSIZE]; - gpr_ltoa(status, status_string); - GRPC_LOG_IF_ERROR("add_status", - grpc_chttp2_incoming_metadata_buffer_replace_or_add( - exec_ctx, &s->metadata_buffer[1], - grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_GRPC_STATUS, - grpc_slice_from_copied_string(status_string)))); - if (!GRPC_SLICE_IS_EMPTY(slice)) { - GRPC_LOG_IF_ERROR( - "add_status_message", - grpc_chttp2_incoming_metadata_buffer_replace_or_add( - exec_ctx, &s->metadata_buffer[1], - grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_MESSAGE, - grpc_slice_ref_internal(slice)))); - } - s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE; - grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); - } - - GRPC_ERROR_UNREF(error); -} - -static void add_error(grpc_error *error, grpc_error **refs, size_t *nrefs) { - if (error == GRPC_ERROR_NONE) return; - for (size_t i = 0; i < *nrefs; i++) { - if (error == refs[i]) { - return; - } - } - refs[*nrefs] = error; - ++*nrefs; -} - -static grpc_error *removal_error(grpc_error *extra_error, grpc_chttp2_stream *s, - const char *master_error_msg) { - grpc_error *refs[3]; - size_t nrefs = 0; - add_error(s->read_closed_error, refs, &nrefs); - add_error(s->write_closed_error, refs, &nrefs); - add_error(extra_error, refs, &nrefs); - grpc_error *error = GRPC_ERROR_NONE; - if (nrefs > 0) { - error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(master_error_msg, - refs, nrefs); - } - GRPC_ERROR_UNREF(extra_error); - return error; -} - -static void flush_write_list(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_chttp2_write_cb **list, - grpc_error *error) { - while (*list) { - grpc_chttp2_write_cb *cb = *list; - *list = cb->next; - grpc_chttp2_complete_closure_step(exec_ctx, t, s, &cb->closure, - GRPC_ERROR_REF(error), - "on_write_finished_cb"); - cb->next = t->write_cb_pool; - t->write_cb_pool = cb; - } - GRPC_ERROR_UNREF(error); -} - -void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_error *error) { - error = - removal_error(error, s, "Pending writes failed due to stream closure"); - s->send_initial_metadata = NULL; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->send_initial_metadata_finished, GRPC_ERROR_REF(error), - "send_initial_metadata_finished"); - - s->send_trailing_metadata = NULL; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->send_trailing_metadata_finished, - GRPC_ERROR_REF(error), "send_trailing_metadata_finished"); - - s->fetching_send_message = NULL; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_REF(error), - "fetching_send_message_finished"); - flush_write_list(exec_ctx, t, s, &s->on_write_finished_cbs, - GRPC_ERROR_REF(error)); - flush_write_list(exec_ctx, t, s, &s->on_flow_controlled_cbs, error); -} - -void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, int close_reads, - int close_writes, grpc_error *error) { - if (s->read_closed && s->write_closed) { - /* already closed */ - grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); - GRPC_ERROR_UNREF(error); - return; - } - bool closed_read = false; - bool became_closed = false; - if (close_reads && !s->read_closed) { - s->read_closed_error = GRPC_ERROR_REF(error); - s->read_closed = true; - closed_read = true; - } - if (close_writes && !s->write_closed) { - s->write_closed_error = GRPC_ERROR_REF(error); - s->write_closed = true; - grpc_chttp2_fail_pending_writes(exec_ctx, t, s, GRPC_ERROR_REF(error)); - } - if (s->read_closed && s->write_closed) { - became_closed = true; - grpc_error *overall_error = - removal_error(GRPC_ERROR_REF(error), s, "Stream removed"); - if (s->id != 0) { - remove_stream(exec_ctx, t, s->id, GRPC_ERROR_REF(overall_error)); - } else { - /* Purge streams waiting on concurrency still waiting for id assignment */ - grpc_chttp2_list_remove_waiting_for_concurrency(t, s); - } - if (overall_error != GRPC_ERROR_NONE) { - grpc_chttp2_fake_status(exec_ctx, t, s, overall_error); - } - } - if (closed_read) { - for (int i = 0; i < 2; i++) { - if (s->published_metadata[i] == GRPC_METADATA_NOT_PUBLISHED) { - s->published_metadata[i] = GPRC_METADATA_PUBLISHED_AT_CLOSE; - } - } - grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); - grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); - } - if (became_closed) { - grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2"); - } - GRPC_ERROR_UNREF(error); -} - -static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_error *error) { - grpc_slice hdr; - grpc_slice status_hdr; - grpc_slice http_status_hdr; - grpc_slice content_type_hdr; - grpc_slice message_pfx; - uint8_t *p; - uint32_t len = 0; - grpc_status_code grpc_status; - grpc_slice slice; - grpc_error_get_status(error, s->deadline, &grpc_status, &slice, NULL); - - GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100); - - /* Hand roll a header block. - This is unnecessarily ugly - at some point we should find a more - elegant solution. - It's complicated by the fact that our send machinery would be dead by - the time we got around to sending this, so instead we ignore HPACK - compression and just write the uncompressed bytes onto the wire. */ - if (!s->sent_initial_metadata) { - http_status_hdr = GRPC_SLICE_MALLOC(13); - p = GRPC_SLICE_START_PTR(http_status_hdr); - *p++ = 0x00; - *p++ = 7; - *p++ = ':'; - *p++ = 's'; - *p++ = 't'; - *p++ = 'a'; - *p++ = 't'; - *p++ = 'u'; - *p++ = 's'; - *p++ = 3; - *p++ = '2'; - *p++ = '0'; - *p++ = '0'; - GPR_ASSERT(p == GRPC_SLICE_END_PTR(http_status_hdr)); - len += (uint32_t)GRPC_SLICE_LENGTH(http_status_hdr); - - content_type_hdr = GRPC_SLICE_MALLOC(31); - p = GRPC_SLICE_START_PTR(content_type_hdr); - *p++ = 0x00; - *p++ = 12; - *p++ = 'c'; - *p++ = 'o'; - *p++ = 'n'; - *p++ = 't'; - *p++ = 'e'; - *p++ = 'n'; - *p++ = 't'; - *p++ = '-'; - *p++ = 't'; - *p++ = 'y'; - *p++ = 'p'; - *p++ = 'e'; - *p++ = 16; - *p++ = 'a'; - *p++ = 'p'; - *p++ = 'p'; - *p++ = 'l'; - *p++ = 'i'; - *p++ = 'c'; - *p++ = 'a'; - *p++ = 't'; - *p++ = 'i'; - *p++ = 'o'; - *p++ = 'n'; - *p++ = '/'; - *p++ = 'g'; - *p++ = 'r'; - *p++ = 'p'; - *p++ = 'c'; - GPR_ASSERT(p == GRPC_SLICE_END_PTR(content_type_hdr)); - len += (uint32_t)GRPC_SLICE_LENGTH(content_type_hdr); - } - - status_hdr = GRPC_SLICE_MALLOC(15 + (grpc_status >= 10)); - p = GRPC_SLICE_START_PTR(status_hdr); - *p++ = 0x00; /* literal header, not indexed */ - *p++ = 11; /* len(grpc-status) */ - *p++ = 'g'; - *p++ = 'r'; - *p++ = 'p'; - *p++ = 'c'; - *p++ = '-'; - *p++ = 's'; - *p++ = 't'; - *p++ = 'a'; - *p++ = 't'; - *p++ = 'u'; - *p++ = 's'; - if (grpc_status < 10) { - *p++ = 1; - *p++ = (uint8_t)('0' + grpc_status); - } else { - *p++ = 2; - *p++ = (uint8_t)('0' + (grpc_status / 10)); - *p++ = (uint8_t)('0' + (grpc_status % 10)); - } - GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr)); - len += (uint32_t)GRPC_SLICE_LENGTH(status_hdr); - - size_t msg_len = GRPC_SLICE_LENGTH(slice); - GPR_ASSERT(msg_len <= UINT32_MAX); - uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 1); - message_pfx = GRPC_SLICE_MALLOC(14 + msg_len_len); - p = GRPC_SLICE_START_PTR(message_pfx); - *p++ = 0x00; /* literal header, not indexed */ - *p++ = 12; /* len(grpc-message) */ - *p++ = 'g'; - *p++ = 'r'; - *p++ = 'p'; - *p++ = 'c'; - *p++ = '-'; - *p++ = 'm'; - *p++ = 'e'; - *p++ = 's'; - *p++ = 's'; - *p++ = 'a'; - *p++ = 'g'; - *p++ = 'e'; - GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 1, 0, p, (uint32_t)msg_len_len); - p += msg_len_len; - GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx)); - len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx); - len += (uint32_t)msg_len; - - hdr = GRPC_SLICE_MALLOC(9); - p = GRPC_SLICE_START_PTR(hdr); - *p++ = (uint8_t)(len >> 16); - *p++ = (uint8_t)(len >> 8); - *p++ = (uint8_t)(len); - *p++ = GRPC_CHTTP2_FRAME_HEADER; - *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; - *p++ = (uint8_t)(s->id >> 24); - *p++ = (uint8_t)(s->id >> 16); - *p++ = (uint8_t)(s->id >> 8); - *p++ = (uint8_t)(s->id); - GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr)); - - grpc_slice_buffer_add(&t->qbuf, hdr); - if (!s->sent_initial_metadata) { - grpc_slice_buffer_add(&t->qbuf, http_status_hdr); - grpc_slice_buffer_add(&t->qbuf, content_type_hdr); - } - grpc_slice_buffer_add(&t->qbuf, status_hdr); - grpc_slice_buffer_add(&t->qbuf, message_pfx); - grpc_slice_buffer_add(&t->qbuf, grpc_slice_ref_internal(slice)); - grpc_slice_buffer_add( - &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, - &s->stats.outgoing)); - - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, error); - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API); -} - -typedef struct { - grpc_exec_ctx *exec_ctx; - grpc_error *error; - grpc_chttp2_transport *t; -} cancel_stream_cb_args; - -static void cancel_stream_cb(void *user_data, uint32_t key, void *stream) { - cancel_stream_cb_args *args = (cancel_stream_cb_args *)user_data; - grpc_chttp2_stream *s = (grpc_chttp2_stream *)stream; - grpc_chttp2_cancel_stream(args->exec_ctx, args->t, s, - GRPC_ERROR_REF(args->error)); -} - -static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_error *error) { - cancel_stream_cb_args args = {exec_ctx, error, t}; - grpc_chttp2_stream_map_for_each(&t->stream_map, cancel_stream_cb, &args); - GRPC_ERROR_UNREF(error); -} - -/******************************************************************************* - * INPUT PROCESSING - PARSING - */ - -void grpc_chttp2_act_on_flowctl_action(grpc_exec_ctx *exec_ctx, - grpc_chttp2_flowctl_action action, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - switch (action.send_stream_update) { - case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED: - break; - case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY: - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - grpc_chttp2_initiate_write( - exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL); - break; - case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE: - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - break; - } - switch (action.send_transport_update) { - case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED: - break; - case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY: - grpc_chttp2_initiate_write( - exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL); - break; - // this is the same as no action b/c every time the transport enters the - // writing path it will maybe do an update - case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE: - break; - } - if (action.send_setting_update != GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED) { - if (action.initial_window_size > 0) { - queue_setting_update(exec_ctx, t, - GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, - (uint32_t)action.initial_window_size); - } - if (action.max_frame_size > 0) { - queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, - (uint32_t)action.max_frame_size); - } - if (action.send_setting_update == GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY) { - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS); - } - } - if (action.need_ping) { - GRPC_CHTTP2_REF_TRANSPORT(t, "bdp_ping"); - grpc_bdp_estimator_schedule_ping(&t->flow_control.bdp_estimator); - send_ping_locked(exec_ctx, t, - GRPC_CHTTP2_PING_BEFORE_TRANSPORT_WINDOW_UPDATE, - &t->start_bdp_ping_locked, &t->finish_bdp_ping_locked, - GRPC_CHTTP2_INITIATE_WRITE_BDP_ESTIMATOR_PING); - } -} - -static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_http_parser parser; - size_t i = 0; - grpc_error *error = GRPC_ERROR_NONE; - grpc_http_response response; - memset(&response, 0, sizeof(response)); - - grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response); - - grpc_error *parse_error = GRPC_ERROR_NONE; - for (; i < t->read_buffer.count && parse_error == GRPC_ERROR_NONE; i++) { - parse_error = - grpc_http_parser_parse(&parser, t->read_buffer.slices[i], NULL); - } - if (parse_error == GRPC_ERROR_NONE && - (parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) { - error = grpc_error_set_int( - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Trying to connect an http1.x server"), - GRPC_ERROR_INT_HTTP_STATUS, response.status), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); - } - GRPC_ERROR_UNREF(parse_error); - - grpc_http_parser_destroy(&parser); - grpc_http_response_destroy(&response); - return error; -} - -static void read_action_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error) { - GPR_TIMER_BEGIN("reading_action_locked", 0); - - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - - GRPC_ERROR_REF(error); - - grpc_error *err = error; - if (err != GRPC_ERROR_NONE) { - err = grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Endpoint read failed", &err, 1), - GRPC_ERROR_INT_OCCURRED_DURING_WRITE, - t->write_state); - } - GPR_SWAP(grpc_error *, err, error); - GRPC_ERROR_UNREF(err); - if (!t->closed) { - GPR_TIMER_BEGIN("reading_action.parse", 0); - size_t i = 0; - grpc_error *errors[3] = {GRPC_ERROR_REF(error), GRPC_ERROR_NONE, - GRPC_ERROR_NONE}; - for (; i < t->read_buffer.count && errors[1] == GRPC_ERROR_NONE; i++) { - grpc_bdp_estimator_add_incoming_bytes( - &t->flow_control.bdp_estimator, - (int64_t)GRPC_SLICE_LENGTH(t->read_buffer.slices[i])); - errors[1] = - grpc_chttp2_perform_read(exec_ctx, t, t->read_buffer.slices[i]); - } - if (errors[1] != GRPC_ERROR_NONE) { - errors[2] = try_http_parsing(exec_ctx, t); - GRPC_ERROR_UNREF(error); - error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed parsing HTTP/2", errors, GPR_ARRAY_SIZE(errors)); - } - for (i = 0; i < GPR_ARRAY_SIZE(errors); i++) { - GRPC_ERROR_UNREF(errors[i]); - } - GPR_TIMER_END("reading_action.parse", 0); - - GPR_TIMER_BEGIN("post_parse_locked", 0); - if (t->flow_control.initial_window_update != 0) { - if (t->flow_control.initial_window_update > 0) { - grpc_chttp2_stream *s; - while (grpc_chttp2_list_pop_stalled_by_stream(t, &s)) { - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - grpc_chttp2_initiate_write( - exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING); - } - } - t->flow_control.initial_window_update = 0; - } - GPR_TIMER_END("post_parse_locked", 0); - } - - GPR_TIMER_BEGIN("post_reading_action_locked", 0); - bool keep_reading = false; - if (error == GRPC_ERROR_NONE && t->closed) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"); - } - if (error != GRPC_ERROR_NONE) { - close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error)); - t->endpoint_reading = 0; - } else if (!t->closed) { - keep_reading = true; - GRPC_CHTTP2_REF_TRANSPORT(t, "keep_reading"); - } - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &t->read_buffer); - - if (keep_reading) { - grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, - &t->read_action_locked); - grpc_chttp2_act_on_flowctl_action( - exec_ctx, grpc_chttp2_flowctl_get_bdp_action(&t->flow_control), t, - NULL); - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keep_reading"); - } else { - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "reading_action"); - } - - GPR_TIMER_END("post_reading_action_locked", 0); - - GRPC_ERROR_UNREF(error); - - GPR_TIMER_END("reading_action_locked", 0); -} - -static void start_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_DEBUG, "%s: Start BDP ping", t->peer_string); - } - /* Reset the keepalive ping timer */ - if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING) { - grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer); - } - grpc_bdp_estimator_start_ping(&t->flow_control.bdp_estimator); -} - -static void finish_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_DEBUG, "%s: Complete BDP ping", t->peer_string); - } - grpc_bdp_estimator_complete_ping(&t->flow_control.bdp_estimator); - - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "bdp_ping"); -} - -void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, - bool is_client) { - size_t i; - if (args) { - for (i = 0; i < args->num_args; i++) { - if (0 == strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { - const int value = grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){g_default_client_keepalive_time_ms, 1, - INT_MAX}); - if (is_client) { - g_default_client_keepalive_time_ms = value; - } else { - g_default_server_keepalive_time_ms = value; - } - } else if (0 == - strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { - const int value = grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){g_default_client_keepalive_timeout_ms, 0, - INT_MAX}); - if (is_client) { - g_default_client_keepalive_timeout_ms = value; - } else { - g_default_server_keepalive_timeout_ms = value; - } - } else if (0 == strcmp(args->args[i].key, - GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { - g_default_keepalive_permit_without_calls = - (uint32_t)grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){g_default_keepalive_permit_without_calls, - 0, 1}); - } else if (0 == - strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { - g_default_max_ping_strikes = grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){g_default_max_ping_strikes, 0, INT_MAX}); - } else if (0 == strcmp(args->args[i].key, - GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { - g_default_max_pings_without_data = grpc_channel_arg_get_integer( - &args->args[i], (grpc_integer_options){ - g_default_max_pings_without_data, 0, INT_MAX}); - } else if (0 == - strcmp( - args->args[i].key, - GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) { - g_default_min_sent_ping_interval_without_data_ms = - grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){ - g_default_min_sent_ping_interval_without_data_ms, 0, - INT_MAX}); - } else if (0 == - strcmp( - args->args[i].key, - GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) { - g_default_min_recv_ping_interval_without_data_ms = - grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){ - g_default_min_recv_ping_interval_without_data_ms, 0, - INT_MAX}); - } - } - } -} - -static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; - GPR_ASSERT(t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING); - if (t->destroying || t->closed) { - t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING; - } else if (error == GRPC_ERROR_NONE) { - if (t->keepalive_permit_without_calls || - grpc_chttp2_stream_map_size(&t->stream_map) > 0) { - t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING; - GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end"); - send_ping_locked(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE, - &t->start_keepalive_ping_locked, - &t->finish_keepalive_ping_locked, - GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING); - } else { - GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); - grpc_timer_init( - exec_ctx, &t->keepalive_ping_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), - &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); - } - } else if (error == GRPC_ERROR_CANCELLED) { - /* The keepalive ping timer may be cancelled by bdp */ - GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); - grpc_timer_init( - exec_ctx, &t->keepalive_ping_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), - &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); - } - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "init keepalive ping"); -} - -static void start_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; - GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog"); - grpc_timer_init( - exec_ctx, &t->keepalive_watchdog_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_timeout), - &t->keepalive_watchdog_fired_locked, gpr_now(GPR_CLOCK_MONOTONIC)); -} - -static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; - if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) { - if (error == GRPC_ERROR_NONE) { - t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING; - grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer); - GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); - grpc_timer_init( - exec_ctx, &t->keepalive_ping_timer, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), - &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); - } - } - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive ping end"); -} - -static void keepalive_watchdog_fired_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; - if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) { - if (error == GRPC_ERROR_NONE) { - t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING; - close_transport_locked(exec_ctx, t, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "keepalive watchdog timeout")); - } - } else { - /* The watchdog timer should have been cancelled by - * finish_keepalive_ping_locked. */ - if (error != GRPC_ERROR_CANCELLED) { - gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)", - t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING); - } - } - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive watchdog"); -} - -/******************************************************************************* - * CALLBACK LOOP - */ - -static void connectivity_state_set(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_connectivity_state state, - grpc_error *error, const char *reason) { - GRPC_CHTTP2_IF_TRACING( - gpr_log(GPR_DEBUG, "set connectivity_state=%d", state)); - grpc_connectivity_state_set(exec_ctx, &t->channel_callback.state_tracker, - state, error, reason); -} - -/******************************************************************************* - * POLLSET STUFF - */ - -static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_pollset *pollset) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_endpoint_add_to_pollset(exec_ctx, t->ep, pollset); -} - -static void set_pollset_set(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_pollset_set *pollset_set) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; - grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, pollset_set); -} - -/******************************************************************************* - * BYTE STREAM - */ - -static void reset_byte_stream(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_stream *s = (grpc_chttp2_stream *)arg; - - s->pending_byte_stream = false; - if (error == GRPC_ERROR_NONE) { - grpc_chttp2_maybe_complete_recv_message(exec_ctx, s->t, s); - grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, s->t, s); - } else { - GPR_ASSERT(error != GRPC_ERROR_NONE); - GRPC_CLOSURE_SCHED(exec_ctx, s->on_next, GRPC_ERROR_REF(error)); - s->on_next = NULL; - GRPC_ERROR_UNREF(s->byte_stream_error); - s->byte_stream_error = GRPC_ERROR_NONE; - grpc_chttp2_cancel_stream(exec_ctx, s->t, s, GRPC_ERROR_REF(error)); - s->byte_stream_error = GRPC_ERROR_REF(error); - } -} - -static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_chttp2_incoming_byte_stream *bs) { - if (gpr_unref(&bs->refs)) { - gpr_free(bs); - } -} - -static void incoming_byte_stream_next_locked(grpc_exec_ctx *exec_ctx, - void *argp, - grpc_error *error_ignored) { - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)argp; - grpc_chttp2_transport *t = bs->transport; - grpc_chttp2_stream *s = bs->stream; - - size_t cur_length = s->frame_storage.length; - if (!s->read_closed) { - grpc_chttp2_flowctl_incoming_bs_update(&t->flow_control, &s->flow_control, - bs->next_action.max_size_hint, - cur_length); - grpc_chttp2_act_on_flowctl_action( - exec_ctx, - grpc_chttp2_flowctl_get_action(&t->flow_control, &s->flow_control), t, - s); - } - GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0); - if (s->frame_storage.length > 0) { - grpc_slice_buffer_swap(&s->frame_storage, - &s->unprocessed_incoming_frames_buffer); - s->unprocessed_incoming_frames_decompressed = false; - GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, GRPC_ERROR_NONE); - } else if (s->byte_stream_error != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, - GRPC_ERROR_REF(s->byte_stream_error)); - if (s->data_parser.parsing_frame != NULL) { - incoming_byte_stream_unref(exec_ctx, s->data_parser.parsing_frame); - s->data_parser.parsing_frame = NULL; - } - } else if (s->read_closed) { - if (bs->remaining_bytes != 0) { - s->byte_stream_error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); - GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, - GRPC_ERROR_REF(s->byte_stream_error)); - if (s->data_parser.parsing_frame != NULL) { - incoming_byte_stream_unref(exec_ctx, s->data_parser.parsing_frame); - s->data_parser.parsing_frame = NULL; - } - } else { - /* Should never reach here. */ - GPR_ASSERT(false); - } - } else { - s->on_next = bs->next_action.on_complete; - } - incoming_byte_stream_unref(exec_ctx, bs); -} - -static bool incoming_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - size_t max_size_hint, - grpc_closure *on_complete) { - GPR_TIMER_BEGIN("incoming_byte_stream_next", 0); - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)byte_stream; - grpc_chttp2_stream *s = bs->stream; - if (s->unprocessed_incoming_frames_buffer.length > 0) { - GPR_TIMER_END("incoming_byte_stream_next", 0); - return true; - } else { - gpr_ref(&bs->refs); - bs->next_action.max_size_hint = max_size_hint; - bs->next_action.on_complete = on_complete; - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&bs->next_action.closure, - incoming_byte_stream_next_locked, bs, - grpc_combiner_scheduler(bs->transport->combiner)), - GRPC_ERROR_NONE); - GPR_TIMER_END("incoming_byte_stream_next", 0); - return false; - } -} - -static grpc_error *incoming_byte_stream_pull(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_slice *slice) { - GPR_TIMER_BEGIN("incoming_byte_stream_pull", 0); - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)byte_stream; - grpc_chttp2_stream *s = bs->stream; - grpc_error *error; - - if (s->unprocessed_incoming_frames_buffer.length > 0) { - if (!s->unprocessed_incoming_frames_decompressed) { - bool end_of_context; - if (!s->stream_decompression_ctx) { - s->stream_decompression_ctx = grpc_stream_compression_context_create( - s->stream_decompression_method); - } - if (!grpc_stream_decompress(s->stream_decompression_ctx, - &s->unprocessed_incoming_frames_buffer, - &s->decompressed_data_buffer, NULL, - MAX_SIZE_T, &end_of_context)) { - error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error."); - return error; - } - GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0); - grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer, - &s->decompressed_data_buffer); - s->unprocessed_incoming_frames_decompressed = true; - if (end_of_context) { - grpc_stream_compression_context_destroy(s->stream_decompression_ctx); - s->stream_decompression_ctx = NULL; - } - if (s->unprocessed_incoming_frames_buffer.length == 0) { - *slice = grpc_empty_slice(); - } - } - error = grpc_deframe_unprocessed_incoming_frames( - exec_ctx, &s->data_parser, s, &s->unprocessed_incoming_frames_buffer, - slice, NULL); - if (error != GRPC_ERROR_NONE) { - return error; - } - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); - GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error)); - return error; - } - GPR_TIMER_END("incoming_byte_stream_pull", 0); - return GRPC_ERROR_NONE; -} - -static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, - void *byte_stream, - grpc_error *error_ignored); - -static void incoming_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) { - GPR_TIMER_BEGIN("incoming_byte_stream_destroy", 0); - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)byte_stream; - GRPC_CLOSURE_SCHED( - exec_ctx, GRPC_CLOSURE_INIT( - &bs->destroy_action, incoming_byte_stream_destroy_locked, - bs, grpc_combiner_scheduler(bs->transport->combiner)), - GRPC_ERROR_NONE); - GPR_TIMER_END("incoming_byte_stream_destroy", 0); -} - -static void incoming_byte_stream_publish_error( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, - grpc_error *error) { - grpc_chttp2_stream *s = bs->stream; - - GPR_ASSERT(error != GRPC_ERROR_NONE); - GRPC_CLOSURE_SCHED(exec_ctx, s->on_next, GRPC_ERROR_REF(error)); - s->on_next = NULL; - GRPC_ERROR_UNREF(s->byte_stream_error); - s->byte_stream_error = GRPC_ERROR_REF(error); - grpc_chttp2_cancel_stream(exec_ctx, bs->transport, bs->stream, - GRPC_ERROR_REF(error)); -} - -grpc_error *grpc_chttp2_incoming_byte_stream_push( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, - grpc_slice slice, grpc_slice *slice_out) { - grpc_chttp2_stream *s = bs->stream; - - if (bs->remaining_bytes < GRPC_SLICE_LENGTH(slice)) { - grpc_error *error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many bytes in stream"); - - GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error)); - grpc_slice_unref_internal(exec_ctx, slice); - return error; - } else { - bs->remaining_bytes -= (uint32_t)GRPC_SLICE_LENGTH(slice); - if (slice_out != NULL) { - *slice_out = slice; - } - return GRPC_ERROR_NONE; - } -} - -grpc_error *grpc_chttp2_incoming_byte_stream_finished( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, - grpc_error *error, bool reset_on_error) { - grpc_chttp2_stream *s = bs->stream; - - if (error == GRPC_ERROR_NONE) { - if (bs->remaining_bytes != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); - } - } - if (error != GRPC_ERROR_NONE && reset_on_error) { - GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error)); - } - incoming_byte_stream_unref(exec_ctx, bs); - return error; -} - -static void incoming_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_error *error) { - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)byte_stream; - GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( - exec_ctx, bs, error, true /* reset_on_error */)); -} - -static const grpc_byte_stream_vtable grpc_chttp2_incoming_byte_stream_vtable = { - incoming_byte_stream_next, incoming_byte_stream_pull, - incoming_byte_stream_shutdown, incoming_byte_stream_destroy}; - -static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, - void *byte_stream, - grpc_error *error_ignored) { - grpc_chttp2_incoming_byte_stream *bs = - (grpc_chttp2_incoming_byte_stream *)byte_stream; - grpc_chttp2_stream *s = bs->stream; - grpc_chttp2_transport *t = s->t; - - GPR_ASSERT(bs->base.vtable == &grpc_chttp2_incoming_byte_stream_vtable); - incoming_byte_stream_unref(exec_ctx, bs); - s->pending_byte_stream = false; - grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); - grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); -} - -grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_stream *s, - uint32_t frame_size, uint32_t flags) { - grpc_chttp2_incoming_byte_stream *incoming_byte_stream = - (grpc_chttp2_incoming_byte_stream *)gpr_malloc( - sizeof(*incoming_byte_stream)); - incoming_byte_stream->base.length = frame_size; - incoming_byte_stream->remaining_bytes = frame_size; - incoming_byte_stream->base.flags = flags; - incoming_byte_stream->base.vtable = &grpc_chttp2_incoming_byte_stream_vtable; - gpr_ref_init(&incoming_byte_stream->refs, 2); - incoming_byte_stream->transport = t; - incoming_byte_stream->stream = s; - GRPC_ERROR_UNREF(s->byte_stream_error); - s->byte_stream_error = GRPC_ERROR_NONE; - return incoming_byte_stream; -} - -/******************************************************************************* - * RESOURCE QUOTAS - */ - -static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (!t->benign_reclaimer_registered) { - t->benign_reclaimer_registered = true; - GRPC_CHTTP2_REF_TRANSPORT(t, "benign_reclaimer"); - grpc_resource_user_post_reclaimer(exec_ctx, - grpc_endpoint_get_resource_user(t->ep), - false, &t->benign_reclaimer_locked); - } -} - -static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (!t->destructive_reclaimer_registered) { - t->destructive_reclaimer_registered = true; - GRPC_CHTTP2_REF_TRANSPORT(t, "destructive_reclaimer"); - grpc_resource_user_post_reclaimer(exec_ctx, - grpc_endpoint_get_resource_user(t->ep), - true, &t->destructive_reclaimer_locked); - } -} - -static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; - if (error == GRPC_ERROR_NONE && - grpc_chttp2_stream_map_size(&t->stream_map) == 0) { - /* Channel with no active streams: send a goaway to try and make it - * disconnect cleanly */ - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory", - t->peer_string); - } - send_goaway(exec_ctx, t, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"), - GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM)); - } else if (error == GRPC_ERROR_NONE && - GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, - "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR - " streams", - t->peer_string, grpc_chttp2_stream_map_size(&t->stream_map)); - } - t->benign_reclaimer_registered = false; - if (error != GRPC_ERROR_CANCELLED) { - grpc_resource_user_finish_reclamation( - exec_ctx, grpc_endpoint_get_resource_user(t->ep)); - } - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "benign_reclaimer"); -} - -static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; - size_t n = grpc_chttp2_stream_map_size(&t->stream_map); - t->destructive_reclaimer_registered = false; - if (error == GRPC_ERROR_NONE && n > 0) { - grpc_chttp2_stream *s = - (grpc_chttp2_stream *)grpc_chttp2_stream_map_rand(&t->stream_map); - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "HTTP2: %s - abandon stream id %d", t->peer_string, - s->id); - } - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"), - GRPC_ERROR_INT_HTTP2_ERROR, - GRPC_HTTP2_ENHANCE_YOUR_CALM)); - if (n > 1) { - /* Since we cancel one stream per destructive reclamation, if - there are more streams left, we can immediately post a new - reclaimer in case the resource quota needs to free more - memory */ - post_destructive_reclaimer(exec_ctx, t); - } - } - if (error != GRPC_ERROR_CANCELLED) { - grpc_resource_user_finish_reclamation( - exec_ctx, grpc_endpoint_get_resource_user(t->ep)); - } - GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "destructive_reclaimer"); -} - -/******************************************************************************* - * MONITORING - */ - -const char *grpc_chttp2_initiate_write_reason_string( - grpc_chttp2_initiate_write_reason reason) { - switch (reason) { - case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE: - return "INITIAL_WRITE"; - case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM: - return "START_NEW_STREAM"; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE: - return "SEND_MESSAGE"; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA: - return "SEND_INITIAL_METADATA"; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA: - return "SEND_TRAILING_METADATA"; - case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING: - return "RETRY_SEND_PING"; - case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS: - return "CONTINUE_PINGS"; - case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT: - return "GOAWAY_SENT"; - case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM: - return "RST_STREAM"; - case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API: - return "CLOSE_FROM_API"; - case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL: - return "STREAM_FLOW_CONTROL"; - case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL: - return "TRANSPORT_FLOW_CONTROL"; - case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS: - return "SEND_SETTINGS"; - case GRPC_CHTTP2_INITIATE_WRITE_BDP_ESTIMATOR_PING: - return "BDP_ESTIMATOR_PING"; - case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING: - return "FLOW_CONTROL_UNSTALLED_BY_SETTING"; - case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE: - return "FLOW_CONTROL_UNSTALLED_BY_UPDATE"; - case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING: - return "APPLICATION_PING"; - case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING: - return "KEEPALIVE_PING"; - case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED: - return "TRANSPORT_FLOW_CONTROL_UNSTALLED"; - case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE: - return "PING_RESPONSE"; - case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM: - return "FORCE_RST_STREAM"; - } - GPR_UNREACHABLE_CODE(return "unknown"); -} - -static grpc_endpoint *chttp2_get_endpoint(grpc_exec_ctx *exec_ctx, - grpc_transport *t) { - return ((grpc_chttp2_transport *)t)->ep; -} - -static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), - "chttp2", - init_stream, - set_pollset, - set_pollset_set, - perform_stream_op, - perform_transport_op, - destroy_stream, - destroy_transport, - chttp2_get_endpoint}; - -static const grpc_transport_vtable *get_vtable(void) { return &vtable; } - -grpc_transport *grpc_create_chttp2_transport( - grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args, - grpc_endpoint *ep, int is_client) { - grpc_chttp2_transport *t = - (grpc_chttp2_transport *)gpr_zalloc(sizeof(grpc_chttp2_transport)); - init_transport(exec_ctx, t, channel_args, ep, is_client != 0); - return &t->base; -} - -void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_slice_buffer *read_buffer) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; - GRPC_CHTTP2_REF_TRANSPORT( - t, "reading_action"); /* matches unref inside reading_action */ - if (read_buffer != NULL) { - grpc_slice_buffer_move_into(read_buffer, &t->read_buffer); - gpr_free(read_buffer); - } - GRPC_CLOSURE_SCHED(exec_ctx, &t->read_action_locked, GRPC_ERROR_NONE); -} diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc new file mode 100644 index 0000000000..acf49632ff --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -0,0 +1,3252 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/frame_data.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/ext/transport/chttp2/transport/varint.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/compression/stream_compression.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/http2_errors.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_conversion.h" +#include "src/core/lib/transport/timeout_encoding.h" +#include "src/core/lib/transport/transport.h" +#include "src/core/lib/transport/transport_impl.h" + +#define DEFAULT_WINDOW 65535 +#define DEFAULT_CONNECTION_WINDOW_TARGET (1024 * 1024) +#define MAX_WINDOW 0x7fffffffu +#define MAX_WRITE_BUFFER_SIZE (64 * 1024 * 1024) +#define DEFAULT_MAX_HEADER_LIST_SIZE (8 * 1024) + +#define DEFAULT_CLIENT_KEEPALIVE_TIME_MS INT_MAX +#define DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEFAULT_SERVER_KEEPALIVE_TIME_MS 7200000 /* 2 hours */ +#define DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS 20000 /* 20 seconds */ +#define DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS false +#define KEEPALIVE_TIME_BACKOFF_MULTIPLIER 2 + +#define DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */ +#define DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS 300000 /* 5 minutes */ +#define DEFAULT_MAX_PINGS_BETWEEN_DATA 0 /* unlimited */ +#define DEFAULT_MAX_PING_STRIKES 2 + +static int g_default_client_keepalive_time_ms = + DEFAULT_CLIENT_KEEPALIVE_TIME_MS; +static int g_default_client_keepalive_timeout_ms = + DEFAULT_CLIENT_KEEPALIVE_TIMEOUT_MS; +static int g_default_server_keepalive_time_ms = + DEFAULT_SERVER_KEEPALIVE_TIME_MS; +static int g_default_server_keepalive_timeout_ms = + DEFAULT_SERVER_KEEPALIVE_TIMEOUT_MS; +static bool g_default_keepalive_permit_without_calls = + DEFAULT_KEEPALIVE_PERMIT_WITHOUT_CALLS; + +static int g_default_min_sent_ping_interval_without_data_ms = + DEFAULT_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS; +static int g_default_min_recv_ping_interval_without_data_ms = + DEFAULT_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS; +static int g_default_max_pings_without_data = DEFAULT_MAX_PINGS_BETWEEN_DATA; +static int g_default_max_ping_strikes = DEFAULT_MAX_PING_STRIKES; + +#define MAX_CLIENT_STREAM_ID 0x7fffffffu +grpc_tracer_flag grpc_http_trace = GRPC_TRACER_INITIALIZER(false, "http"); +grpc_tracer_flag grpc_flowctl_trace = GRPC_TRACER_INITIALIZER(false, "flowctl"); + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_chttp2_refcount = + GRPC_TRACER_INITIALIZER(false, "chttp2_refcount"); +#endif + +/* forward declarations of various callbacks that we'll build closures around */ +static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *t, + grpc_error *error); +static void write_action(grpc_exec_ctx *exec_ctx, void *t, grpc_error *error); +static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *t, + grpc_error *error); + +static void read_action_locked(grpc_exec_ctx *exec_ctx, void *t, + grpc_error *error); + +static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs, + grpc_error *error); +/** Set a transport level setting, and push it to our peer */ +static void queue_setting_update(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_setting_id id, uint32_t value); + +static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_error *error); + +/** Start new streams that have been created if we can */ +static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); + +static void connectivity_state_set(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_connectivity_state state, + grpc_error *error, const char *reason); + +static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, + void *byte_stream, + grpc_error *error_ignored); +static void incoming_byte_stream_publish_error( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, + grpc_error *error); +static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx, + grpc_chttp2_incoming_byte_stream *bs); + +static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t, + grpc_error *error); +static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *t, + grpc_error *error); + +static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); + +static void close_transport_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_error *error); +static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_error *error); + +static void start_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error); +static void finish_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error); + +static void cancel_pings(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_error *error); +static void send_ping_locked( + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_ping_type ping_type, grpc_closure *on_initiate, + grpc_closure *on_complete, + grpc_chttp2_initiate_write_reason initiate_write_reason); +static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error); + +/** keepalive-relevant functions */ +static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); +static void start_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); +static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); +static void keepalive_watchdog_fired_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); + +static void reset_byte_stream(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); + +/******************************************************************************* + * CONSTRUCTION/DESTRUCTION/REFCOUNTING + */ + +static void destruct_transport(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + size_t i; + + grpc_endpoint_destroy(exec_ctx, t->ep); + + grpc_slice_buffer_destroy_internal(exec_ctx, &t->qbuf); + + grpc_slice_buffer_destroy_internal(exec_ctx, &t->outbuf); + grpc_chttp2_hpack_compressor_destroy(exec_ctx, &t->hpack_compressor); + + grpc_slice_buffer_destroy_internal(exec_ctx, &t->read_buffer); + grpc_chttp2_hpack_parser_destroy(exec_ctx, &t->hpack_parser); + grpc_chttp2_goaway_parser_destroy(&t->goaway_parser); + + for (i = 0; i < STREAM_LIST_COUNT; i++) { + GPR_ASSERT(t->lists[i].head == NULL); + GPR_ASSERT(t->lists[i].tail == NULL); + } + + GPR_ASSERT(grpc_chttp2_stream_map_size(&t->stream_map) == 0); + + grpc_chttp2_stream_map_destroy(&t->stream_map); + grpc_connectivity_state_destroy(exec_ctx, &t->channel_callback.state_tracker); + + GRPC_COMBINER_UNREF(exec_ctx, t->combiner, "chttp2_transport"); + + cancel_pings(exec_ctx, t, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed")); + + while (t->write_cb_pool) { + grpc_chttp2_write_cb *next = t->write_cb_pool->next; + gpr_free(t->write_cb_pool); + t->write_cb_pool = next; + } + + gpr_free(t->ping_acks); + gpr_free(t->peer_string); + gpr_free(t); +} + +#ifndef NDEBUG +void grpc_chttp2_unref_transport(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count); + gpr_log(GPR_DEBUG, "chttp2:unref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]", + t, val, val - 1, reason, file, line); + } + if (!gpr_unref(&t->refs)) return; + destruct_transport(exec_ctx, t); +} + +void grpc_chttp2_ref_transport(grpc_chttp2_transport *t, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_chttp2_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&t->refs.count); + gpr_log(GPR_DEBUG, "chttp2: ref:%p %" PRIdPTR "->%" PRIdPTR " %s [%s:%d]", + t, val, val + 1, reason, file, line); + } + gpr_ref(&t->refs); +} +#else +void grpc_chttp2_unref_transport(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (!gpr_unref(&t->refs)) return; + destruct_transport(exec_ctx, t); +} + +void grpc_chttp2_ref_transport(grpc_chttp2_transport *t) { gpr_ref(&t->refs); } +#endif + +static const grpc_transport_vtable *get_vtable(void); + +static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + const grpc_channel_args *channel_args, + grpc_endpoint *ep, bool is_client) { + size_t i; + int j; + + GPR_ASSERT(strlen(GRPC_CHTTP2_CLIENT_CONNECT_STRING) == + GRPC_CHTTP2_CLIENT_CONNECT_STRLEN); + + t->base.vtable = get_vtable(); + t->ep = ep; + /* one ref is for destroy */ + gpr_ref_init(&t->refs, 1); + t->combiner = grpc_combiner_create(); + t->peer_string = grpc_endpoint_get_peer(ep); + t->endpoint_reading = 1; + t->next_stream_id = is_client ? 1 : 2; + t->is_client = is_client; + t->flow_control.remote_window = DEFAULT_WINDOW; + t->flow_control.announced_window = DEFAULT_WINDOW; + t->flow_control.t = t; + t->deframe_state = is_client ? GRPC_DTS_FH_0 : GRPC_DTS_CLIENT_PREFIX_0; + t->is_first_frame = true; + grpc_connectivity_state_init( + &t->channel_callback.state_tracker, GRPC_CHANNEL_READY, + is_client ? "client_transport" : "server_transport"); + + grpc_slice_buffer_init(&t->qbuf); + + grpc_slice_buffer_init(&t->outbuf); + grpc_chttp2_hpack_compressor_init(&t->hpack_compressor); + + GRPC_CLOSURE_INIT(&t->read_action_locked, read_action_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->benign_reclaimer_locked, benign_reclaimer_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->destructive_reclaimer_locked, + destructive_reclaimer_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->retry_initiate_ping_locked, retry_initiate_ping_locked, + t, grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->start_bdp_ping_locked, start_bdp_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->finish_bdp_ping_locked, finish_bdp_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->init_keepalive_ping_locked, init_keepalive_ping_locked, + t, grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->start_keepalive_ping_locked, + start_keepalive_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->finish_keepalive_ping_locked, + finish_keepalive_ping_locked, t, + grpc_combiner_scheduler(t->combiner)); + GRPC_CLOSURE_INIT(&t->keepalive_watchdog_fired_locked, + keepalive_watchdog_fired_locked, t, + grpc_combiner_scheduler(t->combiner)); + + grpc_bdp_estimator_init(&t->flow_control.bdp_estimator, t->peer_string); + t->flow_control.last_pid_update = gpr_now(GPR_CLOCK_MONOTONIC); + grpc_pid_controller_init( + &t->flow_control.pid_controller, + (grpc_pid_controller_args){.gain_p = 4, + .gain_i = 8, + .gain_d = 0, + .initial_control_value = log2(DEFAULT_WINDOW), + .min_control_value = -1, + .max_control_value = 25, + .integral_range = 10}); + + grpc_chttp2_goaway_parser_init(&t->goaway_parser); + grpc_chttp2_hpack_parser_init(exec_ctx, &t->hpack_parser); + + grpc_slice_buffer_init(&t->read_buffer); + + /* 8 is a random stab in the dark as to a good initial size: it's small enough + that it shouldn't waste memory for infrequently used connections, yet + large enough that the exponential growth should happen nicely when it's + needed. + TODO(ctiller): tune this */ + grpc_chttp2_stream_map_init(&t->stream_map, 8); + + /* copy in initial settings to all setting sets */ + for (i = 0; i < GRPC_CHTTP2_NUM_SETTINGS; i++) { + for (j = 0; j < GRPC_NUM_SETTING_SETS; j++) { + t->settings[j][i] = grpc_chttp2_settings_parameters[i].default_value; + } + } + t->dirtied_local_settings = 1; + /* Hack: it's common for implementations to assume 65536 bytes initial send + window -- this should by rights be 0 */ + t->force_send_settings = 1 << GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; + t->sent_local_settings = 0; + t->write_buffer_size = DEFAULT_WINDOW; + t->flow_control.enable_bdp_probe = true; + + if (is_client) { + grpc_slice_buffer_add(&t->outbuf, grpc_slice_from_copied_string( + GRPC_CHTTP2_CLIENT_CONNECT_STRING)); + } + + /* configure http2 the way we like it */ + if (is_client) { + queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_ENABLE_PUSH, 0); + queue_setting_update(exec_ctx, t, + GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 0); + } + queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, + DEFAULT_WINDOW); + queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, + DEFAULT_MAX_HEADER_LIST_SIZE); + queue_setting_update(exec_ctx, t, + GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, 1); + + t->ping_policy.max_pings_without_data = g_default_max_pings_without_data; + t->ping_policy.min_sent_ping_interval_without_data = gpr_time_from_millis( + g_default_min_sent_ping_interval_without_data_ms, GPR_TIMESPAN); + t->ping_policy.max_ping_strikes = g_default_max_ping_strikes; + t->ping_policy.min_recv_ping_interval_without_data = gpr_time_from_millis( + g_default_min_recv_ping_interval_without_data_ms, GPR_TIMESPAN); + + /* Keepalive setting */ + if (t->is_client) { + t->keepalive_time = + g_default_client_keepalive_time_ms == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(g_default_client_keepalive_time_ms, + GPR_TIMESPAN); + t->keepalive_timeout = + g_default_client_keepalive_timeout_ms == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(g_default_client_keepalive_timeout_ms, + GPR_TIMESPAN); + } else { + t->keepalive_time = + g_default_server_keepalive_time_ms == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(g_default_server_keepalive_time_ms, + GPR_TIMESPAN); + t->keepalive_timeout = + g_default_server_keepalive_timeout_ms == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(g_default_server_keepalive_timeout_ms, + GPR_TIMESPAN); + } + t->keepalive_permit_without_calls = g_default_keepalive_permit_without_calls; + + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; + + if (channel_args) { + for (i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER)) { + const grpc_integer_options options = {-1, 0, INT_MAX}; + const int value = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + if (value >= 0) { + if ((t->next_stream_id & 1) != (value & 1)) { + gpr_log(GPR_ERROR, "%s: low bit must be %d on %s", + GRPC_ARG_HTTP2_INITIAL_SEQUENCE_NUMBER, + t->next_stream_id & 1, is_client ? "client" : "server"); + } else { + t->next_stream_id = (uint32_t)value; + } + } + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_ENCODER)) { + const grpc_integer_options options = {-1, 0, INT_MAX}; + const int value = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + if (value >= 0) { + grpc_chttp2_hpack_compressor_set_max_usable_size(&t->hpack_compressor, + (uint32_t)value); + } + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { + t->ping_policy.max_pings_without_data = grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){g_default_max_pings_without_data, 0, + INT_MAX}); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { + t->ping_policy.max_ping_strikes = grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){g_default_max_ping_strikes, 0, INT_MAX}); + } else if (0 == + strcmp( + channel_args->args[i].key, + GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) { + t->ping_policy.min_sent_ping_interval_without_data = + gpr_time_from_millis( + grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){ + g_default_min_sent_ping_interval_without_data_ms, 0, + INT_MAX}), + GPR_TIMESPAN); + } else if (0 == + strcmp( + channel_args->args[i].key, + GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) { + t->ping_policy.min_recv_ping_interval_without_data = + gpr_time_from_millis( + grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){ + g_default_min_recv_ping_interval_without_data_ms, 0, + INT_MAX}), + GPR_TIMESPAN); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE)) { + t->write_buffer_size = (uint32_t)grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){0, 0, MAX_WRITE_BUFFER_SIZE}); + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_BDP_PROBE)) { + t->flow_control.enable_bdp_probe = grpc_channel_arg_get_integer( + &channel_args->args[i], (grpc_integer_options){1, 0, 1}); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_TIME_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){t->is_client + ? g_default_client_keepalive_time_ms + : g_default_server_keepalive_time_ms, + 1, INT_MAX}); + t->keepalive_time = value == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(value, GPR_TIMESPAN); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { + const int value = grpc_channel_arg_get_integer( + &channel_args->args[i], + (grpc_integer_options){t->is_client + ? g_default_client_keepalive_timeout_ms + : g_default_server_keepalive_timeout_ms, + 0, INT_MAX}); + t->keepalive_timeout = value == INT_MAX + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis(value, GPR_TIMESPAN); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { + t->keepalive_permit_without_calls = + (uint32_t)grpc_channel_arg_get_integer( + &channel_args->args[i], (grpc_integer_options){0, 0, 1}); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_OPTIMIZATION_TARGET)) { + if (channel_args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s should be a string", + GRPC_ARG_OPTIMIZATION_TARGET); + } else if (0 == strcmp(channel_args->args[i].value.string, "blend")) { + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; + } else if (0 == strcmp(channel_args->args[i].value.string, "latency")) { + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY; + } else if (0 == + strcmp(channel_args->args[i].value.string, "throughput")) { + t->opt_target = GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT; + } else { + gpr_log(GPR_ERROR, "%s value '%s' unknown, assuming 'blend'", + GRPC_ARG_OPTIMIZATION_TARGET, + channel_args->args[i].value.string); + } + } else { + static const struct { + const char *channel_arg_name; + grpc_chttp2_setting_id setting_id; + grpc_integer_options integer_options; + bool availability[2] /* server, client */; + } settings_map[] = { + {GRPC_ARG_MAX_CONCURRENT_STREAMS, + GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, + {-1, 0, INT32_MAX}, + {true, false}}, + {GRPC_ARG_HTTP2_HPACK_TABLE_SIZE_DECODER, + GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE, + {-1, 0, INT32_MAX}, + {true, true}}, + {GRPC_ARG_MAX_METADATA_SIZE, + GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE, + {-1, 0, INT32_MAX}, + {true, true}}, + {GRPC_ARG_HTTP2_MAX_FRAME_SIZE, + GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, + {-1, 16384, 16777215}, + {true, true}}, + {GRPC_ARG_HTTP2_ENABLE_TRUE_BINARY, + GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA, + {1, 0, 1}, + {true, true}}, + {GRPC_ARG_HTTP2_STREAM_LOOKAHEAD_BYTES, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, + {-1, 5, INT32_MAX}, + {true, true}}}; + for (j = 0; j < (int)GPR_ARRAY_SIZE(settings_map); j++) { + if (0 == strcmp(channel_args->args[i].key, + settings_map[j].channel_arg_name)) { + if (!settings_map[j].availability[is_client]) { + gpr_log(GPR_DEBUG, "%s is not available on %s", + settings_map[j].channel_arg_name, + is_client ? "clients" : "servers"); + } else { + int value = grpc_channel_arg_get_integer( + &channel_args->args[i], settings_map[j].integer_options); + if (value >= 0) { + queue_setting_update(exec_ctx, t, settings_map[j].setting_id, + (uint32_t)value); + } + } + break; + } + } + } + } + } + + /* No pings allowed before receiving a header or data frame. */ + t->ping_state.pings_before_data_required = 0; + t->ping_state.is_delayed_ping_timer_set = false; + + t->ping_recv_state.last_ping_recv_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); + t->ping_recv_state.ping_strikes = 0; + + /* Start keepalive pings */ + if (gpr_time_cmp(t->keepalive_time, gpr_inf_future(GPR_TIMESPAN)) != 0) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING; + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init( + exec_ctx, &t->keepalive_ping_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), + &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); + } else { + /* Use GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED to indicate there are no + inflight keeaplive timers */ + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED; + } + + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE); + post_benign_reclaimer(exec_ctx, t); +} + +static void destroy_transport_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + t->destroying = 1; + close_transport_locked( + exec_ctx, t, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport destroyed"), + GRPC_ERROR_INT_OCCURRED_DURING_WRITE, t->write_state)); + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "destroy"); +} + +static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + GRPC_CLOSURE_SCHED(exec_ctx, + GRPC_CLOSURE_CREATE(destroy_transport_locked, t, + grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); +} + +static void close_transport_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_error *error) { + if (!t->closed) { + if (!grpc_error_has_clear_grpc_status(error)) { + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + } + if (t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE) { + if (t->close_transport_on_writes_finished == NULL) { + t->close_transport_on_writes_finished = + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Delayed close due to in-progress write"); + } + t->close_transport_on_writes_finished = + grpc_error_add_child(t->close_transport_on_writes_finished, error); + return; + } + t->closed = 1; + connectivity_state_set(exec_ctx, t, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_REF(error), "close_transport"); + grpc_endpoint_shutdown(exec_ctx, t->ep, GRPC_ERROR_REF(error)); + if (t->ping_state.is_delayed_ping_timer_set) { + grpc_timer_cancel(exec_ctx, &t->ping_state.delayed_ping_timer); + } + switch (t->keepalive_state) { + case GRPC_CHTTP2_KEEPALIVE_STATE_WAITING: + grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer); + break; + case GRPC_CHTTP2_KEEPALIVE_STATE_PINGING: + grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer); + grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer); + break; + case GRPC_CHTTP2_KEEPALIVE_STATE_DYING: + case GRPC_CHTTP2_KEEPALIVE_STATE_DISABLED: + /* keepalive timers are not set in these two states */ + break; + } + + /* flush writable stream list to avoid dangling references */ + grpc_chttp2_stream *s; + while (grpc_chttp2_list_pop_writable_stream(t, &s)) { + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:close"); + } + end_all_the_calls(exec_ctx, t, GRPC_ERROR_REF(error)); + cancel_pings(exec_ctx, t, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +#ifndef NDEBUG +void grpc_chttp2_stream_ref(grpc_chttp2_stream *s, const char *reason) { + grpc_stream_ref(s->refcount, reason); +} +void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s, + const char *reason) { + grpc_stream_unref(exec_ctx, s->refcount, reason); +} +#else +void grpc_chttp2_stream_ref(grpc_chttp2_stream *s) { + grpc_stream_ref(s->refcount); +} +void grpc_chttp2_stream_unref(grpc_exec_ctx *exec_ctx, grpc_chttp2_stream *s) { + grpc_stream_unref(exec_ctx, s->refcount); +} +#endif + +static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_stream_refcount *refcount, + const void *server_data, gpr_arena *arena) { + GPR_TIMER_BEGIN("init_stream", 0); + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + + s->t = t; + s->refcount = refcount; + /* We reserve one 'active stream' that's dropped when the stream is + read-closed. The others are for incoming_byte_streams that are actively + reading */ + GRPC_CHTTP2_STREAM_REF(s, "chttp2"); + + grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[0], arena); + grpc_chttp2_incoming_metadata_buffer_init(&s->metadata_buffer[1], arena); + grpc_chttp2_data_parser_init(&s->data_parser); + grpc_slice_buffer_init(&s->flow_controlled_buffer); + s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + GRPC_CLOSURE_INIT(&s->complete_fetch_locked, complete_fetch_locked, s, + grpc_schedule_on_exec_ctx); + grpc_slice_buffer_init(&s->unprocessed_incoming_frames_buffer); + grpc_slice_buffer_init(&s->frame_storage); + grpc_slice_buffer_init(&s->compressed_data_buffer); + grpc_slice_buffer_init(&s->decompressed_data_buffer); + s->pending_byte_stream = false; + s->decompressed_header_bytes = 0; + GRPC_CLOSURE_INIT(&s->reset_byte_stream, reset_byte_stream, s, + grpc_combiner_scheduler(t->combiner)); + + GRPC_CHTTP2_REF_TRANSPORT(t, "stream"); + + if (server_data) { + s->id = (uint32_t)(uintptr_t)server_data; + *t->accepting_stream = s; + grpc_chttp2_stream_map_add(&t->stream_map, s->id, s); + post_destructive_reclaimer(exec_ctx, t); + } + + s->flow_control.s = s; + GPR_TIMER_END("init_stream", 0); + + return 0; +} + +static void destroy_stream_locked(grpc_exec_ctx *exec_ctx, void *sp, + grpc_error *error) { + grpc_chttp2_stream *s = (grpc_chttp2_stream *)sp; + grpc_chttp2_transport *t = s->t; + + GPR_TIMER_BEGIN("destroy_stream", 0); + + GPR_ASSERT((s->write_closed && s->read_closed) || s->id == 0); + if (s->id != 0) { + GPR_ASSERT(grpc_chttp2_stream_map_find(&t->stream_map, s->id) == NULL); + } + + grpc_slice_buffer_destroy_internal(exec_ctx, + &s->unprocessed_incoming_frames_buffer); + grpc_slice_buffer_destroy_internal(exec_ctx, &s->frame_storage); + grpc_slice_buffer_destroy_internal(exec_ctx, &s->compressed_data_buffer); + grpc_slice_buffer_destroy_internal(exec_ctx, &s->decompressed_data_buffer); + + grpc_chttp2_list_remove_stalled_by_transport(t, s); + grpc_chttp2_list_remove_stalled_by_stream(t, s); + + for (int i = 0; i < STREAM_LIST_COUNT; i++) { + if (s->included[i]) { + gpr_log(GPR_ERROR, "%s stream %d still included in list %d", + t->is_client ? "client" : "server", s->id, i); + abort(); + } + } + + GPR_ASSERT(s->send_initial_metadata_finished == NULL); + GPR_ASSERT(s->fetching_send_message == NULL); + GPR_ASSERT(s->send_trailing_metadata_finished == NULL); + GPR_ASSERT(s->recv_initial_metadata_ready == NULL); + GPR_ASSERT(s->recv_message_ready == NULL); + GPR_ASSERT(s->recv_trailing_metadata_finished == NULL); + grpc_chttp2_data_parser_destroy(exec_ctx, &s->data_parser); + grpc_chttp2_incoming_metadata_buffer_destroy(exec_ctx, + &s->metadata_buffer[0]); + grpc_chttp2_incoming_metadata_buffer_destroy(exec_ctx, + &s->metadata_buffer[1]); + grpc_slice_buffer_destroy_internal(exec_ctx, &s->flow_controlled_buffer); + GRPC_ERROR_UNREF(s->read_closed_error); + GRPC_ERROR_UNREF(s->write_closed_error); + GRPC_ERROR_UNREF(s->byte_stream_error); + + grpc_chttp2_flowctl_destroy_stream(&t->flow_control, &s->flow_control); + + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "stream"); + + GPR_TIMER_END("destroy_stream", 0); + + GRPC_CLOSURE_SCHED(exec_ctx, s->destroy_stream_arg, GRPC_ERROR_NONE); +} + +static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, + grpc_closure *then_schedule_closure) { + GPR_TIMER_BEGIN("destroy_stream", 0); + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + + if (s->stream_compression_ctx != NULL) { + grpc_stream_compression_context_destroy(s->stream_compression_ctx); + s->stream_compression_ctx = NULL; + } + if (s->stream_decompression_ctx != NULL) { + grpc_stream_compression_context_destroy(s->stream_decompression_ctx); + s->stream_decompression_ctx = NULL; + } + + s->destroy_stream_arg = then_schedule_closure; + GRPC_CLOSURE_SCHED( + exec_ctx, GRPC_CLOSURE_INIT(&s->destroy_stream, destroy_stream_locked, s, + grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); + GPR_TIMER_END("destroy_stream", 0); +} + +grpc_chttp2_stream *grpc_chttp2_parsing_lookup_stream(grpc_chttp2_transport *t, + uint32_t id) { + return (grpc_chttp2_stream *)grpc_chttp2_stream_map_find(&t->stream_map, id); +} + +grpc_chttp2_stream *grpc_chttp2_parsing_accept_stream(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + uint32_t id) { + if (t->channel_callback.accept_stream == NULL) { + return NULL; + } + grpc_chttp2_stream *accepting; + GPR_ASSERT(t->accepting_stream == NULL); + t->accepting_stream = &accepting; + t->channel_callback.accept_stream(exec_ctx, + t->channel_callback.accept_stream_user_data, + &t->base, (void *)(uintptr_t)id); + t->accepting_stream = NULL; + return accepting; +} + +/******************************************************************************* + * OUTPUT PROCESSING + */ + +static const char *write_state_name(grpc_chttp2_write_state st) { + switch (st) { + case GRPC_CHTTP2_WRITE_STATE_IDLE: + return "IDLE"; + case GRPC_CHTTP2_WRITE_STATE_WRITING: + return "WRITING"; + case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: + return "WRITING+MORE"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +static void set_write_state(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_write_state st, const char *reason) { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_DEBUG, "W:%p %s state %s -> %s [%s]", t, + t->is_client ? "CLIENT" : "SERVER", + write_state_name(t->write_state), + write_state_name(st), reason)); + t->write_state = st; + if (st == GRPC_CHTTP2_WRITE_STATE_IDLE) { + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &t->run_after_write); + if (t->close_transport_on_writes_finished != NULL) { + grpc_error *err = t->close_transport_on_writes_finished; + t->close_transport_on_writes_finished = NULL; + close_transport_locked(exec_ctx, t, err); + } + } +} + +static void inc_initiate_write_reason( + grpc_exec_ctx *exec_ctx, grpc_chttp2_initiate_write_reason reason) { + switch (reason) { + case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_INITIAL_WRITE(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_START_NEW_STREAM(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_MESSAGE(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_INITIAL_METADATA( + exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_TRAILING_METADATA( + exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RETRY_SEND_PING(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CONTINUE_PINGS(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_GOAWAY_SENT(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_RST_STREAM(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_CLOSE_FROM_API(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_STREAM_FLOW_CONTROL(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL( + exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_SEND_SETTINGS(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_BDP_ESTIMATOR_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_BDP_ESTIMATOR_PING(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_SETTING( + exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FLOW_CONTROL_UNSTALLED_BY_UPDATE( + exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_APPLICATION_PING(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_KEEPALIVE_PING(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_TRANSPORT_FLOW_CONTROL_UNSTALLED( + exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_PING_RESPONSE(exec_ctx); + break; + case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM: + GRPC_STATS_INC_HTTP2_INITIATE_WRITE_DUE_TO_FORCE_RST_STREAM(exec_ctx); + break; + } +} + +void grpc_chttp2_initiate_write(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_initiate_write_reason reason) { + GPR_TIMER_BEGIN("grpc_chttp2_initiate_write", 0); + + switch (t->write_state) { + case GRPC_CHTTP2_WRITE_STATE_IDLE: + inc_initiate_write_reason(exec_ctx, reason); + set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING, + grpc_chttp2_initiate_write_reason_string(reason)); + t->is_first_write_in_batch = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "writing"); + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&t->write_action_begin_locked, + write_action_begin_locked, t, + grpc_combiner_finally_scheduler(t->combiner)), + GRPC_ERROR_NONE); + break; + case GRPC_CHTTP2_WRITE_STATE_WRITING: + set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE, + grpc_chttp2_initiate_write_reason_string(reason)); + break; + case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: + break; + } + GPR_TIMER_END("grpc_chttp2_initiate_write", 0); +} + +void grpc_chttp2_mark_stream_writable(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + if (!t->closed && grpc_chttp2_list_add_writable_stream(t, s)) { + GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:become"); + } +} + +static grpc_closure_scheduler *write_scheduler(grpc_chttp2_transport *t, + bool early_results_scheduled, + bool partial_write) { + /* if it's not the first write in a batch, always offload to the executor: + we'll probably end up queuing against the kernel anyway, so we'll likely + get better latency overall if we switch writing work elsewhere and continue + with application work above */ + if (!t->is_first_write_in_batch) { + return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + } + /* equivalently, if it's a partial write, we *know* we're going to be taking a + thread jump to write it because of the above, may as well do so + immediately */ + if (partial_write) { + return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + } + switch (t->opt_target) { + case GRPC_CHTTP2_OPTIMIZE_FOR_THROUGHPUT: + /* executor gives us the largest probability of being able to batch a + * write with others on this transport */ + return grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + case GRPC_CHTTP2_OPTIMIZE_FOR_LATENCY: + return grpc_schedule_on_exec_ctx; + } + GPR_UNREACHABLE_CODE(return NULL); +} + +#define WRITE_STATE_TUPLE_TO_INT(p, i) (2 * (int)(p) + (int)(i)) +static const char *begin_writing_desc(bool partial, bool inlined) { + switch (WRITE_STATE_TUPLE_TO_INT(partial, inlined)) { + case WRITE_STATE_TUPLE_TO_INT(false, false): + return "begin write in background"; + case WRITE_STATE_TUPLE_TO_INT(false, true): + return "begin write in current thread"; + case WRITE_STATE_TUPLE_TO_INT(true, false): + return "begin partial write in background"; + case WRITE_STATE_TUPLE_TO_INT(true, true): + return "begin partial write in current thread"; + } + GPR_UNREACHABLE_CODE(return "bad state tuple"); +} + +static void write_action_begin_locked(grpc_exec_ctx *exec_ctx, void *gt, + grpc_error *error_ignored) { + GPR_TIMER_BEGIN("write_action_begin_locked", 0); + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + GPR_ASSERT(t->write_state != GRPC_CHTTP2_WRITE_STATE_IDLE); + grpc_chttp2_begin_write_result r; + if (t->closed) { + r.writing = false; + } else { + r = grpc_chttp2_begin_write(exec_ctx, t); + } + if (r.writing) { + if (r.partial) { + GRPC_STATS_INC_HTTP2_PARTIAL_WRITES(exec_ctx); + } + if (!t->is_first_write_in_batch) { + GRPC_STATS_INC_HTTP2_WRITES_CONTINUED(exec_ctx); + } + grpc_closure_scheduler *scheduler = + write_scheduler(t, r.early_results_scheduled, r.partial); + if (scheduler != grpc_schedule_on_exec_ctx) { + GRPC_STATS_INC_HTTP2_WRITES_OFFLOADED(exec_ctx); + } + set_write_state( + exec_ctx, t, r.partial ? GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE + : GRPC_CHTTP2_WRITE_STATE_WRITING, + begin_writing_desc(r.partial, scheduler == grpc_schedule_on_exec_ctx)); + GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_INIT(&t->write_action, + write_action, t, scheduler), + GRPC_ERROR_NONE); + } else { + set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE, + "begin writing nothing"); + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing"); + } + GPR_TIMER_END("write_action_begin_locked", 0); +} + +static void write_action(grpc_exec_ctx *exec_ctx, void *gt, grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + GPR_TIMER_BEGIN("write_action", 0); + grpc_endpoint_write( + exec_ctx, t->ep, &t->outbuf, + GRPC_CLOSURE_INIT(&t->write_action_end_locked, write_action_end_locked, t, + grpc_combiner_scheduler(t->combiner))); + GPR_TIMER_END("write_action", 0); +} + +static void write_action_end_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + GPR_TIMER_BEGIN("terminate_writing_with_lock", 0); + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + + if (error != GRPC_ERROR_NONE) { + close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error)); + } + + if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED) { + t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SENT; + if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + close_transport_locked( + exec_ctx, t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("goaway sent")); + } + } + + switch (t->write_state) { + case GRPC_CHTTP2_WRITE_STATE_IDLE: + GPR_UNREACHABLE_CODE(break); + case GRPC_CHTTP2_WRITE_STATE_WRITING: + GPR_TIMER_MARK("state=writing", 0); + set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_IDLE, + "finish writing"); + break; + case GRPC_CHTTP2_WRITE_STATE_WRITING_WITH_MORE: + GPR_TIMER_MARK("state=writing_stale_no_poller", 0); + set_write_state(exec_ctx, t, GRPC_CHTTP2_WRITE_STATE_WRITING, + "continue writing"); + t->is_first_write_in_batch = false; + GRPC_CHTTP2_REF_TRANSPORT(t, "writing"); + GRPC_CLOSURE_RUN( + exec_ctx, + GRPC_CLOSURE_INIT(&t->write_action_begin_locked, + write_action_begin_locked, t, + grpc_combiner_finally_scheduler(t->combiner)), + GRPC_ERROR_NONE); + break; + } + + grpc_chttp2_end_write(exec_ctx, t, GRPC_ERROR_REF(error)); + + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "writing"); + GPR_TIMER_END("terminate_writing_with_lock", 0); +} + +// Dirties an HTTP2 setting to be sent out next time a writing path occurs. +// If the change needs to occur immediately, manually initiate a write. +static void queue_setting_update(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_setting_id id, uint32_t value) { + const grpc_chttp2_setting_parameters *sp = + &grpc_chttp2_settings_parameters[id]; + uint32_t use_value = GPR_CLAMP(value, sp->min_value, sp->max_value); + if (use_value != value) { + gpr_log(GPR_INFO, "Requested parameter %s clamped from %d to %d", sp->name, + value, use_value); + } + if (use_value != t->settings[GRPC_LOCAL_SETTINGS][id]) { + t->settings[GRPC_LOCAL_SETTINGS][id] = use_value; + t->dirtied_local_settings = 1; + } +} + +void grpc_chttp2_add_incoming_goaway(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + uint32_t goaway_error, + grpc_slice goaway_text) { + // GRPC_CHTTP2_IF_TRACING( + // gpr_log(GPR_DEBUG, "got goaway [%d]: %s", goaway_error, msg)); + t->seen_goaway = 1; + + /* When a client receives a GOAWAY with error code ENHANCE_YOUR_CALM and debug + * data equal to "too_many_pings", it should log the occurrence at a log level + * that is enabled by default and double the configured KEEPALIVE_TIME used + * for new connections on that channel. */ + if (t->is_client && goaway_error == GRPC_HTTP2_ENHANCE_YOUR_CALM && + grpc_slice_str_cmp(goaway_text, "too_many_pings") == 0) { + gpr_log(GPR_ERROR, + "Received a GOAWAY with error code ENHANCE_YOUR_CALM and debug " + "data equal to \"too_many_pings\""); + double current_keepalive_time_ms = + gpr_timespec_to_micros(t->keepalive_time) / 1000; + t->keepalive_time = + current_keepalive_time_ms > INT_MAX / KEEPALIVE_TIME_BACKOFF_MULTIPLIER + ? gpr_inf_future(GPR_TIMESPAN) + : gpr_time_from_millis((int64_t)(current_keepalive_time_ms * + KEEPALIVE_TIME_BACKOFF_MULTIPLIER), + GPR_TIMESPAN); + } + + /* lie: use transient failure from the transport to indicate goaway has been + * received */ + connectivity_state_set( + exec_ctx, t, GRPC_CHANNEL_TRANSIENT_FAILURE, + grpc_error_set_str( + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("GOAWAY received"), + GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)goaway_error), + GRPC_ERROR_STR_RAW_BYTES, goaway_text), + "got_goaway"); +} + +static void maybe_start_some_streams(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_chttp2_stream *s; + /* start streams where we have free grpc_chttp2_stream ids and free + * concurrency */ + while (t->next_stream_id <= MAX_CLIENT_STREAM_ID && + grpc_chttp2_stream_map_size(&t->stream_map) < + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] && + grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) { + /* safe since we can't (legally) be parsing this stream yet */ + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_DEBUG, "HTTP:%s: Allocating new grpc_chttp2_stream %p to id %d", + t->is_client ? "CLI" : "SVR", s, t->next_stream_id)); + + GPR_ASSERT(s->id == 0); + s->id = t->next_stream_id; + t->next_stream_id += 2; + + if (t->next_stream_id >= MAX_CLIENT_STREAM_ID) { + connectivity_state_set( + exec_ctx, t, GRPC_CHANNEL_TRANSIENT_FAILURE, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"), + "no_more_stream_ids"); + } + + grpc_chttp2_stream_map_add(&t->stream_map, s->id, s); + post_destructive_reclaimer(exec_ctx, t); + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM); + } + /* cancel out streams that will never be started */ + while (t->next_stream_id >= MAX_CLIENT_STREAM_ID && + grpc_chttp2_list_pop_waiting_for_concurrency(t, &s)) { + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream IDs exhausted"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } +} + +/* Flag that this closure barrier wants stats to be updated before finishing */ +#define CLOSURE_BARRIER_STATS_BIT (1 << 0) +/* Flag that this closure barrier may be covering a write in a pollset, and so + we should not complete this closure until we can prove that the write got + scheduled */ +#define CLOSURE_BARRIER_MAY_COVER_WRITE (1 << 1) +/* First bit of the reference count, stored in the high order bits (with the low + bits being used for flags defined above) */ +#define CLOSURE_BARRIER_FIRST_REF_BIT (1 << 16) + +static grpc_closure *add_closure_barrier(grpc_closure *closure) { + closure->next_data.scratch += CLOSURE_BARRIER_FIRST_REF_BIT; + return closure; +} + +static void null_then_run_closure(grpc_exec_ctx *exec_ctx, + grpc_closure **closure, grpc_error *error) { + grpc_closure *c = *closure; + *closure = NULL; + GRPC_CLOSURE_RUN(exec_ctx, c, error); +} + +void grpc_chttp2_complete_closure_step(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_closure **pclosure, + grpc_error *error, const char *desc) { + grpc_closure *closure = *pclosure; + *pclosure = NULL; + if (closure == NULL) { + GRPC_ERROR_UNREF(error); + return; + } + closure->next_data.scratch -= CLOSURE_BARRIER_FIRST_REF_BIT; + if (GRPC_TRACER_ON(grpc_http_trace)) { + const char *errstr = grpc_error_string(error); + gpr_log( + GPR_DEBUG, + "complete_closure_step: t=%p %p refs=%d flags=0x%04x desc=%s err=%s " + "write_state=%s", + t, closure, + (int)(closure->next_data.scratch / CLOSURE_BARRIER_FIRST_REF_BIT), + (int)(closure->next_data.scratch % CLOSURE_BARRIER_FIRST_REF_BIT), desc, + errstr, write_state_name(t->write_state)); + } + if (error != GRPC_ERROR_NONE) { + if (closure->error_data.error == GRPC_ERROR_NONE) { + closure->error_data.error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Error in HTTP transport completing operation"); + closure->error_data.error = grpc_error_set_str( + closure->error_data.error, GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(t->peer_string)); + } + closure->error_data.error = + grpc_error_add_child(closure->error_data.error, error); + } + if (closure->next_data.scratch < CLOSURE_BARRIER_FIRST_REF_BIT) { + if (closure->next_data.scratch & CLOSURE_BARRIER_STATS_BIT) { + grpc_transport_move_stats(&s->stats, s->collecting_stats); + s->collecting_stats = NULL; + } + if ((t->write_state == GRPC_CHTTP2_WRITE_STATE_IDLE) || + !(closure->next_data.scratch & CLOSURE_BARRIER_MAY_COVER_WRITE)) { + GRPC_CLOSURE_RUN(exec_ctx, closure, closure->error_data.error); + } else { + grpc_closure_list_append(&t->run_after_write, closure, + closure->error_data.error); + } + } +} + +static bool contains_non_ok_status(grpc_metadata_batch *batch) { + if (batch->idx.named.grpc_status != NULL) { + return !grpc_mdelem_eq(batch->idx.named.grpc_status->md, + GRPC_MDELEM_GRPC_STATUS_0); + } + return false; +} + +static void maybe_become_writable_due_to_send_msg(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + if (s->id != 0 && (!s->write_buffering || + s->flow_controlled_buffer.length > t->write_buffer_size)) { + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE); + } +} + +static void add_fetched_slice_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + s->fetched_send_message_length += + (uint32_t)GRPC_SLICE_LENGTH(s->fetching_slice); + grpc_slice_buffer_add(&s->flow_controlled_buffer, s->fetching_slice); + maybe_become_writable_due_to_send_msg(exec_ctx, t, s); +} + +static void continue_fetching_send_locked(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + for (;;) { + if (s->fetching_send_message == NULL) { + /* Stream was cancelled before message fetch completed */ + abort(); /* TODO(ctiller): what cleanup here? */ + return; /* early out */ + } + if (s->fetched_send_message_length == s->fetching_send_message->length) { + grpc_byte_stream_destroy(exec_ctx, s->fetching_send_message); + int64_t notify_offset = s->next_message_end_offset; + if (notify_offset <= s->flow_controlled_bytes_written) { + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_NONE, + "fetching_send_message_finished"); + } else { + grpc_chttp2_write_cb *cb = t->write_cb_pool; + if (cb == NULL) { + cb = (grpc_chttp2_write_cb *)gpr_malloc(sizeof(*cb)); + } else { + t->write_cb_pool = cb->next; + } + cb->call_at_byte = notify_offset; + cb->closure = s->fetching_send_message_finished; + s->fetching_send_message_finished = NULL; + grpc_chttp2_write_cb **list = + s->fetching_send_message->flags & GRPC_WRITE_THROUGH + ? &s->on_write_finished_cbs + : &s->on_flow_controlled_cbs; + cb->next = *list; + *list = cb; + } + s->fetching_send_message = NULL; + return; /* early out */ + } else if (grpc_byte_stream_next(exec_ctx, s->fetching_send_message, + UINT32_MAX, &s->complete_fetch_locked)) { + grpc_error *error = grpc_byte_stream_pull( + exec_ctx, s->fetching_send_message, &s->fetching_slice); + if (error != GRPC_ERROR_NONE) { + grpc_byte_stream_destroy(exec_ctx, s->fetching_send_message); + grpc_chttp2_cancel_stream(exec_ctx, t, s, error); + } else { + add_fetched_slice_locked(exec_ctx, t, s); + } + } + } +} + +static void complete_fetch_locked(grpc_exec_ctx *exec_ctx, void *gs, + grpc_error *error) { + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + grpc_chttp2_transport *t = s->t; + if (error == GRPC_ERROR_NONE) { + error = grpc_byte_stream_pull(exec_ctx, s->fetching_send_message, + &s->fetching_slice); + if (error == GRPC_ERROR_NONE) { + add_fetched_slice_locked(exec_ctx, t, s); + continue_fetching_send_locked(exec_ctx, t, s); + } + } + if (error != GRPC_ERROR_NONE) { + grpc_byte_stream_destroy(exec_ctx, s->fetching_send_message); + grpc_chttp2_cancel_stream(exec_ctx, t, s, error); + } +} + +static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {} + +static void log_metadata(const grpc_metadata_batch *md_batch, uint32_t id, + bool is_client, bool is_initial) { + for (grpc_linked_mdelem *md = md_batch->list.head; md != NULL; + md = md->next) { + char *key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); + char *value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); + gpr_log(GPR_INFO, "HTTP:%d:%s:%s: %s: %s", id, is_initial ? "HDR" : "TRL", + is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } +} + +static void perform_stream_op_locked(grpc_exec_ctx *exec_ctx, void *stream_op, + grpc_error *error_ignored) { + GPR_TIMER_BEGIN("perform_stream_op_locked", 0); + + grpc_transport_stream_op_batch *op = + (grpc_transport_stream_op_batch *)stream_op; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)op->handler_private.extra_arg; + grpc_transport_stream_op_batch_payload *op_payload = op->payload; + grpc_chttp2_transport *t = s->t; + + GRPC_STATS_INC_HTTP2_OP_BATCHES(exec_ctx); + + if (GRPC_TRACER_ON(grpc_http_trace)) { + char *str = grpc_transport_stream_op_batch_string(op); + gpr_log(GPR_DEBUG, "perform_stream_op_locked: %s; on_complete = %p", str, + op->on_complete); + gpr_free(str); + if (op->send_initial_metadata) { + log_metadata(op_payload->send_initial_metadata.send_initial_metadata, + s->id, t->is_client, true); + } + if (op->send_trailing_metadata) { + log_metadata(op_payload->send_trailing_metadata.send_trailing_metadata, + s->id, t->is_client, false); + } + } + + grpc_closure *on_complete = op->on_complete; + if (on_complete == NULL) { + on_complete = + GRPC_CLOSURE_CREATE(do_nothing, NULL, grpc_schedule_on_exec_ctx); + } + + /* use final_data as a barrier until enqueue time; the inital counter is + dropped at the end of this function */ + on_complete->next_data.scratch = CLOSURE_BARRIER_FIRST_REF_BIT; + on_complete->error_data.error = GRPC_ERROR_NONE; + + if (op->collect_stats) { + GPR_ASSERT(s->collecting_stats == NULL); + s->collecting_stats = op_payload->collect_stats.collect_stats; + on_complete->next_data.scratch |= CLOSURE_BARRIER_STATS_BIT; + } + + if (op->cancel_stream) { + GRPC_STATS_INC_HTTP2_OP_CANCEL(exec_ctx); + grpc_chttp2_cancel_stream(exec_ctx, t, s, + op_payload->cancel_stream.cancel_error); + } + + if (op->send_initial_metadata) { + GRPC_STATS_INC_HTTP2_OP_SEND_INITIAL_METADATA(exec_ctx); + GPR_ASSERT(s->send_initial_metadata_finished == NULL); + on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; + + /* Identify stream compression */ + if (op_payload->send_initial_metadata.send_initial_metadata->idx.named + .content_encoding == NULL || + grpc_stream_compression_method_parse( + GRPC_MDVALUE( + op_payload->send_initial_metadata.send_initial_metadata->idx + .named.content_encoding->md), + true, &s->stream_compression_method) == 0) { + s->stream_compression_method = GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS; + } + + s->send_initial_metadata_finished = add_closure_barrier(on_complete); + s->send_initial_metadata = + op_payload->send_initial_metadata.send_initial_metadata; + const size_t metadata_size = + grpc_metadata_batch_size(s->send_initial_metadata); + const size_t metadata_peer_limit = + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (t->is_client) { + s->deadline = + gpr_time_min(s->deadline, s->send_initial_metadata->deadline); + } + if (metadata_size > metadata_peer_limit) { + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int( + grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "to-be-sent initial metadata size " + "exceeds peer limit"), + GRPC_ERROR_INT_SIZE, + (intptr_t)metadata_size), + GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); + } else { + if (contains_non_ok_status(s->send_initial_metadata)) { + s->seen_error = true; + } + if (!s->write_closed) { + if (t->is_client) { + if (!t->closed) { + GPR_ASSERT(s->id == 0); + grpc_chttp2_list_add_waiting_for_concurrency(t, s); + maybe_start_some_streams(exec_ctx, t); + } else { + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } + } else { + GPR_ASSERT(s->id != 0); + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + if (!(op->send_message && + (op->payload->send_message.send_message->flags & + GRPC_WRITE_BUFFER_HINT))) { + grpc_chttp2_initiate_write( + exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA); + } + } + } else { + s->send_initial_metadata = NULL; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->send_initial_metadata_finished, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Attempt to send initial metadata after stream was closed", + &s->write_closed_error, 1), + "send_initial_metadata_finished"); + } + } + if (op_payload->send_initial_metadata.peer_string != NULL) { + gpr_atm_rel_store(op_payload->send_initial_metadata.peer_string, + (gpr_atm)gpr_strdup(t->peer_string)); + } + } + + if (op->send_message) { + GRPC_STATS_INC_HTTP2_OP_SEND_MESSAGE(exec_ctx); + GRPC_STATS_INC_HTTP2_SEND_MESSAGE_SIZE( + exec_ctx, op->payload->send_message.send_message->length); + on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; + s->fetching_send_message_finished = add_closure_barrier(op->on_complete); + if (s->write_closed) { + // Return an error unless the client has already received trailing + // metadata from the server, since an application using a + // streaming call might send another message before getting a + // recv_message failure, breaking out of its loop, and then + // starting recv_trailing_metadata. + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->fetching_send_message_finished, + t->is_client && s->received_trailing_metadata + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Attempt to send message after stream was closed", + &s->write_closed_error, 1), + "fetching_send_message_finished"); + } else { + GPR_ASSERT(s->fetching_send_message == NULL); + uint8_t *frame_hdr = grpc_slice_buffer_tiny_add( + &s->flow_controlled_buffer, GRPC_HEADER_SIZE_IN_BYTES); + uint32_t flags = op_payload->send_message.send_message->flags; + frame_hdr[0] = (flags & GRPC_WRITE_INTERNAL_COMPRESS) != 0; + size_t len = op_payload->send_message.send_message->length; + frame_hdr[1] = (uint8_t)(len >> 24); + frame_hdr[2] = (uint8_t)(len >> 16); + frame_hdr[3] = (uint8_t)(len >> 8); + frame_hdr[4] = (uint8_t)(len); + s->fetching_send_message = op_payload->send_message.send_message; + s->fetched_send_message_length = 0; + s->next_message_end_offset = s->flow_controlled_bytes_written + + (int64_t)s->flow_controlled_buffer.length + + (int64_t)len; + if (flags & GRPC_WRITE_BUFFER_HINT) { + s->next_message_end_offset -= t->write_buffer_size; + s->write_buffering = true; + } else { + s->write_buffering = false; + } + continue_fetching_send_locked(exec_ctx, t, s); + maybe_become_writable_due_to_send_msg(exec_ctx, t, s); + } + } + + if (op->send_trailing_metadata) { + GRPC_STATS_INC_HTTP2_OP_SEND_TRAILING_METADATA(exec_ctx); + GPR_ASSERT(s->send_trailing_metadata_finished == NULL); + on_complete->next_data.scratch |= CLOSURE_BARRIER_MAY_COVER_WRITE; + s->send_trailing_metadata_finished = add_closure_barrier(on_complete); + s->send_trailing_metadata = + op_payload->send_trailing_metadata.send_trailing_metadata; + s->write_buffering = false; + const size_t metadata_size = + grpc_metadata_batch_size(s->send_trailing_metadata); + const size_t metadata_peer_limit = + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (metadata_size > metadata_peer_limit) { + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int( + grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "to-be-sent trailing metadata size " + "exceeds peer limit"), + GRPC_ERROR_INT_SIZE, + (intptr_t)metadata_size), + GRPC_ERROR_INT_LIMIT, (intptr_t)metadata_peer_limit), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); + } else { + if (contains_non_ok_status(s->send_trailing_metadata)) { + s->seen_error = true; + } + if (s->write_closed) { + s->send_trailing_metadata = NULL; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->send_trailing_metadata_finished, + grpc_metadata_batch_is_empty( + op->payload->send_trailing_metadata.send_trailing_metadata) + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Attempt to send trailing metadata after " + "stream was closed"), + "send_trailing_metadata_finished"); + } else if (s->id != 0) { + /* TODO(ctiller): check if there's flow control for any outstanding + bytes before going writable */ + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + grpc_chttp2_initiate_write( + exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA); + } + } + } + + if (op->recv_initial_metadata) { + GRPC_STATS_INC_HTTP2_OP_RECV_INITIAL_METADATA(exec_ctx); + GPR_ASSERT(s->recv_initial_metadata_ready == NULL); + s->recv_initial_metadata_ready = + op_payload->recv_initial_metadata.recv_initial_metadata_ready; + s->recv_initial_metadata = + op_payload->recv_initial_metadata.recv_initial_metadata; + s->trailing_metadata_available = + op_payload->recv_initial_metadata.trailing_metadata_available; + if (op_payload->recv_initial_metadata.peer_string != NULL) { + gpr_atm_rel_store(op_payload->recv_initial_metadata.peer_string, + (gpr_atm)gpr_strdup(t->peer_string)); + } + grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); + } + + if (op->recv_message) { + GRPC_STATS_INC_HTTP2_OP_RECV_MESSAGE(exec_ctx); + size_t already_received; + GPR_ASSERT(s->recv_message_ready == NULL); + GPR_ASSERT(!s->pending_byte_stream); + s->recv_message_ready = op_payload->recv_message.recv_message_ready; + s->recv_message = op_payload->recv_message.recv_message; + if (s->id != 0) { + if (!s->read_closed) { + already_received = s->frame_storage.length; + grpc_chttp2_flowctl_incoming_bs_update( + &t->flow_control, &s->flow_control, GRPC_HEADER_SIZE_IN_BYTES, + already_received); + grpc_chttp2_act_on_flowctl_action( + exec_ctx, + grpc_chttp2_flowctl_get_action(&t->flow_control, &s->flow_control), + t, s); + } + } + grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); + } + + if (op->recv_trailing_metadata) { + GRPC_STATS_INC_HTTP2_OP_RECV_TRAILING_METADATA(exec_ctx); + GPR_ASSERT(s->recv_trailing_metadata_finished == NULL); + s->recv_trailing_metadata_finished = add_closure_barrier(on_complete); + s->recv_trailing_metadata = + op_payload->recv_trailing_metadata.recv_trailing_metadata; + s->final_metadata_requested = true; + grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); + } + + grpc_chttp2_complete_closure_step(exec_ctx, t, s, &on_complete, + GRPC_ERROR_NONE, "op->on_complete"); + + GPR_TIMER_END("perform_stream_op_locked", 0); + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "perform_stream_op"); +} + +static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, + grpc_transport_stream_op_batch *op) { + GPR_TIMER_BEGIN("perform_stream_op", 0); + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)gs; + + if (!t->is_client) { + if (op->send_initial_metadata) { + gpr_timespec deadline = + op->payload->send_initial_metadata.send_initial_metadata->deadline; + GPR_ASSERT(0 == + gpr_time_cmp(gpr_inf_future(deadline.clock_type), deadline)); + } + if (op->send_trailing_metadata) { + gpr_timespec deadline = + op->payload->send_trailing_metadata.send_trailing_metadata->deadline; + GPR_ASSERT(0 == + gpr_time_cmp(gpr_inf_future(deadline.clock_type), deadline)); + } + } + + if (GRPC_TRACER_ON(grpc_http_trace)) { + char *str = grpc_transport_stream_op_batch_string(op); + gpr_log(GPR_DEBUG, "perform_stream_op[s=%p]: %s", s, str); + gpr_free(str); + } + + op->handler_private.extra_arg = gs; + GRPC_CHTTP2_STREAM_REF(s, "perform_stream_op"); + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&op->handler_private.closure, perform_stream_op_locked, + op, grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); + GPR_TIMER_END("perform_stream_op", 0); +} + +static void cancel_pings(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_error *error) { + /* callback remaining pings: they're not allowed to call into the transpot, + and maybe they hold resources that need to be freed */ + for (size_t i = 0; i < GRPC_CHTTP2_PING_TYPE_COUNT; i++) { + grpc_chttp2_ping_queue *pq = &t->ping_queues[i]; + for (size_t j = 0; j < GRPC_CHTTP2_PCL_COUNT; j++) { + grpc_closure_list_fail_all(&pq->lists[j], GRPC_ERROR_REF(error)); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pq->lists[j]); + } + } + GRPC_ERROR_UNREF(error); +} + +static void send_ping_locked( + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_ping_type ping_type, grpc_closure *on_initiate, + grpc_closure *on_ack, + grpc_chttp2_initiate_write_reason initiate_write_reason) { + grpc_chttp2_ping_queue *pq = &t->ping_queues[ping_type]; + grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_INITIATE], on_initiate, + GRPC_ERROR_NONE); + if (grpc_closure_list_append(&pq->lists[GRPC_CHTTP2_PCL_NEXT], on_ack, + GRPC_ERROR_NONE)) { + grpc_chttp2_initiate_write(exec_ctx, t, initiate_write_reason); + } +} + +static void retry_initiate_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + t->ping_state.is_delayed_ping_timer_set = false; + if (error == GRPC_ERROR_NONE) { + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING); + } +} + +void grpc_chttp2_ack_ping(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + uint64_t id) { + grpc_chttp2_ping_queue *pq = + &t->ping_queues[id % GRPC_CHTTP2_PING_TYPE_COUNT]; + if (pq->inflight_id != id) { + char *from = grpc_endpoint_get_peer(t->ep); + gpr_log(GPR_DEBUG, "Unknown ping response from %s: %" PRIx64, from, id); + gpr_free(from); + return; + } + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]); + if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) { + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS); + } +} + +static void send_goaway(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_error *error) { + t->sent_goaway_state = GRPC_CHTTP2_GOAWAY_SEND_SCHEDULED; + grpc_http2_error_code http_error; + grpc_slice slice; + grpc_error_get_status(error, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL, + &slice, &http_error); + grpc_chttp2_goaway_append(t->last_new_stream_id, (uint32_t)http_error, + grpc_slice_ref_internal(slice), &t->qbuf); + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT); + GRPC_ERROR_UNREF(error); +} + +void grpc_chttp2_add_ping_strike(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + gpr_log(GPR_DEBUG, "PING strike"); + if (++t->ping_recv_state.ping_strikes > t->ping_policy.max_ping_strikes && + t->ping_policy.max_ping_strikes != 0) { + send_goaway(exec_ctx, t, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("too_many_pings"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM)); + /*The transport will be closed after the write is done */ + close_transport_locked( + exec_ctx, t, GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many pings")); + } +} + +static void perform_transport_op_locked(grpc_exec_ctx *exec_ctx, + void *stream_op, + grpc_error *error_ignored) { + grpc_transport_op *op = (grpc_transport_op *)stream_op; + grpc_chttp2_transport *t = + (grpc_chttp2_transport *)op->handler_private.extra_arg; + grpc_error *close_transport = op->disconnect_with_error; + + if (op->goaway_error) { + send_goaway(exec_ctx, t, op->goaway_error); + } + + if (op->set_accept_stream) { + t->channel_callback.accept_stream = op->set_accept_stream_fn; + t->channel_callback.accept_stream_user_data = + op->set_accept_stream_user_data; + } + + if (op->bind_pollset) { + grpc_endpoint_add_to_pollset(exec_ctx, t->ep, op->bind_pollset); + } + + if (op->bind_pollset_set) { + grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, op->bind_pollset_set); + } + + if (op->send_ping) { + send_ping_locked(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE, NULL, + op->send_ping, + GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING); + } + + if (op->on_connectivity_state_change != NULL) { + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &t->channel_callback.state_tracker, op->connectivity_state, + op->on_connectivity_state_change); + } + + if (close_transport != GRPC_ERROR_NONE) { + close_transport_locked(exec_ctx, t, close_transport); + } + + GRPC_CLOSURE_RUN(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); + + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "transport_op"); +} + +static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_transport_op *op) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + char *msg = grpc_transport_op_string(op); + gpr_free(msg); + op->handler_private.extra_arg = gt; + GRPC_CHTTP2_REF_TRANSPORT(t, "transport_op"); + GRPC_CLOSURE_SCHED(exec_ctx, + GRPC_CLOSURE_INIT(&op->handler_private.closure, + perform_transport_op_locked, op, + grpc_combiner_scheduler(t->combiner)), + GRPC_ERROR_NONE); +} + +/******************************************************************************* + * INPUT PROCESSING - GENERAL + */ + +void grpc_chttp2_maybe_complete_recv_initial_metadata(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + if (s->recv_initial_metadata_ready != NULL && + s->published_metadata[0] != GRPC_METADATA_NOT_PUBLISHED) { + if (s->seen_error) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); + if (!s->pending_byte_stream) { + grpc_slice_buffer_reset_and_unref_internal( + exec_ctx, &s->unprocessed_incoming_frames_buffer); + } + } + grpc_chttp2_incoming_metadata_buffer_publish( + exec_ctx, &s->metadata_buffer[0], s->recv_initial_metadata); + null_then_run_closure(exec_ctx, &s->recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } +} + +void grpc_chttp2_maybe_complete_recv_message(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + grpc_error *error = GRPC_ERROR_NONE; + if (s->recv_message_ready != NULL) { + *s->recv_message = NULL; + if (s->final_metadata_requested && s->seen_error) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); + if (!s->pending_byte_stream) { + grpc_slice_buffer_reset_and_unref_internal( + exec_ctx, &s->unprocessed_incoming_frames_buffer); + } + } + if (!s->pending_byte_stream) { + while (s->unprocessed_incoming_frames_buffer.length > 0 || + s->frame_storage.length > 0) { + if (s->unprocessed_incoming_frames_buffer.length == 0) { + grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer, + &s->frame_storage); + s->unprocessed_incoming_frames_decompressed = false; + } + if (!s->unprocessed_incoming_frames_decompressed) { + GPR_ASSERT(s->decompressed_data_buffer.length == 0); + bool end_of_context; + if (!s->stream_decompression_ctx) { + s->stream_decompression_ctx = + grpc_stream_compression_context_create( + s->stream_decompression_method); + } + if (!grpc_stream_decompress( + s->stream_decompression_ctx, + &s->unprocessed_incoming_frames_buffer, + &s->decompressed_data_buffer, NULL, + GRPC_HEADER_SIZE_IN_BYTES - s->decompressed_header_bytes, + &end_of_context)) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + &s->frame_storage); + grpc_slice_buffer_reset_and_unref_internal( + exec_ctx, &s->unprocessed_incoming_frames_buffer); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Stream decompression error."); + } else { + s->decompressed_header_bytes += s->decompressed_data_buffer.length; + if (s->decompressed_header_bytes == GRPC_HEADER_SIZE_IN_BYTES) { + s->decompressed_header_bytes = 0; + } + error = grpc_deframe_unprocessed_incoming_frames( + exec_ctx, &s->data_parser, s, &s->decompressed_data_buffer, + NULL, s->recv_message); + if (end_of_context) { + grpc_stream_compression_context_destroy( + s->stream_decompression_ctx); + s->stream_decompression_ctx = NULL; + } + } + } else { + error = grpc_deframe_unprocessed_incoming_frames( + exec_ctx, &s->data_parser, s, + &s->unprocessed_incoming_frames_buffer, NULL, s->recv_message); + } + if (error != GRPC_ERROR_NONE) { + s->seen_error = true; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + &s->frame_storage); + grpc_slice_buffer_reset_and_unref_internal( + exec_ctx, &s->unprocessed_incoming_frames_buffer); + break; + } else if (*s->recv_message != NULL) { + break; + } + } + } + if (error == GRPC_ERROR_NONE && *s->recv_message != NULL) { + null_then_run_closure(exec_ctx, &s->recv_message_ready, GRPC_ERROR_NONE); + } else if (s->published_metadata[1] != GRPC_METADATA_NOT_PUBLISHED) { + *s->recv_message = NULL; + null_then_run_closure(exec_ctx, &s->recv_message_ready, GRPC_ERROR_NONE); + } + GRPC_ERROR_UNREF(error); + } +} + +void grpc_chttp2_maybe_complete_recv_trailing_metadata(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); + if (s->recv_trailing_metadata_finished != NULL && s->read_closed && + s->write_closed) { + if (s->seen_error) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); + if (!s->pending_byte_stream) { + grpc_slice_buffer_reset_and_unref_internal( + exec_ctx, &s->unprocessed_incoming_frames_buffer); + } + } + bool pending_data = s->pending_byte_stream || + s->unprocessed_incoming_frames_buffer.length > 0; + if (s->read_closed && s->frame_storage.length > 0 && !pending_data && + !s->seen_error && s->recv_trailing_metadata_finished != NULL) { + /* Maybe some SYNC_FLUSH data is left in frame_storage. Consume them and + * maybe decompress the next 5 bytes in the stream. */ + bool end_of_context; + if (!s->stream_decompression_ctx) { + s->stream_decompression_ctx = grpc_stream_compression_context_create( + s->stream_decompression_method); + } + if (!grpc_stream_decompress(s->stream_decompression_ctx, + &s->frame_storage, + &s->unprocessed_incoming_frames_buffer, NULL, + GRPC_HEADER_SIZE_IN_BYTES, &end_of_context)) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &s->frame_storage); + grpc_slice_buffer_reset_and_unref_internal( + exec_ctx, &s->unprocessed_incoming_frames_buffer); + s->seen_error = true; + } else { + if (s->unprocessed_incoming_frames_buffer.length > 0) { + s->unprocessed_incoming_frames_decompressed = true; + pending_data = true; + } + if (end_of_context) { + grpc_stream_compression_context_destroy(s->stream_decompression_ctx); + s->stream_decompression_ctx = NULL; + } + } + } + if (s->read_closed && s->frame_storage.length == 0 && !pending_data && + s->recv_trailing_metadata_finished != NULL) { + grpc_chttp2_incoming_metadata_buffer_publish( + exec_ctx, &s->metadata_buffer[1], s->recv_trailing_metadata); + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->recv_trailing_metadata_finished, GRPC_ERROR_NONE, + "recv_trailing_metadata_finished"); + } + } +} + +static void remove_stream(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + uint32_t id, grpc_error *error) { + grpc_chttp2_stream *s = + (grpc_chttp2_stream *)grpc_chttp2_stream_map_delete(&t->stream_map, id); + GPR_ASSERT(s); + if (t->incoming_stream == s) { + t->incoming_stream = NULL; + grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); + } + if (s->pending_byte_stream) { + if (s->on_next != NULL) { + grpc_chttp2_incoming_byte_stream *bs = s->data_parser.parsing_frame; + if (error == GRPC_ERROR_NONE) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + } + incoming_byte_stream_publish_error(exec_ctx, bs, error); + incoming_byte_stream_unref(exec_ctx, bs); + s->data_parser.parsing_frame = NULL; + } else { + GRPC_ERROR_UNREF(s->byte_stream_error); + s->byte_stream_error = GRPC_ERROR_REF(error); + } + } + + if (grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + post_benign_reclaimer(exec_ctx, t); + if (t->sent_goaway_state == GRPC_CHTTP2_GOAWAY_SENT) { + close_transport_locked( + exec_ctx, t, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Last stream closed after sending GOAWAY", &error, 1)); + } + } + if (grpc_chttp2_list_remove_writable_stream(t, s)) { + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:remove_stream"); + } + + GRPC_ERROR_UNREF(error); + + maybe_start_some_streams(exec_ctx, t); +} + +void grpc_chttp2_cancel_stream(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_chttp2_stream *s, + grpc_error *due_to_error) { + if (!t->is_client && !s->sent_trailing_metadata && + grpc_error_has_clear_grpc_status(due_to_error)) { + close_from_api(exec_ctx, t, s, due_to_error); + return; + } + + if (!s->read_closed || !s->write_closed) { + if (s->id != 0) { + grpc_http2_error_code http_error; + grpc_error_get_status(due_to_error, s->deadline, NULL, NULL, &http_error); + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(s->id, (uint32_t)http_error, + &s->stats.outgoing)); + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM); + } + } + if (due_to_error != GRPC_ERROR_NONE && !s->seen_error) { + s->seen_error = true; + } + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, due_to_error); +} + +void grpc_chttp2_fake_status(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_error *error) { + grpc_status_code status; + grpc_slice slice; + grpc_error_get_status(error, s->deadline, &status, &slice, NULL); + + if (status != GRPC_STATUS_OK) { + s->seen_error = true; + } + /* stream_global->recv_trailing_metadata_finished gives us a + last chance replacement: we've received trailing metadata, + but something more important has become available to signal + to the upper layers - drop what we've got, and then publish + what we want - which is safe because we haven't told anyone + about the metadata yet */ + if (s->published_metadata[1] == GRPC_METADATA_NOT_PUBLISHED || + s->recv_trailing_metadata_finished != NULL) { + char status_string[GPR_LTOA_MIN_BUFSIZE]; + gpr_ltoa(status, status_string); + GRPC_LOG_IF_ERROR("add_status", + grpc_chttp2_incoming_metadata_buffer_replace_or_add( + exec_ctx, &s->metadata_buffer[1], + grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_GRPC_STATUS, + grpc_slice_from_copied_string(status_string)))); + if (!GRPC_SLICE_IS_EMPTY(slice)) { + GRPC_LOG_IF_ERROR( + "add_status_message", + grpc_chttp2_incoming_metadata_buffer_replace_or_add( + exec_ctx, &s->metadata_buffer[1], + grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_MESSAGE, + grpc_slice_ref_internal(slice)))); + } + s->published_metadata[1] = GRPC_METADATA_SYNTHESIZED_FROM_FAKE; + grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); + } + + GRPC_ERROR_UNREF(error); +} + +static void add_error(grpc_error *error, grpc_error **refs, size_t *nrefs) { + if (error == GRPC_ERROR_NONE) return; + for (size_t i = 0; i < *nrefs; i++) { + if (error == refs[i]) { + return; + } + } + refs[*nrefs] = error; + ++*nrefs; +} + +static grpc_error *removal_error(grpc_error *extra_error, grpc_chttp2_stream *s, + const char *master_error_msg) { + grpc_error *refs[3]; + size_t nrefs = 0; + add_error(s->read_closed_error, refs, &nrefs); + add_error(s->write_closed_error, refs, &nrefs); + add_error(extra_error, refs, &nrefs); + grpc_error *error = GRPC_ERROR_NONE; + if (nrefs > 0) { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING(master_error_msg, + refs, nrefs); + } + GRPC_ERROR_UNREF(extra_error); + return error; +} + +static void flush_write_list(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_chttp2_write_cb **list, + grpc_error *error) { + while (*list) { + grpc_chttp2_write_cb *cb = *list; + *list = cb->next; + grpc_chttp2_complete_closure_step(exec_ctx, t, s, &cb->closure, + GRPC_ERROR_REF(error), + "on_write_finished_cb"); + cb->next = t->write_cb_pool; + t->write_cb_pool = cb; + } + GRPC_ERROR_UNREF(error); +} + +void grpc_chttp2_fail_pending_writes(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_error *error) { + error = + removal_error(error, s, "Pending writes failed due to stream closure"); + s->send_initial_metadata = NULL; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->send_initial_metadata_finished, GRPC_ERROR_REF(error), + "send_initial_metadata_finished"); + + s->send_trailing_metadata = NULL; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->send_trailing_metadata_finished, + GRPC_ERROR_REF(error), "send_trailing_metadata_finished"); + + s->fetching_send_message = NULL; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->fetching_send_message_finished, GRPC_ERROR_REF(error), + "fetching_send_message_finished"); + flush_write_list(exec_ctx, t, s, &s->on_write_finished_cbs, + GRPC_ERROR_REF(error)); + flush_write_list(exec_ctx, t, s, &s->on_flow_controlled_cbs, error); +} + +void grpc_chttp2_mark_stream_closed(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, int close_reads, + int close_writes, grpc_error *error) { + if (s->read_closed && s->write_closed) { + /* already closed */ + grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); + GRPC_ERROR_UNREF(error); + return; + } + bool closed_read = false; + bool became_closed = false; + if (close_reads && !s->read_closed) { + s->read_closed_error = GRPC_ERROR_REF(error); + s->read_closed = true; + closed_read = true; + } + if (close_writes && !s->write_closed) { + s->write_closed_error = GRPC_ERROR_REF(error); + s->write_closed = true; + grpc_chttp2_fail_pending_writes(exec_ctx, t, s, GRPC_ERROR_REF(error)); + } + if (s->read_closed && s->write_closed) { + became_closed = true; + grpc_error *overall_error = + removal_error(GRPC_ERROR_REF(error), s, "Stream removed"); + if (s->id != 0) { + remove_stream(exec_ctx, t, s->id, GRPC_ERROR_REF(overall_error)); + } else { + /* Purge streams waiting on concurrency still waiting for id assignment */ + grpc_chttp2_list_remove_waiting_for_concurrency(t, s); + } + if (overall_error != GRPC_ERROR_NONE) { + grpc_chttp2_fake_status(exec_ctx, t, s, overall_error); + } + } + if (closed_read) { + for (int i = 0; i < 2; i++) { + if (s->published_metadata[i] == GRPC_METADATA_NOT_PUBLISHED) { + s->published_metadata[i] = GPRC_METADATA_PUBLISHED_AT_CLOSE; + } + } + grpc_chttp2_maybe_complete_recv_initial_metadata(exec_ctx, t, s); + grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); + } + if (became_closed) { + grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2"); + } + GRPC_ERROR_UNREF(error); +} + +static void close_from_api(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_error *error) { + grpc_slice hdr; + grpc_slice status_hdr; + grpc_slice http_status_hdr; + grpc_slice content_type_hdr; + grpc_slice message_pfx; + uint8_t *p; + uint32_t len = 0; + grpc_status_code grpc_status; + grpc_slice slice; + grpc_error_get_status(error, s->deadline, &grpc_status, &slice, NULL); + + GPR_ASSERT(grpc_status >= 0 && (int)grpc_status < 100); + + /* Hand roll a header block. + This is unnecessarily ugly - at some point we should find a more + elegant solution. + It's complicated by the fact that our send machinery would be dead by + the time we got around to sending this, so instead we ignore HPACK + compression and just write the uncompressed bytes onto the wire. */ + if (!s->sent_initial_metadata) { + http_status_hdr = GRPC_SLICE_MALLOC(13); + p = GRPC_SLICE_START_PTR(http_status_hdr); + *p++ = 0x00; + *p++ = 7; + *p++ = ':'; + *p++ = 's'; + *p++ = 't'; + *p++ = 'a'; + *p++ = 't'; + *p++ = 'u'; + *p++ = 's'; + *p++ = 3; + *p++ = '2'; + *p++ = '0'; + *p++ = '0'; + GPR_ASSERT(p == GRPC_SLICE_END_PTR(http_status_hdr)); + len += (uint32_t)GRPC_SLICE_LENGTH(http_status_hdr); + + content_type_hdr = GRPC_SLICE_MALLOC(31); + p = GRPC_SLICE_START_PTR(content_type_hdr); + *p++ = 0x00; + *p++ = 12; + *p++ = 'c'; + *p++ = 'o'; + *p++ = 'n'; + *p++ = 't'; + *p++ = 'e'; + *p++ = 'n'; + *p++ = 't'; + *p++ = '-'; + *p++ = 't'; + *p++ = 'y'; + *p++ = 'p'; + *p++ = 'e'; + *p++ = 16; + *p++ = 'a'; + *p++ = 'p'; + *p++ = 'p'; + *p++ = 'l'; + *p++ = 'i'; + *p++ = 'c'; + *p++ = 'a'; + *p++ = 't'; + *p++ = 'i'; + *p++ = 'o'; + *p++ = 'n'; + *p++ = '/'; + *p++ = 'g'; + *p++ = 'r'; + *p++ = 'p'; + *p++ = 'c'; + GPR_ASSERT(p == GRPC_SLICE_END_PTR(content_type_hdr)); + len += (uint32_t)GRPC_SLICE_LENGTH(content_type_hdr); + } + + status_hdr = GRPC_SLICE_MALLOC(15 + (grpc_status >= 10)); + p = GRPC_SLICE_START_PTR(status_hdr); + *p++ = 0x00; /* literal header, not indexed */ + *p++ = 11; /* len(grpc-status) */ + *p++ = 'g'; + *p++ = 'r'; + *p++ = 'p'; + *p++ = 'c'; + *p++ = '-'; + *p++ = 's'; + *p++ = 't'; + *p++ = 'a'; + *p++ = 't'; + *p++ = 'u'; + *p++ = 's'; + if (grpc_status < 10) { + *p++ = 1; + *p++ = (uint8_t)('0' + grpc_status); + } else { + *p++ = 2; + *p++ = (uint8_t)('0' + (grpc_status / 10)); + *p++ = (uint8_t)('0' + (grpc_status % 10)); + } + GPR_ASSERT(p == GRPC_SLICE_END_PTR(status_hdr)); + len += (uint32_t)GRPC_SLICE_LENGTH(status_hdr); + + size_t msg_len = GRPC_SLICE_LENGTH(slice); + GPR_ASSERT(msg_len <= UINT32_MAX); + uint32_t msg_len_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)msg_len, 1); + message_pfx = GRPC_SLICE_MALLOC(14 + msg_len_len); + p = GRPC_SLICE_START_PTR(message_pfx); + *p++ = 0x00; /* literal header, not indexed */ + *p++ = 12; /* len(grpc-message) */ + *p++ = 'g'; + *p++ = 'r'; + *p++ = 'p'; + *p++ = 'c'; + *p++ = '-'; + *p++ = 'm'; + *p++ = 'e'; + *p++ = 's'; + *p++ = 's'; + *p++ = 'a'; + *p++ = 'g'; + *p++ = 'e'; + GRPC_CHTTP2_WRITE_VARINT((uint32_t)msg_len, 1, 0, p, (uint32_t)msg_len_len); + p += msg_len_len; + GPR_ASSERT(p == GRPC_SLICE_END_PTR(message_pfx)); + len += (uint32_t)GRPC_SLICE_LENGTH(message_pfx); + len += (uint32_t)msg_len; + + hdr = GRPC_SLICE_MALLOC(9); + p = GRPC_SLICE_START_PTR(hdr); + *p++ = (uint8_t)(len >> 16); + *p++ = (uint8_t)(len >> 8); + *p++ = (uint8_t)(len); + *p++ = GRPC_CHTTP2_FRAME_HEADER; + *p++ = GRPC_CHTTP2_DATA_FLAG_END_STREAM | GRPC_CHTTP2_DATA_FLAG_END_HEADERS; + *p++ = (uint8_t)(s->id >> 24); + *p++ = (uint8_t)(s->id >> 16); + *p++ = (uint8_t)(s->id >> 8); + *p++ = (uint8_t)(s->id); + GPR_ASSERT(p == GRPC_SLICE_END_PTR(hdr)); + + grpc_slice_buffer_add(&t->qbuf, hdr); + if (!s->sent_initial_metadata) { + grpc_slice_buffer_add(&t->qbuf, http_status_hdr); + grpc_slice_buffer_add(&t->qbuf, content_type_hdr); + } + grpc_slice_buffer_add(&t->qbuf, status_hdr); + grpc_slice_buffer_add(&t->qbuf, message_pfx); + grpc_slice_buffer_add(&t->qbuf, grpc_slice_ref_internal(slice)); + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, + &s->stats.outgoing)); + + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, 1, 1, error); + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API); +} + +typedef struct { + grpc_exec_ctx *exec_ctx; + grpc_error *error; + grpc_chttp2_transport *t; +} cancel_stream_cb_args; + +static void cancel_stream_cb(void *user_data, uint32_t key, void *stream) { + cancel_stream_cb_args *args = (cancel_stream_cb_args *)user_data; + grpc_chttp2_stream *s = (grpc_chttp2_stream *)stream; + grpc_chttp2_cancel_stream(args->exec_ctx, args->t, s, + GRPC_ERROR_REF(args->error)); +} + +static void end_all_the_calls(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_error *error) { + cancel_stream_cb_args args = {exec_ctx, error, t}; + grpc_chttp2_stream_map_for_each(&t->stream_map, cancel_stream_cb, &args); + GRPC_ERROR_UNREF(error); +} + +/******************************************************************************* + * INPUT PROCESSING - PARSING + */ + +void grpc_chttp2_act_on_flowctl_action(grpc_exec_ctx *exec_ctx, + grpc_chttp2_flowctl_action action, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + switch (action.send_stream_update) { + case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED: + break; + case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY: + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + grpc_chttp2_initiate_write( + exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL); + break; + case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE: + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + break; + } + switch (action.send_transport_update) { + case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED: + break; + case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY: + grpc_chttp2_initiate_write( + exec_ctx, t, GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL); + break; + // this is the same as no action b/c every time the transport enters the + // writing path it will maybe do an update + case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE: + break; + } + if (action.send_setting_update != GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED) { + if (action.initial_window_size > 0) { + queue_setting_update(exec_ctx, t, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, + (uint32_t)action.initial_window_size); + } + if (action.max_frame_size > 0) { + queue_setting_update(exec_ctx, t, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE, + (uint32_t)action.max_frame_size); + } + if (action.send_setting_update == GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY) { + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS); + } + } + if (action.need_ping) { + GRPC_CHTTP2_REF_TRANSPORT(t, "bdp_ping"); + grpc_bdp_estimator_schedule_ping(&t->flow_control.bdp_estimator); + send_ping_locked(exec_ctx, t, + GRPC_CHTTP2_PING_BEFORE_TRANSPORT_WINDOW_UPDATE, + &t->start_bdp_ping_locked, &t->finish_bdp_ping_locked, + GRPC_CHTTP2_INITIATE_WRITE_BDP_ESTIMATOR_PING); + } +} + +static grpc_error *try_http_parsing(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_http_parser parser; + size_t i = 0; + grpc_error *error = GRPC_ERROR_NONE; + grpc_http_response response; + memset(&response, 0, sizeof(response)); + + grpc_http_parser_init(&parser, GRPC_HTTP_RESPONSE, &response); + + grpc_error *parse_error = GRPC_ERROR_NONE; + for (; i < t->read_buffer.count && parse_error == GRPC_ERROR_NONE; i++) { + parse_error = + grpc_http_parser_parse(&parser, t->read_buffer.slices[i], NULL); + } + if (parse_error == GRPC_ERROR_NONE && + (parse_error = grpc_http_parser_eof(&parser)) == GRPC_ERROR_NONE) { + error = grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Trying to connect an http1.x server"), + GRPC_ERROR_INT_HTTP_STATUS, response.status), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); + } + GRPC_ERROR_UNREF(parse_error); + + grpc_http_parser_destroy(&parser); + grpc_http_response_destroy(&response); + return error; +} + +static void read_action_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + GPR_TIMER_BEGIN("reading_action_locked", 0); + + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + + GRPC_ERROR_REF(error); + + grpc_error *err = error; + if (err != GRPC_ERROR_NONE) { + err = grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Endpoint read failed", &err, 1), + GRPC_ERROR_INT_OCCURRED_DURING_WRITE, + t->write_state); + } + GPR_SWAP(grpc_error *, err, error); + GRPC_ERROR_UNREF(err); + if (!t->closed) { + GPR_TIMER_BEGIN("reading_action.parse", 0); + size_t i = 0; + grpc_error *errors[3] = {GRPC_ERROR_REF(error), GRPC_ERROR_NONE, + GRPC_ERROR_NONE}; + for (; i < t->read_buffer.count && errors[1] == GRPC_ERROR_NONE; i++) { + grpc_bdp_estimator_add_incoming_bytes( + &t->flow_control.bdp_estimator, + (int64_t)GRPC_SLICE_LENGTH(t->read_buffer.slices[i])); + errors[1] = + grpc_chttp2_perform_read(exec_ctx, t, t->read_buffer.slices[i]); + } + if (errors[1] != GRPC_ERROR_NONE) { + errors[2] = try_http_parsing(exec_ctx, t); + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed parsing HTTP/2", errors, GPR_ARRAY_SIZE(errors)); + } + for (i = 0; i < GPR_ARRAY_SIZE(errors); i++) { + GRPC_ERROR_UNREF(errors[i]); + } + GPR_TIMER_END("reading_action.parse", 0); + + GPR_TIMER_BEGIN("post_parse_locked", 0); + if (t->flow_control.initial_window_update != 0) { + if (t->flow_control.initial_window_update > 0) { + grpc_chttp2_stream *s; + while (grpc_chttp2_list_pop_stalled_by_stream(t, &s)) { + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + grpc_chttp2_initiate_write( + exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING); + } + } + t->flow_control.initial_window_update = 0; + } + GPR_TIMER_END("post_parse_locked", 0); + } + + GPR_TIMER_BEGIN("post_reading_action_locked", 0); + bool keep_reading = false; + if (error == GRPC_ERROR_NONE && t->closed) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"); + } + if (error != GRPC_ERROR_NONE) { + close_transport_locked(exec_ctx, t, GRPC_ERROR_REF(error)); + t->endpoint_reading = 0; + } else if (!t->closed) { + keep_reading = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "keep_reading"); + } + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &t->read_buffer); + + if (keep_reading) { + grpc_endpoint_read(exec_ctx, t->ep, &t->read_buffer, + &t->read_action_locked); + grpc_chttp2_act_on_flowctl_action( + exec_ctx, grpc_chttp2_flowctl_get_bdp_action(&t->flow_control), t, + NULL); + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keep_reading"); + } else { + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "reading_action"); + } + + GPR_TIMER_END("post_reading_action_locked", 0); + + GRPC_ERROR_UNREF(error); + + GPR_TIMER_END("reading_action_locked", 0); +} + +static void start_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_DEBUG, "%s: Start BDP ping", t->peer_string); + } + /* Reset the keepalive ping timer */ + if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING) { + grpc_timer_cancel(exec_ctx, &t->keepalive_ping_timer); + } + grpc_bdp_estimator_start_ping(&t->flow_control.bdp_estimator); +} + +static void finish_bdp_ping_locked(grpc_exec_ctx *exec_ctx, void *tp, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_DEBUG, "%s: Complete BDP ping", t->peer_string); + } + grpc_bdp_estimator_complete_ping(&t->flow_control.bdp_estimator); + + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "bdp_ping"); +} + +void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, + bool is_client) { + size_t i; + if (args) { + for (i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){g_default_client_keepalive_time_ms, 1, + INT_MAX}); + if (is_client) { + g_default_client_keepalive_time_ms = value; + } else { + g_default_server_keepalive_time_ms = value; + } + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { + const int value = grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){g_default_client_keepalive_timeout_ms, 0, + INT_MAX}); + if (is_client) { + g_default_client_keepalive_timeout_ms = value; + } else { + g_default_server_keepalive_timeout_ms = value; + } + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { + g_default_keepalive_permit_without_calls = + (uint32_t)grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){g_default_keepalive_permit_without_calls, + 0, 1}); + } else if (0 == + strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { + g_default_max_ping_strikes = grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){g_default_max_ping_strikes, 0, INT_MAX}); + } else if (0 == strcmp(args->args[i].key, + GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { + g_default_max_pings_without_data = grpc_channel_arg_get_integer( + &args->args[i], (grpc_integer_options){ + g_default_max_pings_without_data, 0, INT_MAX}); + } else if (0 == + strcmp( + args->args[i].key, + GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS)) { + g_default_min_sent_ping_interval_without_data_ms = + grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){ + g_default_min_sent_ping_interval_without_data_ms, 0, + INT_MAX}); + } else if (0 == + strcmp( + args->args[i].key, + GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS)) { + g_default_min_recv_ping_interval_without_data_ms = + grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){ + g_default_min_recv_ping_interval_without_data_ms, 0, + INT_MAX}); + } + } + } +} + +static void init_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; + GPR_ASSERT(t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_WAITING); + if (t->destroying || t->closed) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING; + } else if (error == GRPC_ERROR_NONE) { + if (t->keepalive_permit_without_calls || + grpc_chttp2_stream_map_size(&t->stream_map) > 0) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_PINGING; + GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive ping end"); + send_ping_locked(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE, + &t->start_keepalive_ping_locked, + &t->finish_keepalive_ping_locked, + GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING); + } else { + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init( + exec_ctx, &t->keepalive_ping_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), + &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); + } + } else if (error == GRPC_ERROR_CANCELLED) { + /* The keepalive ping timer may be cancelled by bdp */ + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init( + exec_ctx, &t->keepalive_ping_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), + &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); + } + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "init keepalive ping"); +} + +static void start_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; + GRPC_CHTTP2_REF_TRANSPORT(t, "keepalive watchdog"); + grpc_timer_init( + exec_ctx, &t->keepalive_watchdog_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_timeout), + &t->keepalive_watchdog_fired_locked, gpr_now(GPR_CLOCK_MONOTONIC)); +} + +static void finish_keepalive_ping_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; + if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) { + if (error == GRPC_ERROR_NONE) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_WAITING; + grpc_timer_cancel(exec_ctx, &t->keepalive_watchdog_timer); + GRPC_CHTTP2_REF_TRANSPORT(t, "init keepalive ping"); + grpc_timer_init( + exec_ctx, &t->keepalive_ping_timer, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), t->keepalive_time), + &t->init_keepalive_ping_locked, gpr_now(GPR_CLOCK_MONOTONIC)); + } + } + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive ping end"); +} + +static void keepalive_watchdog_fired_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; + if (t->keepalive_state == GRPC_CHTTP2_KEEPALIVE_STATE_PINGING) { + if (error == GRPC_ERROR_NONE) { + t->keepalive_state = GRPC_CHTTP2_KEEPALIVE_STATE_DYING; + close_transport_locked(exec_ctx, t, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "keepalive watchdog timeout")); + } + } else { + /* The watchdog timer should have been cancelled by + * finish_keepalive_ping_locked. */ + if (error != GRPC_ERROR_CANCELLED) { + gpr_log(GPR_ERROR, "keepalive_ping_end state error: %d (expect: %d)", + t->keepalive_state, GRPC_CHTTP2_KEEPALIVE_STATE_PINGING); + } + } + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "keepalive watchdog"); +} + +/******************************************************************************* + * CALLBACK LOOP + */ + +static void connectivity_state_set(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_connectivity_state state, + grpc_error *error, const char *reason) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_DEBUG, "set connectivity_state=%d", state)); + grpc_connectivity_state_set(exec_ctx, &t->channel_callback.state_tracker, + state, error, reason); +} + +/******************************************************************************* + * POLLSET STUFF + */ + +static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_pollset *pollset) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_endpoint_add_to_pollset(exec_ctx, t->ep, pollset); +} + +static void set_pollset_set(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_pollset_set *pollset_set) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)gt; + grpc_endpoint_add_to_pollset_set(exec_ctx, t->ep, pollset_set); +} + +/******************************************************************************* + * BYTE STREAM + */ + +static void reset_byte_stream(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_stream *s = (grpc_chttp2_stream *)arg; + + s->pending_byte_stream = false; + if (error == GRPC_ERROR_NONE) { + grpc_chttp2_maybe_complete_recv_message(exec_ctx, s->t, s); + grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, s->t, s); + } else { + GPR_ASSERT(error != GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(exec_ctx, s->on_next, GRPC_ERROR_REF(error)); + s->on_next = NULL; + GRPC_ERROR_UNREF(s->byte_stream_error); + s->byte_stream_error = GRPC_ERROR_NONE; + grpc_chttp2_cancel_stream(exec_ctx, s->t, s, GRPC_ERROR_REF(error)); + s->byte_stream_error = GRPC_ERROR_REF(error); + } +} + +static void incoming_byte_stream_unref(grpc_exec_ctx *exec_ctx, + grpc_chttp2_incoming_byte_stream *bs) { + if (gpr_unref(&bs->refs)) { + gpr_free(bs); + } +} + +static void incoming_byte_stream_next_locked(grpc_exec_ctx *exec_ctx, + void *argp, + grpc_error *error_ignored) { + grpc_chttp2_incoming_byte_stream *bs = + (grpc_chttp2_incoming_byte_stream *)argp; + grpc_chttp2_transport *t = bs->transport; + grpc_chttp2_stream *s = bs->stream; + + size_t cur_length = s->frame_storage.length; + if (!s->read_closed) { + grpc_chttp2_flowctl_incoming_bs_update(&t->flow_control, &s->flow_control, + bs->next_action.max_size_hint, + cur_length); + grpc_chttp2_act_on_flowctl_action( + exec_ctx, + grpc_chttp2_flowctl_get_action(&t->flow_control, &s->flow_control), t, + s); + } + GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0); + if (s->frame_storage.length > 0) { + grpc_slice_buffer_swap(&s->frame_storage, + &s->unprocessed_incoming_frames_buffer); + s->unprocessed_incoming_frames_decompressed = false; + GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, GRPC_ERROR_NONE); + } else if (s->byte_stream_error != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, + GRPC_ERROR_REF(s->byte_stream_error)); + if (s->data_parser.parsing_frame != NULL) { + incoming_byte_stream_unref(exec_ctx, s->data_parser.parsing_frame); + s->data_parser.parsing_frame = NULL; + } + } else if (s->read_closed) { + if (bs->remaining_bytes != 0) { + s->byte_stream_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + GRPC_CLOSURE_SCHED(exec_ctx, bs->next_action.on_complete, + GRPC_ERROR_REF(s->byte_stream_error)); + if (s->data_parser.parsing_frame != NULL) { + incoming_byte_stream_unref(exec_ctx, s->data_parser.parsing_frame); + s->data_parser.parsing_frame = NULL; + } + } else { + /* Should never reach here. */ + GPR_ASSERT(false); + } + } else { + s->on_next = bs->next_action.on_complete; + } + incoming_byte_stream_unref(exec_ctx, bs); +} + +static bool incoming_byte_stream_next(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + size_t max_size_hint, + grpc_closure *on_complete) { + GPR_TIMER_BEGIN("incoming_byte_stream_next", 0); + grpc_chttp2_incoming_byte_stream *bs = + (grpc_chttp2_incoming_byte_stream *)byte_stream; + grpc_chttp2_stream *s = bs->stream; + if (s->unprocessed_incoming_frames_buffer.length > 0) { + GPR_TIMER_END("incoming_byte_stream_next", 0); + return true; + } else { + gpr_ref(&bs->refs); + bs->next_action.max_size_hint = max_size_hint; + bs->next_action.on_complete = on_complete; + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&bs->next_action.closure, + incoming_byte_stream_next_locked, bs, + grpc_combiner_scheduler(bs->transport->combiner)), + GRPC_ERROR_NONE); + GPR_TIMER_END("incoming_byte_stream_next", 0); + return false; + } +} + +static grpc_error *incoming_byte_stream_pull(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_slice *slice) { + GPR_TIMER_BEGIN("incoming_byte_stream_pull", 0); + grpc_chttp2_incoming_byte_stream *bs = + (grpc_chttp2_incoming_byte_stream *)byte_stream; + grpc_chttp2_stream *s = bs->stream; + grpc_error *error; + + if (s->unprocessed_incoming_frames_buffer.length > 0) { + if (!s->unprocessed_incoming_frames_decompressed) { + bool end_of_context; + if (!s->stream_decompression_ctx) { + s->stream_decompression_ctx = grpc_stream_compression_context_create( + s->stream_decompression_method); + } + if (!grpc_stream_decompress(s->stream_decompression_ctx, + &s->unprocessed_incoming_frames_buffer, + &s->decompressed_data_buffer, NULL, + MAX_SIZE_T, &end_of_context)) { + error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Stream decompression error."); + return error; + } + GPR_ASSERT(s->unprocessed_incoming_frames_buffer.length == 0); + grpc_slice_buffer_swap(&s->unprocessed_incoming_frames_buffer, + &s->decompressed_data_buffer); + s->unprocessed_incoming_frames_decompressed = true; + if (end_of_context) { + grpc_stream_compression_context_destroy(s->stream_decompression_ctx); + s->stream_decompression_ctx = NULL; + } + if (s->unprocessed_incoming_frames_buffer.length == 0) { + *slice = grpc_empty_slice(); + } + } + error = grpc_deframe_unprocessed_incoming_frames( + exec_ctx, &s->data_parser, s, &s->unprocessed_incoming_frames_buffer, + slice, NULL); + if (error != GRPC_ERROR_NONE) { + return error; + } + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error)); + return error; + } + GPR_TIMER_END("incoming_byte_stream_pull", 0); + return GRPC_ERROR_NONE; +} + +static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, + void *byte_stream, + grpc_error *error_ignored); + +static void incoming_byte_stream_destroy(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream) { + GPR_TIMER_BEGIN("incoming_byte_stream_destroy", 0); + grpc_chttp2_incoming_byte_stream *bs = + (grpc_chttp2_incoming_byte_stream *)byte_stream; + GRPC_CLOSURE_SCHED( + exec_ctx, GRPC_CLOSURE_INIT( + &bs->destroy_action, incoming_byte_stream_destroy_locked, + bs, grpc_combiner_scheduler(bs->transport->combiner)), + GRPC_ERROR_NONE); + GPR_TIMER_END("incoming_byte_stream_destroy", 0); +} + +static void incoming_byte_stream_publish_error( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, + grpc_error *error) { + grpc_chttp2_stream *s = bs->stream; + + GPR_ASSERT(error != GRPC_ERROR_NONE); + GRPC_CLOSURE_SCHED(exec_ctx, s->on_next, GRPC_ERROR_REF(error)); + s->on_next = NULL; + GRPC_ERROR_UNREF(s->byte_stream_error); + s->byte_stream_error = GRPC_ERROR_REF(error); + grpc_chttp2_cancel_stream(exec_ctx, bs->transport, bs->stream, + GRPC_ERROR_REF(error)); +} + +grpc_error *grpc_chttp2_incoming_byte_stream_push( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, + grpc_slice slice, grpc_slice *slice_out) { + grpc_chttp2_stream *s = bs->stream; + + if (bs->remaining_bytes < GRPC_SLICE_LENGTH(slice)) { + grpc_error *error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Too many bytes in stream"); + + GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error)); + grpc_slice_unref_internal(exec_ctx, slice); + return error; + } else { + bs->remaining_bytes -= (uint32_t)GRPC_SLICE_LENGTH(slice); + if (slice_out != NULL) { + *slice_out = slice; + } + return GRPC_ERROR_NONE; + } +} + +grpc_error *grpc_chttp2_incoming_byte_stream_finished( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_byte_stream *bs, + grpc_error *error, bool reset_on_error) { + grpc_chttp2_stream *s = bs->stream; + + if (error == GRPC_ERROR_NONE) { + if (bs->remaining_bytes != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Truncated message"); + } + } + if (error != GRPC_ERROR_NONE && reset_on_error) { + GRPC_CLOSURE_SCHED(exec_ctx, &s->reset_byte_stream, GRPC_ERROR_REF(error)); + } + incoming_byte_stream_unref(exec_ctx, bs); + return error; +} + +static void incoming_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_error *error) { + grpc_chttp2_incoming_byte_stream *bs = + (grpc_chttp2_incoming_byte_stream *)byte_stream; + GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( + exec_ctx, bs, error, true /* reset_on_error */)); +} + +static const grpc_byte_stream_vtable grpc_chttp2_incoming_byte_stream_vtable = { + incoming_byte_stream_next, incoming_byte_stream_pull, + incoming_byte_stream_shutdown, incoming_byte_stream_destroy}; + +static void incoming_byte_stream_destroy_locked(grpc_exec_ctx *exec_ctx, + void *byte_stream, + grpc_error *error_ignored) { + grpc_chttp2_incoming_byte_stream *bs = + (grpc_chttp2_incoming_byte_stream *)byte_stream; + grpc_chttp2_stream *s = bs->stream; + grpc_chttp2_transport *t = s->t; + + GPR_ASSERT(bs->base.vtable == &grpc_chttp2_incoming_byte_stream_vtable); + incoming_byte_stream_unref(exec_ctx, bs); + s->pending_byte_stream = false; + grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); + grpc_chttp2_maybe_complete_recv_trailing_metadata(exec_ctx, t, s); +} + +grpc_chttp2_incoming_byte_stream *grpc_chttp2_incoming_byte_stream_create( + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_chttp2_stream *s, + uint32_t frame_size, uint32_t flags) { + grpc_chttp2_incoming_byte_stream *incoming_byte_stream = + (grpc_chttp2_incoming_byte_stream *)gpr_malloc( + sizeof(*incoming_byte_stream)); + incoming_byte_stream->base.length = frame_size; + incoming_byte_stream->remaining_bytes = frame_size; + incoming_byte_stream->base.flags = flags; + incoming_byte_stream->base.vtable = &grpc_chttp2_incoming_byte_stream_vtable; + gpr_ref_init(&incoming_byte_stream->refs, 2); + incoming_byte_stream->transport = t; + incoming_byte_stream->stream = s; + GRPC_ERROR_UNREF(s->byte_stream_error); + s->byte_stream_error = GRPC_ERROR_NONE; + return incoming_byte_stream; +} + +/******************************************************************************* + * RESOURCE QUOTAS + */ + +static void post_benign_reclaimer(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (!t->benign_reclaimer_registered) { + t->benign_reclaimer_registered = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "benign_reclaimer"); + grpc_resource_user_post_reclaimer(exec_ctx, + grpc_endpoint_get_resource_user(t->ep), + false, &t->benign_reclaimer_locked); + } +} + +static void post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (!t->destructive_reclaimer_registered) { + t->destructive_reclaimer_registered = true; + GRPC_CHTTP2_REF_TRANSPORT(t, "destructive_reclaimer"); + grpc_resource_user_post_reclaimer(exec_ctx, + grpc_endpoint_get_resource_user(t->ep), + true, &t->destructive_reclaimer_locked); + } +} + +static void benign_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; + if (error == GRPC_ERROR_NONE && + grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + /* Channel with no active streams: send a goaway to try and make it + * disconnect cleanly */ + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "HTTP2: %s - send goaway to free memory", + t->peer_string); + } + send_goaway(exec_ctx, t, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"), + GRPC_ERROR_INT_HTTP2_ERROR, GRPC_HTTP2_ENHANCE_YOUR_CALM)); + } else if (error == GRPC_ERROR_NONE && + GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, + "HTTP2: %s - skip benign reclamation, there are still %" PRIdPTR + " streams", + t->peer_string, grpc_chttp2_stream_map_size(&t->stream_map)); + } + t->benign_reclaimer_registered = false; + if (error != GRPC_ERROR_CANCELLED) { + grpc_resource_user_finish_reclamation( + exec_ctx, grpc_endpoint_get_resource_user(t->ep)); + } + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "benign_reclaimer"); +} + +static void destructive_reclaimer_locked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)arg; + size_t n = grpc_chttp2_stream_map_size(&t->stream_map); + t->destructive_reclaimer_registered = false; + if (error == GRPC_ERROR_NONE && n > 0) { + grpc_chttp2_stream *s = + (grpc_chttp2_stream *)grpc_chttp2_stream_map_rand(&t->stream_map); + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "HTTP2: %s - abandon stream id %d", t->peer_string, + s->id); + } + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Buffers full"), + GRPC_ERROR_INT_HTTP2_ERROR, + GRPC_HTTP2_ENHANCE_YOUR_CALM)); + if (n > 1) { + /* Since we cancel one stream per destructive reclamation, if + there are more streams left, we can immediately post a new + reclaimer in case the resource quota needs to free more + memory */ + post_destructive_reclaimer(exec_ctx, t); + } + } + if (error != GRPC_ERROR_CANCELLED) { + grpc_resource_user_finish_reclamation( + exec_ctx, grpc_endpoint_get_resource_user(t->ep)); + } + GRPC_CHTTP2_UNREF_TRANSPORT(exec_ctx, t, "destructive_reclaimer"); +} + +/******************************************************************************* + * MONITORING + */ + +const char *grpc_chttp2_initiate_write_reason_string( + grpc_chttp2_initiate_write_reason reason) { + switch (reason) { + case GRPC_CHTTP2_INITIATE_WRITE_INITIAL_WRITE: + return "INITIAL_WRITE"; + case GRPC_CHTTP2_INITIATE_WRITE_START_NEW_STREAM: + return "START_NEW_STREAM"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_MESSAGE: + return "SEND_MESSAGE"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_INITIAL_METADATA: + return "SEND_INITIAL_METADATA"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_TRAILING_METADATA: + return "SEND_TRAILING_METADATA"; + case GRPC_CHTTP2_INITIATE_WRITE_RETRY_SEND_PING: + return "RETRY_SEND_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_CONTINUE_PINGS: + return "CONTINUE_PINGS"; + case GRPC_CHTTP2_INITIATE_WRITE_GOAWAY_SENT: + return "GOAWAY_SENT"; + case GRPC_CHTTP2_INITIATE_WRITE_RST_STREAM: + return "RST_STREAM"; + case GRPC_CHTTP2_INITIATE_WRITE_CLOSE_FROM_API: + return "CLOSE_FROM_API"; + case GRPC_CHTTP2_INITIATE_WRITE_STREAM_FLOW_CONTROL: + return "STREAM_FLOW_CONTROL"; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL: + return "TRANSPORT_FLOW_CONTROL"; + case GRPC_CHTTP2_INITIATE_WRITE_SEND_SETTINGS: + return "SEND_SETTINGS"; + case GRPC_CHTTP2_INITIATE_WRITE_BDP_ESTIMATOR_PING: + return "BDP_ESTIMATOR_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_SETTING: + return "FLOW_CONTROL_UNSTALLED_BY_SETTING"; + case GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE: + return "FLOW_CONTROL_UNSTALLED_BY_UPDATE"; + case GRPC_CHTTP2_INITIATE_WRITE_APPLICATION_PING: + return "APPLICATION_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_KEEPALIVE_PING: + return "KEEPALIVE_PING"; + case GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED: + return "TRANSPORT_FLOW_CONTROL_UNSTALLED"; + case GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE: + return "PING_RESPONSE"; + case GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM: + return "FORCE_RST_STREAM"; + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static grpc_endpoint *chttp2_get_endpoint(grpc_exec_ctx *exec_ctx, + grpc_transport *t) { + return ((grpc_chttp2_transport *)t)->ep; +} + +static const grpc_transport_vtable vtable = {sizeof(grpc_chttp2_stream), + "chttp2", + init_stream, + set_pollset, + set_pollset_set, + perform_stream_op, + perform_transport_op, + destroy_stream, + destroy_transport, + chttp2_get_endpoint}; + +static const grpc_transport_vtable *get_vtable(void) { return &vtable; } + +grpc_transport *grpc_create_chttp2_transport( + grpc_exec_ctx *exec_ctx, const grpc_channel_args *channel_args, + grpc_endpoint *ep, int is_client) { + grpc_chttp2_transport *t = + (grpc_chttp2_transport *)gpr_zalloc(sizeof(grpc_chttp2_transport)); + init_transport(exec_ctx, t, channel_args, ep, is_client != 0); + return &t->base; +} + +void grpc_chttp2_transport_start_reading(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, + grpc_slice_buffer *read_buffer) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)transport; + GRPC_CHTTP2_REF_TRANSPORT( + t, "reading_action"); /* matches unref inside reading_action */ + if (read_buffer != NULL) { + grpc_slice_buffer_move_into(read_buffer, &t->read_buffer); + gpr_free(read_buffer); + } + GRPC_CLOSURE_SCHED(exec_ctx, &t->read_action_locked, GRPC_ERROR_NONE); +} diff --git a/src/core/ext/transport/chttp2/transport/flow_control.c b/src/core/ext/transport/chttp2/transport/flow_control.c deleted file mode 100644 index 569a6349d3..0000000000 --- a/src/core/ext/transport/chttp2/transport/flow_control.c +++ /dev/null @@ -1,502 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/support/string.h" - -static uint32_t grpc_chttp2_target_announced_window( - const grpc_chttp2_transport_flowctl* tfc); - -#ifndef NDEBUG - -typedef struct { - int64_t remote_window; - int64_t target_window; - int64_t announced_window; - int64_t remote_window_delta; - int64_t local_window_delta; - int64_t announced_window_delta; - uint32_t local_init_window; - uint32_t local_max_frame; -} shadow_flow_control; - -static void pretrace(shadow_flow_control* shadow_fc, - grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc) { - shadow_fc->remote_window = tfc->remote_window; - shadow_fc->target_window = grpc_chttp2_target_announced_window(tfc); - shadow_fc->announced_window = tfc->announced_window; - if (sfc != NULL) { - shadow_fc->remote_window_delta = sfc->remote_window_delta; - shadow_fc->local_window_delta = sfc->local_window_delta; - shadow_fc->announced_window_delta = sfc->announced_window_delta; - } -} - -#define TRACE_PADDING 30 - -static char* fmt_int64_diff_str(int64_t old_val, int64_t new_val) { - char* str; - if (old_val != new_val) { - gpr_asprintf(&str, "%" PRId64 " -> %" PRId64 "", old_val, new_val); - } else { - gpr_asprintf(&str, "%" PRId64 "", old_val); - } - char* str_lp = gpr_leftpad(str, ' ', TRACE_PADDING); - gpr_free(str); - return str_lp; -} - -static char* fmt_uint32_diff_str(uint32_t old_val, uint32_t new_val) { - char* str; - if (new_val > 0 && old_val != new_val) { - gpr_asprintf(&str, "%" PRIu32 " -> %" PRIu32 "", old_val, new_val); - } else { - gpr_asprintf(&str, "%" PRIu32 "", old_val); - } - char* str_lp = gpr_leftpad(str, ' ', TRACE_PADDING); - gpr_free(str); - return str_lp; -} - -static void posttrace(shadow_flow_control* shadow_fc, - grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc, char* reason) { - uint32_t acked_local_window = - tfc->t->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - uint32_t remote_window = - tfc->t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - char* trw_str = - fmt_int64_diff_str(shadow_fc->remote_window, tfc->remote_window); - char* tlw_str = fmt_int64_diff_str(shadow_fc->target_window, - grpc_chttp2_target_announced_window(tfc)); - char* taw_str = - fmt_int64_diff_str(shadow_fc->announced_window, tfc->announced_window); - char* srw_str; - char* slw_str; - char* saw_str; - if (sfc != NULL) { - srw_str = fmt_int64_diff_str(shadow_fc->remote_window_delta + remote_window, - sfc->remote_window_delta + remote_window); - slw_str = - fmt_int64_diff_str(shadow_fc->local_window_delta + acked_local_window, - sfc->local_window_delta + acked_local_window); - saw_str = fmt_int64_diff_str( - shadow_fc->announced_window_delta + acked_local_window, - sfc->announced_window_delta + acked_local_window); - } else { - srw_str = gpr_leftpad("", ' ', TRACE_PADDING); - slw_str = gpr_leftpad("", ' ', TRACE_PADDING); - saw_str = gpr_leftpad("", ' ', TRACE_PADDING); - } - gpr_log(GPR_DEBUG, - "%p[%u][%s] | %s | trw:%s, ttw:%s, taw:%s, srw:%s, slw:%s, saw:%s", - tfc, sfc != NULL ? sfc->s->id : 0, tfc->t->is_client ? "cli" : "svr", - reason, trw_str, tlw_str, taw_str, srw_str, slw_str, saw_str); - gpr_free(trw_str); - gpr_free(tlw_str); - gpr_free(taw_str); - gpr_free(srw_str); - gpr_free(slw_str); - gpr_free(saw_str); -} - -static char* urgency_to_string(grpc_chttp2_flowctl_urgency urgency) { - switch (urgency) { - case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED: - return "no action"; - case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY: - return "update immediately"; - case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE: - return "queue update"; - default: - GPR_UNREACHABLE_CODE(return "unknown"); - } - GPR_UNREACHABLE_CODE(return "unknown"); -} - -static void trace_action(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_flowctl_action action) { - char* iw_str = fmt_uint32_diff_str( - tfc->t->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE], - action.initial_window_size); - char* mf_str = fmt_uint32_diff_str( - tfc->t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], - action.max_frame_size); - gpr_log(GPR_DEBUG, "t[%s], s[%s], settings[%s] iw:%s mf:%s", - urgency_to_string(action.send_transport_update), - urgency_to_string(action.send_stream_update), - urgency_to_string(action.send_setting_update), iw_str, mf_str); - gpr_free(iw_str); - gpr_free(mf_str); -} - -#define PRETRACE(tfc, sfc) \ - shadow_flow_control shadow_fc; \ - GRPC_FLOW_CONTROL_IF_TRACING(pretrace(&shadow_fc, tfc, sfc)) -#define POSTTRACE(tfc, sfc, reason) \ - GRPC_FLOW_CONTROL_IF_TRACING(posttrace(&shadow_fc, tfc, sfc, reason)) -#define TRACEACTION(tfc, action) \ - GRPC_FLOW_CONTROL_IF_TRACING(trace_action(tfc, action)) -#else -#define PRETRACE(tfc, sfc) -#define POSTTRACE(tfc, sfc, reason) -#define TRACEACTION(tfc, action) -#endif - -/* How many bytes of incoming flow control would we like to advertise */ -static uint32_t grpc_chttp2_target_announced_window( - const grpc_chttp2_transport_flowctl* tfc) { - return (uint32_t)GPR_MIN( - (int64_t)((1u << 31) - 1), - tfc->announced_stream_total_over_incoming_window + - tfc->t->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); -} - -// we have sent data on the wire, we must track this in our bookkeeping for the -// remote peer's flow control. -void grpc_chttp2_flowctl_sent_data(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc, - int64_t size) { - PRETRACE(tfc, sfc); - tfc->remote_window -= size; - sfc->remote_window_delta -= size; - POSTTRACE(tfc, sfc, " data sent"); -} - -static void announced_window_delta_preupdate(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc) { - if (sfc->announced_window_delta > 0) { - tfc->announced_stream_total_over_incoming_window -= - sfc->announced_window_delta; - } else { - tfc->announced_stream_total_under_incoming_window += - -sfc->announced_window_delta; - } -} - -static void announced_window_delta_postupdate( - grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) { - if (sfc->announced_window_delta > 0) { - tfc->announced_stream_total_over_incoming_window += - sfc->announced_window_delta; - } else { - tfc->announced_stream_total_under_incoming_window -= - -sfc->announced_window_delta; - } -} - -// We have received data from the wire. We must track this in our own flow -// control bookkeeping. -// Returns an error if the incoming frame violates our flow control. -grpc_error* grpc_chttp2_flowctl_recv_data(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc, - int64_t incoming_frame_size) { - uint32_t sent_init_window = - tfc->t->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - uint32_t acked_init_window = - tfc->t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - PRETRACE(tfc, sfc); - if (incoming_frame_size > tfc->announced_window) { - char* msg; - gpr_asprintf(&msg, - "frame of size %" PRId64 " overflows local window of %" PRId64, - incoming_frame_size, tfc->announced_window); - grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - - if (sfc != NULL) { - int64_t acked_stream_window = - sfc->announced_window_delta + acked_init_window; - int64_t sent_stream_window = sfc->announced_window_delta + sent_init_window; - if (incoming_frame_size > acked_stream_window) { - if (incoming_frame_size <= sent_stream_window) { - gpr_log( - GPR_ERROR, - "Incoming frame of size %" PRId64 - " exceeds local window size of %" PRId64 - ".\n" - "The (un-acked, future) window size would be %" PRId64 - " which is not exceeded.\n" - "This would usually cause a disconnection, but allowing it due to" - "broken HTTP2 implementations in the wild.\n" - "See (for example) https://github.com/netty/netty/issues/6520.", - incoming_frame_size, acked_stream_window, sent_stream_window); - } else { - char* msg; - gpr_asprintf(&msg, "frame of size %" PRId64 - " overflows local window of %" PRId64, - incoming_frame_size, acked_stream_window); - grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - } - - announced_window_delta_preupdate(tfc, sfc); - sfc->announced_window_delta -= incoming_frame_size; - announced_window_delta_postupdate(tfc, sfc); - sfc->local_window_delta -= incoming_frame_size; - } - - tfc->announced_window -= incoming_frame_size; - - POSTTRACE(tfc, sfc, " data recv"); - return GRPC_ERROR_NONE; -} - -// Returns a non zero announce integer if we should send a transport window -// update -uint32_t grpc_chttp2_flowctl_maybe_send_transport_update( - grpc_chttp2_transport_flowctl* tfc) { - PRETRACE(tfc, NULL); - uint32_t target_announced_window = grpc_chttp2_target_announced_window(tfc); - uint32_t threshold_to_send_transport_window_update = - tfc->t->outbuf.count > 0 ? 3 * target_announced_window / 4 - : target_announced_window / 2; - if (tfc->announced_window <= threshold_to_send_transport_window_update && - tfc->announced_window != target_announced_window) { - uint32_t announce = (uint32_t)GPR_CLAMP( - target_announced_window - tfc->announced_window, 0, UINT32_MAX); - tfc->announced_window += announce; - POSTTRACE(tfc, NULL, "t updt sent"); - return announce; - } - GRPC_FLOW_CONTROL_IF_TRACING( - gpr_log(GPR_DEBUG, "%p[0][%s] will not send transport update", tfc, - tfc->t->is_client ? "cli" : "svr")); - return 0; -} - -// Returns a non zero announce integer if we should send a stream window update -uint32_t grpc_chttp2_flowctl_maybe_send_stream_update( - grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) { - PRETRACE(tfc, sfc); - if (sfc->local_window_delta > sfc->announced_window_delta) { - uint32_t announce = (uint32_t)GPR_CLAMP( - sfc->local_window_delta - sfc->announced_window_delta, 0, UINT32_MAX); - announced_window_delta_preupdate(tfc, sfc); - sfc->announced_window_delta += announce; - announced_window_delta_postupdate(tfc, sfc); - POSTTRACE(tfc, sfc, "s updt sent"); - return announce; - } - GRPC_FLOW_CONTROL_IF_TRACING( - gpr_log(GPR_DEBUG, "%p[%u][%s] will not send stream update", tfc, - sfc->s->id, tfc->t->is_client ? "cli" : "svr")); - return 0; -} - -// we have received a WINDOW_UPDATE frame for a transport -void grpc_chttp2_flowctl_recv_transport_update( - grpc_chttp2_transport_flowctl* tfc, uint32_t size) { - PRETRACE(tfc, NULL); - tfc->remote_window += size; - POSTTRACE(tfc, NULL, "t updt recv"); -} - -// we have received a WINDOW_UPDATE frame for a stream -void grpc_chttp2_flowctl_recv_stream_update(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc, - uint32_t size) { - PRETRACE(tfc, sfc); - sfc->remote_window_delta += size; - POSTTRACE(tfc, sfc, "s updt recv"); -} - -void grpc_chttp2_flowctl_incoming_bs_update(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc, - size_t max_size_hint, - size_t have_already) { - PRETRACE(tfc, sfc); - uint32_t max_recv_bytes; - uint32_t sent_init_window = - tfc->t->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - - /* clamp max recv hint to an allowable size */ - if (max_size_hint >= UINT32_MAX - sent_init_window) { - max_recv_bytes = UINT32_MAX - sent_init_window; - } else { - max_recv_bytes = (uint32_t)max_size_hint; - } - - /* account for bytes already received but unknown to higher layers */ - if (max_recv_bytes >= have_already) { - max_recv_bytes -= (uint32_t)have_already; - } else { - max_recv_bytes = 0; - } - - /* add some small lookahead to keep pipelines flowing */ - GPR_ASSERT(max_recv_bytes <= UINT32_MAX - sent_init_window); - if (sfc->local_window_delta < max_recv_bytes) { - uint32_t add_max_recv_bytes = - (uint32_t)(max_recv_bytes - sfc->local_window_delta); - sfc->local_window_delta += add_max_recv_bytes; - } - POSTTRACE(tfc, sfc, "app st recv"); -} - -void grpc_chttp2_flowctl_destroy_stream(grpc_chttp2_transport_flowctl* tfc, - grpc_chttp2_stream_flowctl* sfc) { - announced_window_delta_preupdate(tfc, sfc); -} - -// Returns an urgency with which to make an update -static grpc_chttp2_flowctl_urgency delta_is_significant( - const grpc_chttp2_transport_flowctl* tfc, int32_t value, - grpc_chttp2_setting_id setting_id) { - int64_t delta = (int64_t)value - - (int64_t)tfc->t->settings[GRPC_LOCAL_SETTINGS][setting_id]; - // TODO(ncteisen): tune this - if (delta != 0 && (delta <= -value / 5 || delta >= value / 5)) { - return GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE; - } else { - return GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED; - } -} - -// Takes in a target and uses the pid controller to return a stabilized -// guess at the new bdp. -static double get_pid_controller_guess(grpc_chttp2_transport_flowctl* tfc, - double target) { - double bdp_error = target - grpc_pid_controller_last(&tfc->pid_controller); - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec dt_timespec = gpr_time_sub(now, tfc->last_pid_update); - double dt = (double)dt_timespec.tv_sec + dt_timespec.tv_nsec * 1e-9; - if (dt > 0.1) { - dt = 0.1; - } - double log2_bdp_guess = - grpc_pid_controller_update(&tfc->pid_controller, bdp_error, dt); - tfc->last_pid_update = now; - return pow(2, log2_bdp_guess); -} - -// Take in a target and modifies it based on the memory pressure of the system -static double get_target_under_memory_pressure( - grpc_chttp2_transport_flowctl* tfc, double target) { - // do not increase window under heavy memory pressure. - double memory_pressure = grpc_resource_quota_get_memory_pressure( - grpc_resource_user_quota(grpc_endpoint_get_resource_user(tfc->t->ep))); - if (memory_pressure > 0.8) { - target *= 1 - GPR_MIN(1, (memory_pressure - 0.8) / 0.1); - } - return target; -} - -grpc_chttp2_flowctl_action grpc_chttp2_flowctl_get_action( - grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) { - grpc_chttp2_flowctl_action action; - memset(&action, 0, sizeof(action)); - uint32_t target_announced_window = grpc_chttp2_target_announced_window(tfc); - if (tfc->announced_window < target_announced_window / 2) { - action.send_transport_update = GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY; - } - // TODO(ncteisen): tune this - if (sfc != NULL && !sfc->s->read_closed) { - uint32_t sent_init_window = - tfc->t->settings[GRPC_SENT_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; - if ((int64_t)sfc->local_window_delta > - (int64_t)sfc->announced_window_delta && - (int64_t)sfc->announced_window_delta + sent_init_window <= - sent_init_window / 2) { - action.send_stream_update = GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY; - } else if (sfc->local_window_delta > sfc->announced_window_delta) { - action.send_stream_update = GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE; - } - } - TRACEACTION(tfc, action); - return action; -} - -grpc_chttp2_flowctl_action grpc_chttp2_flowctl_get_bdp_action( - grpc_chttp2_transport_flowctl* tfc) { - grpc_chttp2_flowctl_action action; - memset(&action, 0, sizeof(action)); - if (tfc->enable_bdp_probe) { - action.need_ping = grpc_bdp_estimator_need_ping(&tfc->bdp_estimator); - - // get bdp estimate and update initial_window accordingly. - int64_t estimate = -1; - int32_t bdp = -1; - if (grpc_bdp_estimator_get_estimate(&tfc->bdp_estimator, &estimate)) { - double target = 1 + log2((double)estimate); - - // target might change based on how much memory pressure we are under - // TODO(ncteisen): experiment with setting target to be huge under low - // memory pressure. - target = get_target_under_memory_pressure(tfc, target); - - // run our target through the pid controller to stabilize change. - // TODO(ncteisen): experiment with other controllers here. - double bdp_guess = get_pid_controller_guess(tfc, target); - - // Though initial window 'could' drop to 0, we keep the floor at 128 - bdp = GPR_MAX((int32_t)bdp_guess, 128); - - grpc_chttp2_flowctl_urgency init_window_update_urgency = - delta_is_significant(tfc, bdp, - GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE); - if (init_window_update_urgency != GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED) { - action.send_setting_update = init_window_update_urgency; - action.initial_window_size = (uint32_t)bdp; - } - } - - // get bandwidth estimate and update max_frame accordingly. - double bw_dbl = -1; - if (grpc_bdp_estimator_get_bw(&tfc->bdp_estimator, &bw_dbl)) { - // we target the max of BDP or bandwidth in microseconds. - int32_t frame_size = (int32_t)GPR_CLAMP( - GPR_MAX((int32_t)GPR_CLAMP(bw_dbl, 0, INT_MAX) / 1000, bdp), 16384, - 16777215); - grpc_chttp2_flowctl_urgency frame_size_urgency = delta_is_significant( - tfc, frame_size, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE); - if (frame_size_urgency != GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED) { - if (frame_size_urgency > action.send_setting_update) { - action.send_setting_update = frame_size_urgency; - } - action.max_frame_size = (uint32_t)frame_size; - } - } - } - - TRACEACTION(tfc, action); - return action; -} diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc new file mode 100644 index 0000000000..569a6349d3 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/flow_control.cc @@ -0,0 +1,502 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/support/string.h" + +static uint32_t grpc_chttp2_target_announced_window( + const grpc_chttp2_transport_flowctl* tfc); + +#ifndef NDEBUG + +typedef struct { + int64_t remote_window; + int64_t target_window; + int64_t announced_window; + int64_t remote_window_delta; + int64_t local_window_delta; + int64_t announced_window_delta; + uint32_t local_init_window; + uint32_t local_max_frame; +} shadow_flow_control; + +static void pretrace(shadow_flow_control* shadow_fc, + grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc) { + shadow_fc->remote_window = tfc->remote_window; + shadow_fc->target_window = grpc_chttp2_target_announced_window(tfc); + shadow_fc->announced_window = tfc->announced_window; + if (sfc != NULL) { + shadow_fc->remote_window_delta = sfc->remote_window_delta; + shadow_fc->local_window_delta = sfc->local_window_delta; + shadow_fc->announced_window_delta = sfc->announced_window_delta; + } +} + +#define TRACE_PADDING 30 + +static char* fmt_int64_diff_str(int64_t old_val, int64_t new_val) { + char* str; + if (old_val != new_val) { + gpr_asprintf(&str, "%" PRId64 " -> %" PRId64 "", old_val, new_val); + } else { + gpr_asprintf(&str, "%" PRId64 "", old_val); + } + char* str_lp = gpr_leftpad(str, ' ', TRACE_PADDING); + gpr_free(str); + return str_lp; +} + +static char* fmt_uint32_diff_str(uint32_t old_val, uint32_t new_val) { + char* str; + if (new_val > 0 && old_val != new_val) { + gpr_asprintf(&str, "%" PRIu32 " -> %" PRIu32 "", old_val, new_val); + } else { + gpr_asprintf(&str, "%" PRIu32 "", old_val); + } + char* str_lp = gpr_leftpad(str, ' ', TRACE_PADDING); + gpr_free(str); + return str_lp; +} + +static void posttrace(shadow_flow_control* shadow_fc, + grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc, char* reason) { + uint32_t acked_local_window = + tfc->t->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + uint32_t remote_window = + tfc->t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + char* trw_str = + fmt_int64_diff_str(shadow_fc->remote_window, tfc->remote_window); + char* tlw_str = fmt_int64_diff_str(shadow_fc->target_window, + grpc_chttp2_target_announced_window(tfc)); + char* taw_str = + fmt_int64_diff_str(shadow_fc->announced_window, tfc->announced_window); + char* srw_str; + char* slw_str; + char* saw_str; + if (sfc != NULL) { + srw_str = fmt_int64_diff_str(shadow_fc->remote_window_delta + remote_window, + sfc->remote_window_delta + remote_window); + slw_str = + fmt_int64_diff_str(shadow_fc->local_window_delta + acked_local_window, + sfc->local_window_delta + acked_local_window); + saw_str = fmt_int64_diff_str( + shadow_fc->announced_window_delta + acked_local_window, + sfc->announced_window_delta + acked_local_window); + } else { + srw_str = gpr_leftpad("", ' ', TRACE_PADDING); + slw_str = gpr_leftpad("", ' ', TRACE_PADDING); + saw_str = gpr_leftpad("", ' ', TRACE_PADDING); + } + gpr_log(GPR_DEBUG, + "%p[%u][%s] | %s | trw:%s, ttw:%s, taw:%s, srw:%s, slw:%s, saw:%s", + tfc, sfc != NULL ? sfc->s->id : 0, tfc->t->is_client ? "cli" : "svr", + reason, trw_str, tlw_str, taw_str, srw_str, slw_str, saw_str); + gpr_free(trw_str); + gpr_free(tlw_str); + gpr_free(taw_str); + gpr_free(srw_str); + gpr_free(slw_str); + gpr_free(saw_str); +} + +static char* urgency_to_string(grpc_chttp2_flowctl_urgency urgency) { + switch (urgency) { + case GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED: + return "no action"; + case GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY: + return "update immediately"; + case GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE: + return "queue update"; + default: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static void trace_action(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_flowctl_action action) { + char* iw_str = fmt_uint32_diff_str( + tfc->t->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE], + action.initial_window_size); + char* mf_str = fmt_uint32_diff_str( + tfc->t->settings[GRPC_SENT_SETTINGS][GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + action.max_frame_size); + gpr_log(GPR_DEBUG, "t[%s], s[%s], settings[%s] iw:%s mf:%s", + urgency_to_string(action.send_transport_update), + urgency_to_string(action.send_stream_update), + urgency_to_string(action.send_setting_update), iw_str, mf_str); + gpr_free(iw_str); + gpr_free(mf_str); +} + +#define PRETRACE(tfc, sfc) \ + shadow_flow_control shadow_fc; \ + GRPC_FLOW_CONTROL_IF_TRACING(pretrace(&shadow_fc, tfc, sfc)) +#define POSTTRACE(tfc, sfc, reason) \ + GRPC_FLOW_CONTROL_IF_TRACING(posttrace(&shadow_fc, tfc, sfc, reason)) +#define TRACEACTION(tfc, action) \ + GRPC_FLOW_CONTROL_IF_TRACING(trace_action(tfc, action)) +#else +#define PRETRACE(tfc, sfc) +#define POSTTRACE(tfc, sfc, reason) +#define TRACEACTION(tfc, action) +#endif + +/* How many bytes of incoming flow control would we like to advertise */ +static uint32_t grpc_chttp2_target_announced_window( + const grpc_chttp2_transport_flowctl* tfc) { + return (uint32_t)GPR_MIN( + (int64_t)((1u << 31) - 1), + tfc->announced_stream_total_over_incoming_window + + tfc->t->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); +} + +// we have sent data on the wire, we must track this in our bookkeeping for the +// remote peer's flow control. +void grpc_chttp2_flowctl_sent_data(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc, + int64_t size) { + PRETRACE(tfc, sfc); + tfc->remote_window -= size; + sfc->remote_window_delta -= size; + POSTTRACE(tfc, sfc, " data sent"); +} + +static void announced_window_delta_preupdate(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc) { + if (sfc->announced_window_delta > 0) { + tfc->announced_stream_total_over_incoming_window -= + sfc->announced_window_delta; + } else { + tfc->announced_stream_total_under_incoming_window += + -sfc->announced_window_delta; + } +} + +static void announced_window_delta_postupdate( + grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) { + if (sfc->announced_window_delta > 0) { + tfc->announced_stream_total_over_incoming_window += + sfc->announced_window_delta; + } else { + tfc->announced_stream_total_under_incoming_window -= + -sfc->announced_window_delta; + } +} + +// We have received data from the wire. We must track this in our own flow +// control bookkeeping. +// Returns an error if the incoming frame violates our flow control. +grpc_error* grpc_chttp2_flowctl_recv_data(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc, + int64_t incoming_frame_size) { + uint32_t sent_init_window = + tfc->t->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + uint32_t acked_init_window = + tfc->t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + PRETRACE(tfc, sfc); + if (incoming_frame_size > tfc->announced_window) { + char* msg; + gpr_asprintf(&msg, + "frame of size %" PRId64 " overflows local window of %" PRId64, + incoming_frame_size, tfc->announced_window); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + + if (sfc != NULL) { + int64_t acked_stream_window = + sfc->announced_window_delta + acked_init_window; + int64_t sent_stream_window = sfc->announced_window_delta + sent_init_window; + if (incoming_frame_size > acked_stream_window) { + if (incoming_frame_size <= sent_stream_window) { + gpr_log( + GPR_ERROR, + "Incoming frame of size %" PRId64 + " exceeds local window size of %" PRId64 + ".\n" + "The (un-acked, future) window size would be %" PRId64 + " which is not exceeded.\n" + "This would usually cause a disconnection, but allowing it due to" + "broken HTTP2 implementations in the wild.\n" + "See (for example) https://github.com/netty/netty/issues/6520.", + incoming_frame_size, acked_stream_window, sent_stream_window); + } else { + char* msg; + gpr_asprintf(&msg, "frame of size %" PRId64 + " overflows local window of %" PRId64, + incoming_frame_size, acked_stream_window); + grpc_error* err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + } + + announced_window_delta_preupdate(tfc, sfc); + sfc->announced_window_delta -= incoming_frame_size; + announced_window_delta_postupdate(tfc, sfc); + sfc->local_window_delta -= incoming_frame_size; + } + + tfc->announced_window -= incoming_frame_size; + + POSTTRACE(tfc, sfc, " data recv"); + return GRPC_ERROR_NONE; +} + +// Returns a non zero announce integer if we should send a transport window +// update +uint32_t grpc_chttp2_flowctl_maybe_send_transport_update( + grpc_chttp2_transport_flowctl* tfc) { + PRETRACE(tfc, NULL); + uint32_t target_announced_window = grpc_chttp2_target_announced_window(tfc); + uint32_t threshold_to_send_transport_window_update = + tfc->t->outbuf.count > 0 ? 3 * target_announced_window / 4 + : target_announced_window / 2; + if (tfc->announced_window <= threshold_to_send_transport_window_update && + tfc->announced_window != target_announced_window) { + uint32_t announce = (uint32_t)GPR_CLAMP( + target_announced_window - tfc->announced_window, 0, UINT32_MAX); + tfc->announced_window += announce; + POSTTRACE(tfc, NULL, "t updt sent"); + return announce; + } + GRPC_FLOW_CONTROL_IF_TRACING( + gpr_log(GPR_DEBUG, "%p[0][%s] will not send transport update", tfc, + tfc->t->is_client ? "cli" : "svr")); + return 0; +} + +// Returns a non zero announce integer if we should send a stream window update +uint32_t grpc_chttp2_flowctl_maybe_send_stream_update( + grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) { + PRETRACE(tfc, sfc); + if (sfc->local_window_delta > sfc->announced_window_delta) { + uint32_t announce = (uint32_t)GPR_CLAMP( + sfc->local_window_delta - sfc->announced_window_delta, 0, UINT32_MAX); + announced_window_delta_preupdate(tfc, sfc); + sfc->announced_window_delta += announce; + announced_window_delta_postupdate(tfc, sfc); + POSTTRACE(tfc, sfc, "s updt sent"); + return announce; + } + GRPC_FLOW_CONTROL_IF_TRACING( + gpr_log(GPR_DEBUG, "%p[%u][%s] will not send stream update", tfc, + sfc->s->id, tfc->t->is_client ? "cli" : "svr")); + return 0; +} + +// we have received a WINDOW_UPDATE frame for a transport +void grpc_chttp2_flowctl_recv_transport_update( + grpc_chttp2_transport_flowctl* tfc, uint32_t size) { + PRETRACE(tfc, NULL); + tfc->remote_window += size; + POSTTRACE(tfc, NULL, "t updt recv"); +} + +// we have received a WINDOW_UPDATE frame for a stream +void grpc_chttp2_flowctl_recv_stream_update(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc, + uint32_t size) { + PRETRACE(tfc, sfc); + sfc->remote_window_delta += size; + POSTTRACE(tfc, sfc, "s updt recv"); +} + +void grpc_chttp2_flowctl_incoming_bs_update(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc, + size_t max_size_hint, + size_t have_already) { + PRETRACE(tfc, sfc); + uint32_t max_recv_bytes; + uint32_t sent_init_window = + tfc->t->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + + /* clamp max recv hint to an allowable size */ + if (max_size_hint >= UINT32_MAX - sent_init_window) { + max_recv_bytes = UINT32_MAX - sent_init_window; + } else { + max_recv_bytes = (uint32_t)max_size_hint; + } + + /* account for bytes already received but unknown to higher layers */ + if (max_recv_bytes >= have_already) { + max_recv_bytes -= (uint32_t)have_already; + } else { + max_recv_bytes = 0; + } + + /* add some small lookahead to keep pipelines flowing */ + GPR_ASSERT(max_recv_bytes <= UINT32_MAX - sent_init_window); + if (sfc->local_window_delta < max_recv_bytes) { + uint32_t add_max_recv_bytes = + (uint32_t)(max_recv_bytes - sfc->local_window_delta); + sfc->local_window_delta += add_max_recv_bytes; + } + POSTTRACE(tfc, sfc, "app st recv"); +} + +void grpc_chttp2_flowctl_destroy_stream(grpc_chttp2_transport_flowctl* tfc, + grpc_chttp2_stream_flowctl* sfc) { + announced_window_delta_preupdate(tfc, sfc); +} + +// Returns an urgency with which to make an update +static grpc_chttp2_flowctl_urgency delta_is_significant( + const grpc_chttp2_transport_flowctl* tfc, int32_t value, + grpc_chttp2_setting_id setting_id) { + int64_t delta = (int64_t)value - + (int64_t)tfc->t->settings[GRPC_LOCAL_SETTINGS][setting_id]; + // TODO(ncteisen): tune this + if (delta != 0 && (delta <= -value / 5 || delta >= value / 5)) { + return GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE; + } else { + return GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED; + } +} + +// Takes in a target and uses the pid controller to return a stabilized +// guess at the new bdp. +static double get_pid_controller_guess(grpc_chttp2_transport_flowctl* tfc, + double target) { + double bdp_error = target - grpc_pid_controller_last(&tfc->pid_controller); + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec dt_timespec = gpr_time_sub(now, tfc->last_pid_update); + double dt = (double)dt_timespec.tv_sec + dt_timespec.tv_nsec * 1e-9; + if (dt > 0.1) { + dt = 0.1; + } + double log2_bdp_guess = + grpc_pid_controller_update(&tfc->pid_controller, bdp_error, dt); + tfc->last_pid_update = now; + return pow(2, log2_bdp_guess); +} + +// Take in a target and modifies it based on the memory pressure of the system +static double get_target_under_memory_pressure( + grpc_chttp2_transport_flowctl* tfc, double target) { + // do not increase window under heavy memory pressure. + double memory_pressure = grpc_resource_quota_get_memory_pressure( + grpc_resource_user_quota(grpc_endpoint_get_resource_user(tfc->t->ep))); + if (memory_pressure > 0.8) { + target *= 1 - GPR_MIN(1, (memory_pressure - 0.8) / 0.1); + } + return target; +} + +grpc_chttp2_flowctl_action grpc_chttp2_flowctl_get_action( + grpc_chttp2_transport_flowctl* tfc, grpc_chttp2_stream_flowctl* sfc) { + grpc_chttp2_flowctl_action action; + memset(&action, 0, sizeof(action)); + uint32_t target_announced_window = grpc_chttp2_target_announced_window(tfc); + if (tfc->announced_window < target_announced_window / 2) { + action.send_transport_update = GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY; + } + // TODO(ncteisen): tune this + if (sfc != NULL && !sfc->s->read_closed) { + uint32_t sent_init_window = + tfc->t->settings[GRPC_SENT_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]; + if ((int64_t)sfc->local_window_delta > + (int64_t)sfc->announced_window_delta && + (int64_t)sfc->announced_window_delta + sent_init_window <= + sent_init_window / 2) { + action.send_stream_update = GRPC_CHTTP2_FLOWCTL_UPDATE_IMMEDIATELY; + } else if (sfc->local_window_delta > sfc->announced_window_delta) { + action.send_stream_update = GRPC_CHTTP2_FLOWCTL_QUEUE_UPDATE; + } + } + TRACEACTION(tfc, action); + return action; +} + +grpc_chttp2_flowctl_action grpc_chttp2_flowctl_get_bdp_action( + grpc_chttp2_transport_flowctl* tfc) { + grpc_chttp2_flowctl_action action; + memset(&action, 0, sizeof(action)); + if (tfc->enable_bdp_probe) { + action.need_ping = grpc_bdp_estimator_need_ping(&tfc->bdp_estimator); + + // get bdp estimate and update initial_window accordingly. + int64_t estimate = -1; + int32_t bdp = -1; + if (grpc_bdp_estimator_get_estimate(&tfc->bdp_estimator, &estimate)) { + double target = 1 + log2((double)estimate); + + // target might change based on how much memory pressure we are under + // TODO(ncteisen): experiment with setting target to be huge under low + // memory pressure. + target = get_target_under_memory_pressure(tfc, target); + + // run our target through the pid controller to stabilize change. + // TODO(ncteisen): experiment with other controllers here. + double bdp_guess = get_pid_controller_guess(tfc, target); + + // Though initial window 'could' drop to 0, we keep the floor at 128 + bdp = GPR_MAX((int32_t)bdp_guess, 128); + + grpc_chttp2_flowctl_urgency init_window_update_urgency = + delta_is_significant(tfc, bdp, + GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE); + if (init_window_update_urgency != GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED) { + action.send_setting_update = init_window_update_urgency; + action.initial_window_size = (uint32_t)bdp; + } + } + + // get bandwidth estimate and update max_frame accordingly. + double bw_dbl = -1; + if (grpc_bdp_estimator_get_bw(&tfc->bdp_estimator, &bw_dbl)) { + // we target the max of BDP or bandwidth in microseconds. + int32_t frame_size = (int32_t)GPR_CLAMP( + GPR_MAX((int32_t)GPR_CLAMP(bw_dbl, 0, INT_MAX) / 1000, bdp), 16384, + 16777215); + grpc_chttp2_flowctl_urgency frame_size_urgency = delta_is_significant( + tfc, frame_size, GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE); + if (frame_size_urgency != GRPC_CHTTP2_FLOWCTL_NO_ACTION_NEEDED) { + if (frame_size_urgency > action.send_setting_update) { + action.send_setting_update = frame_size_urgency; + } + action.max_frame_size = (uint32_t)frame_size; + } + } + } + + TRACEACTION(tfc, action); + return action; +} diff --git a/src/core/ext/transport/chttp2/transport/frame_data.c b/src/core/ext/transport/chttp2/transport/frame_data.c deleted file mode 100644 index 73aaab1802..0000000000 --- a/src/core/ext/transport/chttp2/transport/frame_data.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/frame_data.h" - -#include - -#include -#include -#include -#include -#include "src/core/ext/transport/chttp2/transport/internal.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/transport.h" - -grpc_error *grpc_chttp2_data_parser_init(grpc_chttp2_data_parser *parser) { - parser->state = GRPC_CHTTP2_DATA_FH_0; - parser->parsing_frame = NULL; - return GRPC_ERROR_NONE; -} - -void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx, - grpc_chttp2_data_parser *parser) { - if (parser->parsing_frame != NULL) { - GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( - exec_ctx, parser->parsing_frame, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Parser destroyed"), false)); - } - GRPC_ERROR_UNREF(parser->error); -} - -grpc_error *grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser *parser, - uint8_t flags, - uint32_t stream_id, - grpc_chttp2_stream *s) { - if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) { - char *msg; - gpr_asprintf(&msg, "unsupported data flags: 0x%02x", flags); - grpc_error *err = - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg), - GRPC_ERROR_INT_STREAM_ID, (intptr_t)stream_id); - gpr_free(msg); - return err; - } - - if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { - s->received_last_frame = true; - } else { - s->received_last_frame = false; - } - - return GRPC_ERROR_NONE; -} - -void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer *inbuf, - uint32_t write_bytes, int is_eof, - grpc_transport_one_way_stats *stats, - grpc_slice_buffer *outbuf) { - grpc_slice hdr; - uint8_t *p; - static const size_t header_size = 9; - - hdr = GRPC_SLICE_MALLOC(header_size); - p = GRPC_SLICE_START_PTR(hdr); - GPR_ASSERT(write_bytes < (1 << 24)); - *p++ = (uint8_t)(write_bytes >> 16); - *p++ = (uint8_t)(write_bytes >> 8); - *p++ = (uint8_t)(write_bytes); - *p++ = GRPC_CHTTP2_FRAME_DATA; - *p++ = is_eof ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0; - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); - grpc_slice_buffer_add(outbuf, hdr); - - grpc_slice_buffer_move_first_no_ref(inbuf, write_bytes, outbuf); - - stats->framing_bytes += header_size; - stats->data_bytes += write_bytes; -} - -grpc_error *grpc_deframe_unprocessed_incoming_frames( - grpc_exec_ctx *exec_ctx, grpc_chttp2_data_parser *p, grpc_chttp2_stream *s, - grpc_slice_buffer *slices, grpc_slice *slice_out, - grpc_byte_stream **stream_out) { - grpc_error *error = GRPC_ERROR_NONE; - grpc_chttp2_transport *t = s->t; - - while (slices->count > 0) { - uint8_t *beg = NULL; - uint8_t *end = NULL; - uint8_t *cur = NULL; - - grpc_slice slice = grpc_slice_buffer_take_first(slices); - - beg = GRPC_SLICE_START_PTR(slice); - end = GRPC_SLICE_END_PTR(slice); - cur = beg; - uint32_t message_flags; - char *msg; - - if (cur == end) { - grpc_slice_unref_internal(exec_ctx, slice); - continue; - } - - switch (p->state) { - case GRPC_CHTTP2_DATA_ERROR: - p->state = GRPC_CHTTP2_DATA_ERROR; - grpc_slice_unref_internal(exec_ctx, slice); - return GRPC_ERROR_REF(p->error); - case GRPC_CHTTP2_DATA_FH_0: - s->stats.incoming.framing_bytes++; - p->frame_type = *cur; - switch (p->frame_type) { - case 0: - p->is_frame_compressed = false; /* GPR_FALSE */ - break; - case 1: - p->is_frame_compressed = true; /* GPR_TRUE */ - break; - default: - gpr_asprintf(&msg, "Bad GRPC frame type 0x%02x", p->frame_type); - p->error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - p->error = grpc_error_set_int(p->error, GRPC_ERROR_INT_STREAM_ID, - (intptr_t)s->id); - gpr_free(msg); - msg = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); - p->error = grpc_error_set_str(p->error, GRPC_ERROR_STR_RAW_BYTES, - grpc_slice_from_copied_string(msg)); - gpr_free(msg); - p->error = - grpc_error_set_int(p->error, GRPC_ERROR_INT_OFFSET, cur - beg); - p->state = GRPC_CHTTP2_DATA_ERROR; - grpc_slice_unref_internal(exec_ctx, slice); - return GRPC_ERROR_REF(p->error); - } - if (++cur == end) { - p->state = GRPC_CHTTP2_DATA_FH_1; - grpc_slice_unref_internal(exec_ctx, slice); - continue; - } - /* fallthrough */ - case GRPC_CHTTP2_DATA_FH_1: - s->stats.incoming.framing_bytes++; - p->frame_size = ((uint32_t)*cur) << 24; - if (++cur == end) { - p->state = GRPC_CHTTP2_DATA_FH_2; - grpc_slice_unref_internal(exec_ctx, slice); - continue; - } - /* fallthrough */ - case GRPC_CHTTP2_DATA_FH_2: - s->stats.incoming.framing_bytes++; - p->frame_size |= ((uint32_t)*cur) << 16; - if (++cur == end) { - p->state = GRPC_CHTTP2_DATA_FH_3; - grpc_slice_unref_internal(exec_ctx, slice); - continue; - } - /* fallthrough */ - case GRPC_CHTTP2_DATA_FH_3: - s->stats.incoming.framing_bytes++; - p->frame_size |= ((uint32_t)*cur) << 8; - if (++cur == end) { - p->state = GRPC_CHTTP2_DATA_FH_4; - grpc_slice_unref_internal(exec_ctx, slice); - continue; - } - /* fallthrough */ - case GRPC_CHTTP2_DATA_FH_4: - s->stats.incoming.framing_bytes++; - GPR_ASSERT(stream_out != NULL); - GPR_ASSERT(p->parsing_frame == NULL); - p->frame_size |= ((uint32_t)*cur); - p->state = GRPC_CHTTP2_DATA_FRAME; - ++cur; - message_flags = 0; - if (p->is_frame_compressed) { - message_flags |= GRPC_WRITE_INTERNAL_COMPRESS; - } - p->parsing_frame = grpc_chttp2_incoming_byte_stream_create( - exec_ctx, t, s, p->frame_size, message_flags); - *stream_out = &p->parsing_frame->base; - if (p->parsing_frame->remaining_bytes == 0) { - GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( - exec_ctx, p->parsing_frame, GRPC_ERROR_NONE, true)); - p->parsing_frame = NULL; - p->state = GRPC_CHTTP2_DATA_FH_0; - } - s->pending_byte_stream = true; - - if (cur != end) { - grpc_slice_buffer_undo_take_first( - slices, - grpc_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); - } - grpc_slice_unref_internal(exec_ctx, slice); - return GRPC_ERROR_NONE; - case GRPC_CHTTP2_DATA_FRAME: { - GPR_ASSERT(p->parsing_frame != NULL); - GPR_ASSERT(slice_out != NULL); - if (cur == end) { - grpc_slice_unref_internal(exec_ctx, slice); - continue; - } - uint32_t remaining = (uint32_t)(end - cur); - if (remaining == p->frame_size) { - s->stats.incoming.data_bytes += remaining; - if (GRPC_ERROR_NONE != (error = grpc_chttp2_incoming_byte_stream_push( - exec_ctx, p->parsing_frame, - grpc_slice_sub(slice, (size_t)(cur - beg), - (size_t)(end - beg)), - slice_out))) { - grpc_slice_unref_internal(exec_ctx, slice); - return error; - } - if (GRPC_ERROR_NONE != - (error = grpc_chttp2_incoming_byte_stream_finished( - exec_ctx, p->parsing_frame, GRPC_ERROR_NONE, true))) { - grpc_slice_unref_internal(exec_ctx, slice); - return error; - } - p->parsing_frame = NULL; - p->state = GRPC_CHTTP2_DATA_FH_0; - grpc_slice_unref_internal(exec_ctx, slice); - return GRPC_ERROR_NONE; - } else if (remaining < p->frame_size) { - s->stats.incoming.data_bytes += remaining; - if (GRPC_ERROR_NONE != (error = grpc_chttp2_incoming_byte_stream_push( - exec_ctx, p->parsing_frame, - grpc_slice_sub(slice, (size_t)(cur - beg), - (size_t)(end - beg)), - slice_out))) { - return error; - } - p->frame_size -= remaining; - grpc_slice_unref_internal(exec_ctx, slice); - return GRPC_ERROR_NONE; - } else { - GPR_ASSERT(remaining > p->frame_size); - s->stats.incoming.data_bytes += p->frame_size; - if (GRPC_ERROR_NONE != - (grpc_chttp2_incoming_byte_stream_push( - exec_ctx, p->parsing_frame, - grpc_slice_sub(slice, (size_t)(cur - beg), - (size_t)(cur + p->frame_size - beg)), - slice_out))) { - grpc_slice_unref_internal(exec_ctx, slice); - return error; - } - if (GRPC_ERROR_NONE != - (error = grpc_chttp2_incoming_byte_stream_finished( - exec_ctx, p->parsing_frame, GRPC_ERROR_NONE, true))) { - grpc_slice_unref_internal(exec_ctx, slice); - return error; - } - p->parsing_frame = NULL; - p->state = GRPC_CHTTP2_DATA_FH_0; - cur += p->frame_size; - grpc_slice_buffer_undo_take_first( - slices, - grpc_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); - grpc_slice_unref_internal(exec_ctx, slice); - return GRPC_ERROR_NONE; - } - } - } - } - - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_chttp2_data_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - if (!s->pending_byte_stream) { - grpc_slice_ref_internal(slice); - grpc_slice_buffer_add(&s->frame_storage, slice); - grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); - } else if (s->on_next) { - GPR_ASSERT(s->frame_storage.length == 0); - grpc_slice_ref_internal(slice); - grpc_slice_buffer_add(&s->unprocessed_incoming_frames_buffer, slice); - GRPC_CLOSURE_SCHED(exec_ctx, s->on_next, GRPC_ERROR_NONE); - s->on_next = NULL; - s->unprocessed_incoming_frames_decompressed = false; - } else { - grpc_slice_ref_internal(slice); - grpc_slice_buffer_add(&s->frame_storage, slice); - } - - if (is_last && s->received_last_frame) { - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, - GRPC_ERROR_NONE); - } - - return GRPC_ERROR_NONE; -} diff --git a/src/core/ext/transport/chttp2/transport/frame_data.cc b/src/core/ext/transport/chttp2/transport/frame_data.cc new file mode 100644 index 0000000000..73aaab1802 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_data.cc @@ -0,0 +1,318 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/frame_data.h" + +#include + +#include +#include +#include +#include +#include "src/core/ext/transport/chttp2/transport/internal.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/transport.h" + +grpc_error *grpc_chttp2_data_parser_init(grpc_chttp2_data_parser *parser) { + parser->state = GRPC_CHTTP2_DATA_FH_0; + parser->parsing_frame = NULL; + return GRPC_ERROR_NONE; +} + +void grpc_chttp2_data_parser_destroy(grpc_exec_ctx *exec_ctx, + grpc_chttp2_data_parser *parser) { + if (parser->parsing_frame != NULL) { + GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( + exec_ctx, parser->parsing_frame, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Parser destroyed"), false)); + } + GRPC_ERROR_UNREF(parser->error); +} + +grpc_error *grpc_chttp2_data_parser_begin_frame(grpc_chttp2_data_parser *parser, + uint8_t flags, + uint32_t stream_id, + grpc_chttp2_stream *s) { + if (flags & ~GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + char *msg; + gpr_asprintf(&msg, "unsupported data flags: 0x%02x", flags); + grpc_error *err = + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg), + GRPC_ERROR_INT_STREAM_ID, (intptr_t)stream_id); + gpr_free(msg); + return err; + } + + if (flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) { + s->received_last_frame = true; + } else { + s->received_last_frame = false; + } + + return GRPC_ERROR_NONE; +} + +void grpc_chttp2_encode_data(uint32_t id, grpc_slice_buffer *inbuf, + uint32_t write_bytes, int is_eof, + grpc_transport_one_way_stats *stats, + grpc_slice_buffer *outbuf) { + grpc_slice hdr; + uint8_t *p; + static const size_t header_size = 9; + + hdr = GRPC_SLICE_MALLOC(header_size); + p = GRPC_SLICE_START_PTR(hdr); + GPR_ASSERT(write_bytes < (1 << 24)); + *p++ = (uint8_t)(write_bytes >> 16); + *p++ = (uint8_t)(write_bytes >> 8); + *p++ = (uint8_t)(write_bytes); + *p++ = GRPC_CHTTP2_FRAME_DATA; + *p++ = is_eof ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0; + *p++ = (uint8_t)(id >> 24); + *p++ = (uint8_t)(id >> 16); + *p++ = (uint8_t)(id >> 8); + *p++ = (uint8_t)(id); + grpc_slice_buffer_add(outbuf, hdr); + + grpc_slice_buffer_move_first_no_ref(inbuf, write_bytes, outbuf); + + stats->framing_bytes += header_size; + stats->data_bytes += write_bytes; +} + +grpc_error *grpc_deframe_unprocessed_incoming_frames( + grpc_exec_ctx *exec_ctx, grpc_chttp2_data_parser *p, grpc_chttp2_stream *s, + grpc_slice_buffer *slices, grpc_slice *slice_out, + grpc_byte_stream **stream_out) { + grpc_error *error = GRPC_ERROR_NONE; + grpc_chttp2_transport *t = s->t; + + while (slices->count > 0) { + uint8_t *beg = NULL; + uint8_t *end = NULL; + uint8_t *cur = NULL; + + grpc_slice slice = grpc_slice_buffer_take_first(slices); + + beg = GRPC_SLICE_START_PTR(slice); + end = GRPC_SLICE_END_PTR(slice); + cur = beg; + uint32_t message_flags; + char *msg; + + if (cur == end) { + grpc_slice_unref_internal(exec_ctx, slice); + continue; + } + + switch (p->state) { + case GRPC_CHTTP2_DATA_ERROR: + p->state = GRPC_CHTTP2_DATA_ERROR; + grpc_slice_unref_internal(exec_ctx, slice); + return GRPC_ERROR_REF(p->error); + case GRPC_CHTTP2_DATA_FH_0: + s->stats.incoming.framing_bytes++; + p->frame_type = *cur; + switch (p->frame_type) { + case 0: + p->is_frame_compressed = false; /* GPR_FALSE */ + break; + case 1: + p->is_frame_compressed = true; /* GPR_TRUE */ + break; + default: + gpr_asprintf(&msg, "Bad GRPC frame type 0x%02x", p->frame_type); + p->error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + p->error = grpc_error_set_int(p->error, GRPC_ERROR_INT_STREAM_ID, + (intptr_t)s->id); + gpr_free(msg); + msg = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + p->error = grpc_error_set_str(p->error, GRPC_ERROR_STR_RAW_BYTES, + grpc_slice_from_copied_string(msg)); + gpr_free(msg); + p->error = + grpc_error_set_int(p->error, GRPC_ERROR_INT_OFFSET, cur - beg); + p->state = GRPC_CHTTP2_DATA_ERROR; + grpc_slice_unref_internal(exec_ctx, slice); + return GRPC_ERROR_REF(p->error); + } + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_1; + grpc_slice_unref_internal(exec_ctx, slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_1: + s->stats.incoming.framing_bytes++; + p->frame_size = ((uint32_t)*cur) << 24; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_2; + grpc_slice_unref_internal(exec_ctx, slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_2: + s->stats.incoming.framing_bytes++; + p->frame_size |= ((uint32_t)*cur) << 16; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_3; + grpc_slice_unref_internal(exec_ctx, slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_3: + s->stats.incoming.framing_bytes++; + p->frame_size |= ((uint32_t)*cur) << 8; + if (++cur == end) { + p->state = GRPC_CHTTP2_DATA_FH_4; + grpc_slice_unref_internal(exec_ctx, slice); + continue; + } + /* fallthrough */ + case GRPC_CHTTP2_DATA_FH_4: + s->stats.incoming.framing_bytes++; + GPR_ASSERT(stream_out != NULL); + GPR_ASSERT(p->parsing_frame == NULL); + p->frame_size |= ((uint32_t)*cur); + p->state = GRPC_CHTTP2_DATA_FRAME; + ++cur; + message_flags = 0; + if (p->is_frame_compressed) { + message_flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + p->parsing_frame = grpc_chttp2_incoming_byte_stream_create( + exec_ctx, t, s, p->frame_size, message_flags); + *stream_out = &p->parsing_frame->base; + if (p->parsing_frame->remaining_bytes == 0) { + GRPC_ERROR_UNREF(grpc_chttp2_incoming_byte_stream_finished( + exec_ctx, p->parsing_frame, GRPC_ERROR_NONE, true)); + p->parsing_frame = NULL; + p->state = GRPC_CHTTP2_DATA_FH_0; + } + s->pending_byte_stream = true; + + if (cur != end) { + grpc_slice_buffer_undo_take_first( + slices, + grpc_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); + } + grpc_slice_unref_internal(exec_ctx, slice); + return GRPC_ERROR_NONE; + case GRPC_CHTTP2_DATA_FRAME: { + GPR_ASSERT(p->parsing_frame != NULL); + GPR_ASSERT(slice_out != NULL); + if (cur == end) { + grpc_slice_unref_internal(exec_ctx, slice); + continue; + } + uint32_t remaining = (uint32_t)(end - cur); + if (remaining == p->frame_size) { + s->stats.incoming.data_bytes += remaining; + if (GRPC_ERROR_NONE != (error = grpc_chttp2_incoming_byte_stream_push( + exec_ctx, p->parsing_frame, + grpc_slice_sub(slice, (size_t)(cur - beg), + (size_t)(end - beg)), + slice_out))) { + grpc_slice_unref_internal(exec_ctx, slice); + return error; + } + if (GRPC_ERROR_NONE != + (error = grpc_chttp2_incoming_byte_stream_finished( + exec_ctx, p->parsing_frame, GRPC_ERROR_NONE, true))) { + grpc_slice_unref_internal(exec_ctx, slice); + return error; + } + p->parsing_frame = NULL; + p->state = GRPC_CHTTP2_DATA_FH_0; + grpc_slice_unref_internal(exec_ctx, slice); + return GRPC_ERROR_NONE; + } else if (remaining < p->frame_size) { + s->stats.incoming.data_bytes += remaining; + if (GRPC_ERROR_NONE != (error = grpc_chttp2_incoming_byte_stream_push( + exec_ctx, p->parsing_frame, + grpc_slice_sub(slice, (size_t)(cur - beg), + (size_t)(end - beg)), + slice_out))) { + return error; + } + p->frame_size -= remaining; + grpc_slice_unref_internal(exec_ctx, slice); + return GRPC_ERROR_NONE; + } else { + GPR_ASSERT(remaining > p->frame_size); + s->stats.incoming.data_bytes += p->frame_size; + if (GRPC_ERROR_NONE != + (grpc_chttp2_incoming_byte_stream_push( + exec_ctx, p->parsing_frame, + grpc_slice_sub(slice, (size_t)(cur - beg), + (size_t)(cur + p->frame_size - beg)), + slice_out))) { + grpc_slice_unref_internal(exec_ctx, slice); + return error; + } + if (GRPC_ERROR_NONE != + (error = grpc_chttp2_incoming_byte_stream_finished( + exec_ctx, p->parsing_frame, GRPC_ERROR_NONE, true))) { + grpc_slice_unref_internal(exec_ctx, slice); + return error; + } + p->parsing_frame = NULL; + p->state = GRPC_CHTTP2_DATA_FH_0; + cur += p->frame_size; + grpc_slice_buffer_undo_take_first( + slices, + grpc_slice_sub(slice, (size_t)(cur - beg), (size_t)(end - beg))); + grpc_slice_unref_internal(exec_ctx, slice); + return GRPC_ERROR_NONE; + } + } + } + } + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_chttp2_data_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + if (!s->pending_byte_stream) { + grpc_slice_ref_internal(slice); + grpc_slice_buffer_add(&s->frame_storage, slice); + grpc_chttp2_maybe_complete_recv_message(exec_ctx, t, s); + } else if (s->on_next) { + GPR_ASSERT(s->frame_storage.length == 0); + grpc_slice_ref_internal(slice); + grpc_slice_buffer_add(&s->unprocessed_incoming_frames_buffer, slice); + GRPC_CLOSURE_SCHED(exec_ctx, s->on_next, GRPC_ERROR_NONE); + s->on_next = NULL; + s->unprocessed_incoming_frames_decompressed = false; + } else { + grpc_slice_ref_internal(slice); + grpc_slice_buffer_add(&s->frame_storage, slice); + } + + if (is_last && s->received_last_frame) { + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, + GRPC_ERROR_NONE); + } + + return GRPC_ERROR_NONE; +} diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.c b/src/core/ext/transport/chttp2/transport/frame_goaway.c deleted file mode 100644 index 78ec08e177..0000000000 --- a/src/core/ext/transport/chttp2/transport/frame_goaway.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/frame_goaway.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include - -#include -#include -#include - -void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p) { - p->debug_data = NULL; -} - -void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p) { - gpr_free(p->debug_data); -} - -grpc_error *grpc_chttp2_goaway_parser_begin_frame(grpc_chttp2_goaway_parser *p, - uint32_t length, - uint8_t flags) { - if (length < 8) { - char *msg; - gpr_asprintf(&msg, "goaway frame too short (%d bytes)", length); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - - gpr_free(p->debug_data); - p->debug_length = length - 8; - p->debug_data = (char *)gpr_malloc(p->debug_length); - p->debug_pos = 0; - p->state = GRPC_CHTTP2_GOAWAY_LSI0; - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_chttp2_goaway_parser_parse(grpc_exec_ctx *exec_ctx, - void *parser, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - uint8_t *const beg = GRPC_SLICE_START_PTR(slice); - uint8_t *const end = GRPC_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_goaway_parser *p = (grpc_chttp2_goaway_parser *)parser; - - switch (p->state) { - case GRPC_CHTTP2_GOAWAY_LSI0: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_LSI0; - return GRPC_ERROR_NONE; - } - p->last_stream_id = ((uint32_t)*cur) << 24; - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_LSI1: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_LSI1; - return GRPC_ERROR_NONE; - } - p->last_stream_id |= ((uint32_t)*cur) << 16; - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_LSI2: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_LSI2; - return GRPC_ERROR_NONE; - } - p->last_stream_id |= ((uint32_t)*cur) << 8; - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_LSI3: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_LSI3; - return GRPC_ERROR_NONE; - } - p->last_stream_id |= ((uint32_t)*cur); - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_ERR0: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_ERR0; - return GRPC_ERROR_NONE; - } - p->error_code = ((uint32_t)*cur) << 24; - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_ERR1: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_ERR1; - return GRPC_ERROR_NONE; - } - p->error_code |= ((uint32_t)*cur) << 16; - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_ERR2: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_ERR2; - return GRPC_ERROR_NONE; - } - p->error_code |= ((uint32_t)*cur) << 8; - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_ERR3: - if (cur == end) { - p->state = GRPC_CHTTP2_GOAWAY_ERR3; - return GRPC_ERROR_NONE; - } - p->error_code |= ((uint32_t)*cur); - ++cur; - /* fallthrough */ - case GRPC_CHTTP2_GOAWAY_DEBUG: - if (end != cur) - memcpy(p->debug_data + p->debug_pos, cur, (size_t)(end - cur)); - GPR_ASSERT((size_t)(end - cur) < UINT32_MAX - p->debug_pos); - p->debug_pos += (uint32_t)(end - cur); - p->state = GRPC_CHTTP2_GOAWAY_DEBUG; - if (is_last) { - grpc_chttp2_add_incoming_goaway( - exec_ctx, t, (uint32_t)p->error_code, - grpc_slice_new(p->debug_data, p->debug_length, gpr_free)); - p->debug_data = NULL; - } - return GRPC_ERROR_NONE; - } - GPR_UNREACHABLE_CODE( - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); -} - -void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code, - grpc_slice debug_data, - grpc_slice_buffer *slice_buffer) { - grpc_slice header = GRPC_SLICE_MALLOC(9 + 4 + 4); - uint8_t *p = GRPC_SLICE_START_PTR(header); - uint32_t frame_length; - GPR_ASSERT(GRPC_SLICE_LENGTH(debug_data) < UINT32_MAX - 4 - 4); - frame_length = 4 + 4 + (uint32_t)GRPC_SLICE_LENGTH(debug_data); - - /* frame header: length */ - *p++ = (uint8_t)(frame_length >> 16); - *p++ = (uint8_t)(frame_length >> 8); - *p++ = (uint8_t)(frame_length); - /* frame header: type */ - *p++ = GRPC_CHTTP2_FRAME_GOAWAY; - /* frame header: flags */ - *p++ = 0; - /* frame header: stream id */ - *p++ = 0; - *p++ = 0; - *p++ = 0; - *p++ = 0; - /* payload: last stream id */ - *p++ = (uint8_t)(last_stream_id >> 24); - *p++ = (uint8_t)(last_stream_id >> 16); - *p++ = (uint8_t)(last_stream_id >> 8); - *p++ = (uint8_t)(last_stream_id); - /* payload: error code */ - *p++ = (uint8_t)(error_code >> 24); - *p++ = (uint8_t)(error_code >> 16); - *p++ = (uint8_t)(error_code >> 8); - *p++ = (uint8_t)(error_code); - GPR_ASSERT(p == GRPC_SLICE_END_PTR(header)); - grpc_slice_buffer_add(slice_buffer, header); - grpc_slice_buffer_add(slice_buffer, debug_data); -} diff --git a/src/core/ext/transport/chttp2/transport/frame_goaway.cc b/src/core/ext/transport/chttp2/transport/frame_goaway.cc new file mode 100644 index 0000000000..78ec08e177 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_goaway.cc @@ -0,0 +1,183 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/frame_goaway.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +void grpc_chttp2_goaway_parser_init(grpc_chttp2_goaway_parser *p) { + p->debug_data = NULL; +} + +void grpc_chttp2_goaway_parser_destroy(grpc_chttp2_goaway_parser *p) { + gpr_free(p->debug_data); +} + +grpc_error *grpc_chttp2_goaway_parser_begin_frame(grpc_chttp2_goaway_parser *p, + uint32_t length, + uint8_t flags) { + if (length < 8) { + char *msg; + gpr_asprintf(&msg, "goaway frame too short (%d bytes)", length); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + + gpr_free(p->debug_data); + p->debug_length = length - 8; + p->debug_data = (char *)gpr_malloc(p->debug_length); + p->debug_pos = 0; + p->state = GRPC_CHTTP2_GOAWAY_LSI0; + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_chttp2_goaway_parser_parse(grpc_exec_ctx *exec_ctx, + void *parser, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + uint8_t *const beg = GRPC_SLICE_START_PTR(slice); + uint8_t *const end = GRPC_SLICE_END_PTR(slice); + uint8_t *cur = beg; + grpc_chttp2_goaway_parser *p = (grpc_chttp2_goaway_parser *)parser; + + switch (p->state) { + case GRPC_CHTTP2_GOAWAY_LSI0: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI0; + return GRPC_ERROR_NONE; + } + p->last_stream_id = ((uint32_t)*cur) << 24; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI1: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI1; + return GRPC_ERROR_NONE; + } + p->last_stream_id |= ((uint32_t)*cur) << 16; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI2: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI2; + return GRPC_ERROR_NONE; + } + p->last_stream_id |= ((uint32_t)*cur) << 8; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_LSI3: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_LSI3; + return GRPC_ERROR_NONE; + } + p->last_stream_id |= ((uint32_t)*cur); + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR0: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR0; + return GRPC_ERROR_NONE; + } + p->error_code = ((uint32_t)*cur) << 24; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR1: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR1; + return GRPC_ERROR_NONE; + } + p->error_code |= ((uint32_t)*cur) << 16; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR2: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR2; + return GRPC_ERROR_NONE; + } + p->error_code |= ((uint32_t)*cur) << 8; + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_ERR3: + if (cur == end) { + p->state = GRPC_CHTTP2_GOAWAY_ERR3; + return GRPC_ERROR_NONE; + } + p->error_code |= ((uint32_t)*cur); + ++cur; + /* fallthrough */ + case GRPC_CHTTP2_GOAWAY_DEBUG: + if (end != cur) + memcpy(p->debug_data + p->debug_pos, cur, (size_t)(end - cur)); + GPR_ASSERT((size_t)(end - cur) < UINT32_MAX - p->debug_pos); + p->debug_pos += (uint32_t)(end - cur); + p->state = GRPC_CHTTP2_GOAWAY_DEBUG; + if (is_last) { + grpc_chttp2_add_incoming_goaway( + exec_ctx, t, (uint32_t)p->error_code, + grpc_slice_new(p->debug_data, p->debug_length, gpr_free)); + p->debug_data = NULL; + } + return GRPC_ERROR_NONE; + } + GPR_UNREACHABLE_CODE( + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); +} + +void grpc_chttp2_goaway_append(uint32_t last_stream_id, uint32_t error_code, + grpc_slice debug_data, + grpc_slice_buffer *slice_buffer) { + grpc_slice header = GRPC_SLICE_MALLOC(9 + 4 + 4); + uint8_t *p = GRPC_SLICE_START_PTR(header); + uint32_t frame_length; + GPR_ASSERT(GRPC_SLICE_LENGTH(debug_data) < UINT32_MAX - 4 - 4); + frame_length = 4 + 4 + (uint32_t)GRPC_SLICE_LENGTH(debug_data); + + /* frame header: length */ + *p++ = (uint8_t)(frame_length >> 16); + *p++ = (uint8_t)(frame_length >> 8); + *p++ = (uint8_t)(frame_length); + /* frame header: type */ + *p++ = GRPC_CHTTP2_FRAME_GOAWAY; + /* frame header: flags */ + *p++ = 0; + /* frame header: stream id */ + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + /* payload: last stream id */ + *p++ = (uint8_t)(last_stream_id >> 24); + *p++ = (uint8_t)(last_stream_id >> 16); + *p++ = (uint8_t)(last_stream_id >> 8); + *p++ = (uint8_t)(last_stream_id); + /* payload: error code */ + *p++ = (uint8_t)(error_code >> 24); + *p++ = (uint8_t)(error_code >> 16); + *p++ = (uint8_t)(error_code >> 8); + *p++ = (uint8_t)(error_code); + GPR_ASSERT(p == GRPC_SLICE_END_PTR(header)); + grpc_slice_buffer_add(slice_buffer, header); + grpc_slice_buffer_add(slice_buffer, debug_data); +} diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.c b/src/core/ext/transport/chttp2/transport/frame_ping.c deleted file mode 100644 index d431d6b2df..0000000000 --- a/src/core/ext/transport/chttp2/transport/frame_ping.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/frame_ping.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include - -#include -#include -#include - -static bool g_disable_ping_ack = false; - -grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes) { - grpc_slice slice = GRPC_SLICE_MALLOC(9 + 8); - uint8_t *p = GRPC_SLICE_START_PTR(slice); - - *p++ = 0; - *p++ = 0; - *p++ = 8; - *p++ = GRPC_CHTTP2_FRAME_PING; - *p++ = ack ? 1 : 0; - *p++ = 0; - *p++ = 0; - *p++ = 0; - *p++ = 0; - *p++ = (uint8_t)(opaque_8bytes >> 56); - *p++ = (uint8_t)(opaque_8bytes >> 48); - *p++ = (uint8_t)(opaque_8bytes >> 40); - *p++ = (uint8_t)(opaque_8bytes >> 32); - *p++ = (uint8_t)(opaque_8bytes >> 24); - *p++ = (uint8_t)(opaque_8bytes >> 16); - *p++ = (uint8_t)(opaque_8bytes >> 8); - *p++ = (uint8_t)(opaque_8bytes); - - return slice; -} - -grpc_error *grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser *parser, - uint32_t length, - uint8_t flags) { - if (flags & 0xfe || length != 8) { - char *msg; - gpr_asprintf(&msg, "invalid ping: length=%d, flags=%02x", length, flags); - grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - parser->byte = 0; - parser->is_ack = flags; - parser->opaque_8bytes = 0; - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_chttp2_ping_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - uint8_t *const beg = GRPC_SLICE_START_PTR(slice); - uint8_t *const end = GRPC_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_ping_parser *p = (grpc_chttp2_ping_parser *)parser; - - while (p->byte != 8 && cur != end) { - p->opaque_8bytes |= (((uint64_t)*cur) << (56 - 8 * p->byte)); - cur++; - p->byte++; - } - - if (p->byte == 8) { - GPR_ASSERT(is_last); - if (p->is_ack) { - grpc_chttp2_ack_ping(exec_ctx, t, p->opaque_8bytes); - } else { - if (!t->is_client) { - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec next_allowed_ping = - gpr_time_add(t->ping_recv_state.last_ping_recv_time, - t->ping_policy.min_recv_ping_interval_without_data); - - if (t->keepalive_permit_without_calls == 0 && - grpc_chttp2_stream_map_size(&t->stream_map) == 0) { - /* According to RFC1122, the interval of TCP Keep-Alive is default to - no less than two hours. When there is no outstanding streams, we - restrict the number of PINGS equivalent to TCP Keep-Alive. */ - next_allowed_ping = - gpr_time_add(t->ping_recv_state.last_ping_recv_time, - gpr_time_from_seconds(7200, GPR_TIMESPAN)); - } - - if (gpr_time_cmp(next_allowed_ping, now) > 0) { - grpc_chttp2_add_ping_strike(exec_ctx, t); - } - - t->ping_recv_state.last_ping_recv_time = now; - } - if (!g_disable_ping_ack) { - if (t->ping_ack_count == t->ping_ack_capacity) { - t->ping_ack_capacity = GPR_MAX(t->ping_ack_capacity * 3 / 2, 3); - t->ping_acks = (uint64_t *)gpr_realloc( - t->ping_acks, t->ping_ack_capacity * sizeof(*t->ping_acks)); - } - t->ping_acks[t->ping_ack_count++] = p->opaque_8bytes; - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE); - } - } - } - - return GRPC_ERROR_NONE; -} - -void grpc_set_disable_ping_ack(bool disable_ping_ack) { - g_disable_ping_ack = disable_ping_ack; -} diff --git a/src/core/ext/transport/chttp2/transport/frame_ping.cc b/src/core/ext/transport/chttp2/transport/frame_ping.cc new file mode 100644 index 0000000000..d431d6b2df --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_ping.cc @@ -0,0 +1,131 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/frame_ping.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +static bool g_disable_ping_ack = false; + +grpc_slice grpc_chttp2_ping_create(uint8_t ack, uint64_t opaque_8bytes) { + grpc_slice slice = GRPC_SLICE_MALLOC(9 + 8); + uint8_t *p = GRPC_SLICE_START_PTR(slice); + + *p++ = 0; + *p++ = 0; + *p++ = 8; + *p++ = GRPC_CHTTP2_FRAME_PING; + *p++ = ack ? 1 : 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p++ = (uint8_t)(opaque_8bytes >> 56); + *p++ = (uint8_t)(opaque_8bytes >> 48); + *p++ = (uint8_t)(opaque_8bytes >> 40); + *p++ = (uint8_t)(opaque_8bytes >> 32); + *p++ = (uint8_t)(opaque_8bytes >> 24); + *p++ = (uint8_t)(opaque_8bytes >> 16); + *p++ = (uint8_t)(opaque_8bytes >> 8); + *p++ = (uint8_t)(opaque_8bytes); + + return slice; +} + +grpc_error *grpc_chttp2_ping_parser_begin_frame(grpc_chttp2_ping_parser *parser, + uint32_t length, + uint8_t flags) { + if (flags & 0xfe || length != 8) { + char *msg; + gpr_asprintf(&msg, "invalid ping: length=%d, flags=%02x", length, flags); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + parser->byte = 0; + parser->is_ack = flags; + parser->opaque_8bytes = 0; + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_chttp2_ping_parser_parse(grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + uint8_t *const beg = GRPC_SLICE_START_PTR(slice); + uint8_t *const end = GRPC_SLICE_END_PTR(slice); + uint8_t *cur = beg; + grpc_chttp2_ping_parser *p = (grpc_chttp2_ping_parser *)parser; + + while (p->byte != 8 && cur != end) { + p->opaque_8bytes |= (((uint64_t)*cur) << (56 - 8 * p->byte)); + cur++; + p->byte++; + } + + if (p->byte == 8) { + GPR_ASSERT(is_last); + if (p->is_ack) { + grpc_chttp2_ack_ping(exec_ctx, t, p->opaque_8bytes); + } else { + if (!t->is_client) { + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec next_allowed_ping = + gpr_time_add(t->ping_recv_state.last_ping_recv_time, + t->ping_policy.min_recv_ping_interval_without_data); + + if (t->keepalive_permit_without_calls == 0 && + grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + /* According to RFC1122, the interval of TCP Keep-Alive is default to + no less than two hours. When there is no outstanding streams, we + restrict the number of PINGS equivalent to TCP Keep-Alive. */ + next_allowed_ping = + gpr_time_add(t->ping_recv_state.last_ping_recv_time, + gpr_time_from_seconds(7200, GPR_TIMESPAN)); + } + + if (gpr_time_cmp(next_allowed_ping, now) > 0) { + grpc_chttp2_add_ping_strike(exec_ctx, t); + } + + t->ping_recv_state.last_ping_recv_time = now; + } + if (!g_disable_ping_ack) { + if (t->ping_ack_count == t->ping_ack_capacity) { + t->ping_ack_capacity = GPR_MAX(t->ping_ack_capacity * 3 / 2, 3); + t->ping_acks = (uint64_t *)gpr_realloc( + t->ping_acks, t->ping_ack_capacity * sizeof(*t->ping_acks)); + } + t->ping_acks[t->ping_ack_count++] = p->opaque_8bytes; + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_PING_RESPONSE); + } + } + } + + return GRPC_ERROR_NONE; +} + +void grpc_set_disable_ping_ack(bool disable_ping_ack) { + g_disable_ping_ack = disable_ping_ack; +} diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c b/src/core/ext/transport/chttp2/transport/frame_rst_stream.c deleted file mode 100644 index 0133b6efa2..0000000000 --- a/src/core/ext/transport/chttp2/transport/frame_rst_stream.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include -#include -#include - -#include "src/core/ext/transport/chttp2/transport/frame.h" -#include "src/core/lib/transport/http2_errors.h" - -grpc_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code, - grpc_transport_one_way_stats *stats) { - static const size_t frame_size = 13; - grpc_slice slice = GRPC_SLICE_MALLOC(frame_size); - stats->framing_bytes += frame_size; - uint8_t *p = GRPC_SLICE_START_PTR(slice); - - // Frame size. - *p++ = 0; - *p++ = 0; - *p++ = 4; - // Frame type. - *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; - // Flags. - *p++ = 0; - // Stream ID. - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); - // Error code. - *p++ = (uint8_t)(code >> 24); - *p++ = (uint8_t)(code >> 16); - *p++ = (uint8_t)(code >> 8); - *p++ = (uint8_t)(code); - - return slice; -} - -grpc_error *grpc_chttp2_rst_stream_parser_begin_frame( - grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t flags) { - if (length != 4) { - char *msg; - gpr_asprintf(&msg, "invalid rst_stream: length=%d, flags=%02x", length, - flags); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - parser->byte = 0; - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_chttp2_rst_stream_parser_parse(grpc_exec_ctx *exec_ctx, - void *parser, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - uint8_t *const beg = GRPC_SLICE_START_PTR(slice); - uint8_t *const end = GRPC_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_rst_stream_parser *p = (grpc_chttp2_rst_stream_parser *)parser; - - while (p->byte != 4 && cur != end) { - p->reason_bytes[p->byte] = *cur; - cur++; - p->byte++; - } - s->stats.incoming.framing_bytes += (uint64_t)(end - cur); - - if (p->byte == 4) { - GPR_ASSERT(is_last); - uint32_t reason = (((uint32_t)p->reason_bytes[0]) << 24) | - (((uint32_t)p->reason_bytes[1]) << 16) | - (((uint32_t)p->reason_bytes[2]) << 8) | - (((uint32_t)p->reason_bytes[3])); - grpc_error *error = GRPC_ERROR_NONE; - if (reason != GRPC_HTTP2_NO_ERROR || s->metadata_buffer[1].size == 0) { - char *message; - gpr_asprintf(&message, "Received RST_STREAM with error code %d", reason); - error = grpc_error_set_int( - grpc_error_set_str(GRPC_ERROR_CREATE_FROM_STATIC_STRING("RST_STREAM"), - GRPC_ERROR_STR_GRPC_MESSAGE, - grpc_slice_from_copied_string(message)), - GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)reason); - gpr_free(message); - } - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, error); - } - - return GRPC_ERROR_NONE; -} diff --git a/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc new file mode 100644 index 0000000000..0133b6efa2 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_rst_stream.cc @@ -0,0 +1,110 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/frame_rst_stream.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/transport/http2_errors.h" + +grpc_slice grpc_chttp2_rst_stream_create(uint32_t id, uint32_t code, + grpc_transport_one_way_stats *stats) { + static const size_t frame_size = 13; + grpc_slice slice = GRPC_SLICE_MALLOC(frame_size); + stats->framing_bytes += frame_size; + uint8_t *p = GRPC_SLICE_START_PTR(slice); + + // Frame size. + *p++ = 0; + *p++ = 0; + *p++ = 4; + // Frame type. + *p++ = GRPC_CHTTP2_FRAME_RST_STREAM; + // Flags. + *p++ = 0; + // Stream ID. + *p++ = (uint8_t)(id >> 24); + *p++ = (uint8_t)(id >> 16); + *p++ = (uint8_t)(id >> 8); + *p++ = (uint8_t)(id); + // Error code. + *p++ = (uint8_t)(code >> 24); + *p++ = (uint8_t)(code >> 16); + *p++ = (uint8_t)(code >> 8); + *p++ = (uint8_t)(code); + + return slice; +} + +grpc_error *grpc_chttp2_rst_stream_parser_begin_frame( + grpc_chttp2_rst_stream_parser *parser, uint32_t length, uint8_t flags) { + if (length != 4) { + char *msg; + gpr_asprintf(&msg, "invalid rst_stream: length=%d, flags=%02x", length, + flags); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + parser->byte = 0; + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_chttp2_rst_stream_parser_parse(grpc_exec_ctx *exec_ctx, + void *parser, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + uint8_t *const beg = GRPC_SLICE_START_PTR(slice); + uint8_t *const end = GRPC_SLICE_END_PTR(slice); + uint8_t *cur = beg; + grpc_chttp2_rst_stream_parser *p = (grpc_chttp2_rst_stream_parser *)parser; + + while (p->byte != 4 && cur != end) { + p->reason_bytes[p->byte] = *cur; + cur++; + p->byte++; + } + s->stats.incoming.framing_bytes += (uint64_t)(end - cur); + + if (p->byte == 4) { + GPR_ASSERT(is_last); + uint32_t reason = (((uint32_t)p->reason_bytes[0]) << 24) | + (((uint32_t)p->reason_bytes[1]) << 16) | + (((uint32_t)p->reason_bytes[2]) << 8) | + (((uint32_t)p->reason_bytes[3])); + grpc_error *error = GRPC_ERROR_NONE; + if (reason != GRPC_HTTP2_NO_ERROR || s->metadata_buffer[1].size == 0) { + char *message; + gpr_asprintf(&message, "Received RST_STREAM with error code %d", reason); + error = grpc_error_set_int( + grpc_error_set_str(GRPC_ERROR_CREATE_FROM_STATIC_STRING("RST_STREAM"), + GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_copied_string(message)), + GRPC_ERROR_INT_HTTP2_ERROR, (intptr_t)reason); + gpr_free(message); + } + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, error); + } + + return GRPC_ERROR_NONE; +} diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.c b/src/core/ext/transport/chttp2/transport/frame_settings.c deleted file mode 100644 index 2995bf7310..0000000000 --- a/src/core/ext/transport/chttp2/transport/frame_settings.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/frame_settings.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include - -#include -#include -#include -#include - -#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" -#include "src/core/ext/transport/chttp2/transport/frame.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/transport/http2_errors.h" - -static uint8_t *fill_header(uint8_t *out, uint32_t length, uint8_t flags) { - *out++ = (uint8_t)(length >> 16); - *out++ = (uint8_t)(length >> 8); - *out++ = (uint8_t)(length); - *out++ = GRPC_CHTTP2_FRAME_SETTINGS; - *out++ = flags; - *out++ = 0; - *out++ = 0; - *out++ = 0; - *out++ = 0; - return out; -} - -grpc_slice grpc_chttp2_settings_create(uint32_t *old_settings, - const uint32_t *new_settings, - uint32_t force_mask, size_t count) { - size_t i; - uint32_t n = 0; - grpc_slice output; - uint8_t *p; - - for (i = 0; i < count; i++) { - n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0); - } - - output = GRPC_SLICE_MALLOC(9 + 6 * n); - p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0); - - for (i = 0; i < count; i++) { - if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) { - *p++ = (uint8_t)(grpc_setting_id_to_wire_id[i] >> 8); - *p++ = (uint8_t)(grpc_setting_id_to_wire_id[i]); - *p++ = (uint8_t)(new_settings[i] >> 24); - *p++ = (uint8_t)(new_settings[i] >> 16); - *p++ = (uint8_t)(new_settings[i] >> 8); - *p++ = (uint8_t)(new_settings[i]); - old_settings[i] = new_settings[i]; - } - } - - GPR_ASSERT(p == GRPC_SLICE_END_PTR(output)); - - return output; -} - -grpc_slice grpc_chttp2_settings_ack_create(void) { - grpc_slice output = GRPC_SLICE_MALLOC(9); - fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK); - return output; -} - -grpc_error *grpc_chttp2_settings_parser_begin_frame( - grpc_chttp2_settings_parser *parser, uint32_t length, uint8_t flags, - uint32_t *settings) { - parser->target_settings = settings; - memcpy(parser->incoming_settings, settings, - GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); - parser->is_ack = 0; - parser->state = GRPC_CHTTP2_SPS_ID0; - if (flags == GRPC_CHTTP2_FLAG_ACK) { - parser->is_ack = 1; - if (length != 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "non-empty settings ack frame received"); - } - return GRPC_ERROR_NONE; - } else if (flags != 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "invalid flags on settings frame"); - } else if (length % 6 != 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "settings frames must be a multiple of six bytes"); - } else { - return GRPC_ERROR_NONE; - } -} - -grpc_error *grpc_chttp2_settings_parser_parse(grpc_exec_ctx *exec_ctx, void *p, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - grpc_chttp2_settings_parser *parser = (grpc_chttp2_settings_parser *)p; - const uint8_t *cur = GRPC_SLICE_START_PTR(slice); - const uint8_t *end = GRPC_SLICE_END_PTR(slice); - char *msg; - grpc_chttp2_setting_id id; - - if (parser->is_ack) { - return GRPC_ERROR_NONE; - } - - for (;;) { - switch (parser->state) { - case GRPC_CHTTP2_SPS_ID0: - if (cur == end) { - parser->state = GRPC_CHTTP2_SPS_ID0; - if (is_last) { - memcpy(parser->target_settings, parser->incoming_settings, - GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); - grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create()); - } - return GRPC_ERROR_NONE; - } - parser->id = (uint16_t)(((uint16_t)*cur) << 8); - cur++; - /* fallthrough */ - case GRPC_CHTTP2_SPS_ID1: - if (cur == end) { - parser->state = GRPC_CHTTP2_SPS_ID1; - return GRPC_ERROR_NONE; - } - parser->id = (uint16_t)(parser->id | (*cur)); - cur++; - /* fallthrough */ - case GRPC_CHTTP2_SPS_VAL0: - if (cur == end) { - parser->state = GRPC_CHTTP2_SPS_VAL0; - return GRPC_ERROR_NONE; - } - parser->value = ((uint32_t)*cur) << 24; - cur++; - /* fallthrough */ - case GRPC_CHTTP2_SPS_VAL1: - if (cur == end) { - parser->state = GRPC_CHTTP2_SPS_VAL1; - return GRPC_ERROR_NONE; - } - parser->value |= ((uint32_t)*cur) << 16; - cur++; - /* fallthrough */ - case GRPC_CHTTP2_SPS_VAL2: - if (cur == end) { - parser->state = GRPC_CHTTP2_SPS_VAL2; - return GRPC_ERROR_NONE; - } - parser->value |= ((uint32_t)*cur) << 8; - cur++; - /* fallthrough */ - case GRPC_CHTTP2_SPS_VAL3: - if (cur == end) { - parser->state = GRPC_CHTTP2_SPS_VAL3; - return GRPC_ERROR_NONE; - } else { - parser->state = GRPC_CHTTP2_SPS_ID0; - } - parser->value |= *cur; - cur++; - - if (grpc_wire_id_to_setting_id(parser->id, &id)) { - const grpc_chttp2_setting_parameters *sp = - &grpc_chttp2_settings_parameters[id]; - if (parser->value < sp->min_value || parser->value > sp->max_value) { - switch (sp->invalid_value_behavior) { - case GRPC_CHTTP2_CLAMP_INVALID_VALUE: - parser->value = - GPR_CLAMP(parser->value, sp->min_value, sp->max_value); - break; - case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE: - grpc_chttp2_goaway_append( - t->last_new_stream_id, sp->error_value, - grpc_slice_from_static_string("HTTP2 settings error"), - &t->qbuf); - gpr_asprintf(&msg, "invalid value %u passed for %s", - parser->value, sp->name); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - } - if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE && - parser->incoming_settings[id] != parser->value) { - t->flow_control.initial_window_update += - (int64_t)parser->value - parser->incoming_settings[id]; - if (GRPC_TRACER_ON(grpc_http_trace) || - GRPC_TRACER_ON(grpc_flowctl_trace)) { - gpr_log(GPR_DEBUG, "%p[%s] adding %d for initial_window change", - t, t->is_client ? "cli" : "svr", - (int)t->flow_control.initial_window_update); - } - } - parser->incoming_settings[id] = parser->value; - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_DEBUG, "CHTTP2:%s:%s: got setting %s = %d", - t->is_client ? "CLI" : "SVR", t->peer_string, sp->name, - parser->value); - } - } else if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)", - parser->id, parser->value); - } - break; - } - } -} diff --git a/src/core/ext/transport/chttp2/transport/frame_settings.cc b/src/core/ext/transport/chttp2/transport/frame_settings.cc new file mode 100644 index 0000000000..2995bf7310 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_settings.cc @@ -0,0 +1,227 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/frame_settings.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include "src/core/ext/transport/chttp2/transport/frame.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/transport/http2_errors.h" + +static uint8_t *fill_header(uint8_t *out, uint32_t length, uint8_t flags) { + *out++ = (uint8_t)(length >> 16); + *out++ = (uint8_t)(length >> 8); + *out++ = (uint8_t)(length); + *out++ = GRPC_CHTTP2_FRAME_SETTINGS; + *out++ = flags; + *out++ = 0; + *out++ = 0; + *out++ = 0; + *out++ = 0; + return out; +} + +grpc_slice grpc_chttp2_settings_create(uint32_t *old_settings, + const uint32_t *new_settings, + uint32_t force_mask, size_t count) { + size_t i; + uint32_t n = 0; + grpc_slice output; + uint8_t *p; + + for (i = 0; i < count; i++) { + n += (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0); + } + + output = GRPC_SLICE_MALLOC(9 + 6 * n); + p = fill_header(GRPC_SLICE_START_PTR(output), 6 * n, 0); + + for (i = 0; i < count; i++) { + if (new_settings[i] != old_settings[i] || (force_mask & (1u << i)) != 0) { + *p++ = (uint8_t)(grpc_setting_id_to_wire_id[i] >> 8); + *p++ = (uint8_t)(grpc_setting_id_to_wire_id[i]); + *p++ = (uint8_t)(new_settings[i] >> 24); + *p++ = (uint8_t)(new_settings[i] >> 16); + *p++ = (uint8_t)(new_settings[i] >> 8); + *p++ = (uint8_t)(new_settings[i]); + old_settings[i] = new_settings[i]; + } + } + + GPR_ASSERT(p == GRPC_SLICE_END_PTR(output)); + + return output; +} + +grpc_slice grpc_chttp2_settings_ack_create(void) { + grpc_slice output = GRPC_SLICE_MALLOC(9); + fill_header(GRPC_SLICE_START_PTR(output), 0, GRPC_CHTTP2_FLAG_ACK); + return output; +} + +grpc_error *grpc_chttp2_settings_parser_begin_frame( + grpc_chttp2_settings_parser *parser, uint32_t length, uint8_t flags, + uint32_t *settings) { + parser->target_settings = settings; + memcpy(parser->incoming_settings, settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); + parser->is_ack = 0; + parser->state = GRPC_CHTTP2_SPS_ID0; + if (flags == GRPC_CHTTP2_FLAG_ACK) { + parser->is_ack = 1; + if (length != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "non-empty settings ack frame received"); + } + return GRPC_ERROR_NONE; + } else if (flags != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "invalid flags on settings frame"); + } else if (length % 6 != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "settings frames must be a multiple of six bytes"); + } else { + return GRPC_ERROR_NONE; + } +} + +grpc_error *grpc_chttp2_settings_parser_parse(grpc_exec_ctx *exec_ctx, void *p, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + grpc_chttp2_settings_parser *parser = (grpc_chttp2_settings_parser *)p; + const uint8_t *cur = GRPC_SLICE_START_PTR(slice); + const uint8_t *end = GRPC_SLICE_END_PTR(slice); + char *msg; + grpc_chttp2_setting_id id; + + if (parser->is_ack) { + return GRPC_ERROR_NONE; + } + + for (;;) { + switch (parser->state) { + case GRPC_CHTTP2_SPS_ID0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID0; + if (is_last) { + memcpy(parser->target_settings, parser->incoming_settings, + GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); + grpc_slice_buffer_add(&t->qbuf, grpc_chttp2_settings_ack_create()); + } + return GRPC_ERROR_NONE; + } + parser->id = (uint16_t)(((uint16_t)*cur) << 8); + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_ID1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_ID1; + return GRPC_ERROR_NONE; + } + parser->id = (uint16_t)(parser->id | (*cur)); + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL0: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL0; + return GRPC_ERROR_NONE; + } + parser->value = ((uint32_t)*cur) << 24; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL1: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL1; + return GRPC_ERROR_NONE; + } + parser->value |= ((uint32_t)*cur) << 16; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL2: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL2; + return GRPC_ERROR_NONE; + } + parser->value |= ((uint32_t)*cur) << 8; + cur++; + /* fallthrough */ + case GRPC_CHTTP2_SPS_VAL3: + if (cur == end) { + parser->state = GRPC_CHTTP2_SPS_VAL3; + return GRPC_ERROR_NONE; + } else { + parser->state = GRPC_CHTTP2_SPS_ID0; + } + parser->value |= *cur; + cur++; + + if (grpc_wire_id_to_setting_id(parser->id, &id)) { + const grpc_chttp2_setting_parameters *sp = + &grpc_chttp2_settings_parameters[id]; + if (parser->value < sp->min_value || parser->value > sp->max_value) { + switch (sp->invalid_value_behavior) { + case GRPC_CHTTP2_CLAMP_INVALID_VALUE: + parser->value = + GPR_CLAMP(parser->value, sp->min_value, sp->max_value); + break; + case GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE: + grpc_chttp2_goaway_append( + t->last_new_stream_id, sp->error_value, + grpc_slice_from_static_string("HTTP2 settings error"), + &t->qbuf); + gpr_asprintf(&msg, "invalid value %u passed for %s", + parser->value, sp->name); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + } + if (id == GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE && + parser->incoming_settings[id] != parser->value) { + t->flow_control.initial_window_update += + (int64_t)parser->value - parser->incoming_settings[id]; + if (GRPC_TRACER_ON(grpc_http_trace) || + GRPC_TRACER_ON(grpc_flowctl_trace)) { + gpr_log(GPR_DEBUG, "%p[%s] adding %d for initial_window change", + t, t->is_client ? "cli" : "svr", + (int)t->flow_control.initial_window_update); + } + } + parser->incoming_settings[id] = parser->value; + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_DEBUG, "CHTTP2:%s:%s: got setting %s = %d", + t->is_client ? "CLI" : "SVR", t->peer_string, sp->name, + parser->value); + } + } else if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_ERROR, "CHTTP2: Ignoring unknown setting %d (value %d)", + parser->id, parser->value); + } + break; + } + } +} diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.c b/src/core/ext/transport/chttp2/transport/frame_window_update.c deleted file mode 100644 index c9ab8d1b50..0000000000 --- a/src/core/ext/transport/chttp2/transport/frame_window_update.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/frame_window_update.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include -#include -#include - -grpc_slice grpc_chttp2_window_update_create( - uint32_t id, uint32_t window_update, grpc_transport_one_way_stats *stats) { - static const size_t frame_size = 13; - grpc_slice slice = GRPC_SLICE_MALLOC(frame_size); - stats->header_bytes += frame_size; - uint8_t *p = GRPC_SLICE_START_PTR(slice); - - GPR_ASSERT(window_update); - - *p++ = 0; - *p++ = 0; - *p++ = 4; - *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE; - *p++ = 0; - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); - *p++ = (uint8_t)(window_update >> 24); - *p++ = (uint8_t)(window_update >> 16); - *p++ = (uint8_t)(window_update >> 8); - *p++ = (uint8_t)(window_update); - - return slice; -} - -grpc_error *grpc_chttp2_window_update_parser_begin_frame( - grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t flags) { - if (flags || length != 4) { - char *msg; - gpr_asprintf(&msg, "invalid window update: length=%d, flags=%02x", length, - flags); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - parser->byte = 0; - parser->amount = 0; - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_chttp2_window_update_parser_parse( - grpc_exec_ctx *exec_ctx, void *parser, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_slice slice, int is_last) { - uint8_t *const beg = GRPC_SLICE_START_PTR(slice); - uint8_t *const end = GRPC_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_chttp2_window_update_parser *p = - (grpc_chttp2_window_update_parser *)parser; - - while (p->byte != 4 && cur != end) { - p->amount |= ((uint32_t)*cur) << (8 * (3 - p->byte)); - cur++; - p->byte++; - } - - if (s != NULL) { - s->stats.incoming.framing_bytes += (uint32_t)(end - cur); - } - - if (p->byte == 4) { - uint32_t received_update = p->amount; - if (received_update == 0 || (received_update & 0x80000000u)) { - char *msg; - gpr_asprintf(&msg, "invalid window update bytes: %d", p->amount); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - GPR_ASSERT(is_last); - - if (t->incoming_stream_id != 0) { - if (s != NULL) { - grpc_chttp2_flowctl_recv_stream_update( - &t->flow_control, &s->flow_control, received_update); - if (grpc_chttp2_list_remove_stalled_by_stream(t, s)) { - grpc_chttp2_mark_stream_writable(exec_ctx, t, s); - grpc_chttp2_initiate_write( - exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE); - } - } - } else { - bool was_zero = t->flow_control.remote_window <= 0; - grpc_chttp2_flowctl_recv_transport_update(&t->flow_control, - received_update); - bool is_zero = t->flow_control.remote_window <= 0; - if (was_zero && !is_zero) { - grpc_chttp2_initiate_write( - exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED); - } - } - } - - return GRPC_ERROR_NONE; -} diff --git a/src/core/ext/transport/chttp2/transport/frame_window_update.cc b/src/core/ext/transport/chttp2/transport/frame_window_update.cc new file mode 100644 index 0000000000..c9ab8d1b50 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/frame_window_update.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/frame_window_update.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include +#include + +grpc_slice grpc_chttp2_window_update_create( + uint32_t id, uint32_t window_update, grpc_transport_one_way_stats *stats) { + static const size_t frame_size = 13; + grpc_slice slice = GRPC_SLICE_MALLOC(frame_size); + stats->header_bytes += frame_size; + uint8_t *p = GRPC_SLICE_START_PTR(slice); + + GPR_ASSERT(window_update); + + *p++ = 0; + *p++ = 0; + *p++ = 4; + *p++ = GRPC_CHTTP2_FRAME_WINDOW_UPDATE; + *p++ = 0; + *p++ = (uint8_t)(id >> 24); + *p++ = (uint8_t)(id >> 16); + *p++ = (uint8_t)(id >> 8); + *p++ = (uint8_t)(id); + *p++ = (uint8_t)(window_update >> 24); + *p++ = (uint8_t)(window_update >> 16); + *p++ = (uint8_t)(window_update >> 8); + *p++ = (uint8_t)(window_update); + + return slice; +} + +grpc_error *grpc_chttp2_window_update_parser_begin_frame( + grpc_chttp2_window_update_parser *parser, uint32_t length, uint8_t flags) { + if (flags || length != 4) { + char *msg; + gpr_asprintf(&msg, "invalid window update: length=%d, flags=%02x", length, + flags); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + parser->byte = 0; + parser->amount = 0; + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_chttp2_window_update_parser_parse( + grpc_exec_ctx *exec_ctx, void *parser, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_slice slice, int is_last) { + uint8_t *const beg = GRPC_SLICE_START_PTR(slice); + uint8_t *const end = GRPC_SLICE_END_PTR(slice); + uint8_t *cur = beg; + grpc_chttp2_window_update_parser *p = + (grpc_chttp2_window_update_parser *)parser; + + while (p->byte != 4 && cur != end) { + p->amount |= ((uint32_t)*cur) << (8 * (3 - p->byte)); + cur++; + p->byte++; + } + + if (s != NULL) { + s->stats.incoming.framing_bytes += (uint32_t)(end - cur); + } + + if (p->byte == 4) { + uint32_t received_update = p->amount; + if (received_update == 0 || (received_update & 0x80000000u)) { + char *msg; + gpr_asprintf(&msg, "invalid window update bytes: %d", p->amount); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + GPR_ASSERT(is_last); + + if (t->incoming_stream_id != 0) { + if (s != NULL) { + grpc_chttp2_flowctl_recv_stream_update( + &t->flow_control, &s->flow_control, received_update); + if (grpc_chttp2_list_remove_stalled_by_stream(t, s)) { + grpc_chttp2_mark_stream_writable(exec_ctx, t, s); + grpc_chttp2_initiate_write( + exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_FLOW_CONTROL_UNSTALLED_BY_UPDATE); + } + } + } else { + bool was_zero = t->flow_control.remote_window <= 0; + grpc_chttp2_flowctl_recv_transport_update(&t->flow_control, + received_update); + bool is_zero = t->flow_control.remote_window <= 0; + if (was_zero && !is_zero) { + grpc_chttp2_initiate_write( + exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_TRANSPORT_FLOW_CONTROL_UNSTALLED); + } + } + } + + return GRPC_ERROR_NONE; +} diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.c b/src/core/ext/transport/chttp2/transport/hpack_encoder.c deleted file mode 100644 index a404b664e3..0000000000 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" - -#include -#include - -/* This is here for grpc_is_binary_header - * TODO(murgatroid99): Remove this - */ -#include - -#include -#include -#include - -#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" -#include "src/core/ext/transport/chttp2/transport/hpack_table.h" -#include "src/core/ext/transport/chttp2/transport/varint.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/transport/metadata.h" -#include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/timeout_encoding.h" - -#define HASH_FRAGMENT_1(x) ((x)&255) -#define HASH_FRAGMENT_2(x) ((x >> 8) & 255) -#define HASH_FRAGMENT_3(x) ((x >> 16) & 255) -#define HASH_FRAGMENT_4(x) ((x >> 24) & 255) - -/* if the probability of this item being seen again is < 1/x then don't add - it to the table */ -#define ONE_ON_ADD_PROBABILITY 128 -/* don't consider adding anything bigger than this to the hpack table */ -#define MAX_DECODER_SPACE_USAGE 512 - -static grpc_slice_refcount terminal_slice_refcount = {NULL, NULL}; -static const grpc_slice terminal_slice = { - &terminal_slice_refcount, /* refcount */ - {{0, 0}} /* data.refcounted */ -}; - -extern grpc_tracer_flag grpc_http_trace; - -typedef struct { - int is_first_frame; - /* number of bytes in 'output' when we started the frame - used to calculate - frame length */ - size_t output_length_at_start_of_frame; - /* index (in output) of the header for the current frame */ - size_t header_idx; - /* have we seen a regular (non-colon-prefixed) header yet? */ - uint8_t seen_regular_header; - /* output stream id */ - uint32_t stream_id; - grpc_slice_buffer *output; - grpc_transport_one_way_stats *stats; - /* maximum size of a frame */ - size_t max_frame_size; - bool use_true_binary_metadata; -} framer_state; - -/* fills p (which is expected to be 9 bytes long) with a data frame header */ -static void fill_header(uint8_t *p, uint8_t type, uint32_t id, size_t len, - uint8_t flags) { - GPR_ASSERT(len < 16777316); - *p++ = (uint8_t)(len >> 16); - *p++ = (uint8_t)(len >> 8); - *p++ = (uint8_t)(len); - *p++ = type; - *p++ = flags; - *p++ = (uint8_t)(id >> 24); - *p++ = (uint8_t)(id >> 16); - *p++ = (uint8_t)(id >> 8); - *p++ = (uint8_t)(id); -} - -/* finish a frame - fill in the previously reserved header */ -static void finish_frame(framer_state *st, int is_header_boundary, - int is_last_in_stream) { - uint8_t type = 0xff; - type = st->is_first_frame ? GRPC_CHTTP2_FRAME_HEADER - : GRPC_CHTTP2_FRAME_CONTINUATION; - fill_header( - GRPC_SLICE_START_PTR(st->output->slices[st->header_idx]), type, - st->stream_id, st->output->length - st->output_length_at_start_of_frame, - (uint8_t)((is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | - (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0))); - st->stats->framing_bytes += 9; - st->is_first_frame = 0; -} - -/* begin a new frame: reserve off header space, remember how many bytes we'd - output before beginning */ -static void begin_frame(framer_state *st) { - st->header_idx = - grpc_slice_buffer_add_indexed(st->output, GRPC_SLICE_MALLOC(9)); - st->output_length_at_start_of_frame = st->output->length; -} - -/* make sure that the current frame is of the type desired, and has sufficient - space to add at least about_to_add bytes -- finishes the current frame if - needed */ -static void ensure_space(framer_state *st, size_t need_bytes) { - if (st->output->length - st->output_length_at_start_of_frame + need_bytes <= - st->max_frame_size) { - return; - } - finish_frame(st, 0, 0); - begin_frame(st); -} - -/* increment a filter count, halve all counts if one element reaches max */ -static void inc_filter(uint8_t idx, uint32_t *sum, uint8_t *elems) { - elems[idx]++; - if (elems[idx] < 255) { - (*sum)++; - } else { - int i; - *sum = 0; - for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_FILTERS; i++) { - elems[i] /= 2; - (*sum) += elems[i]; - } - } -} - -static void add_header_data(framer_state *st, grpc_slice slice) { - size_t len = GRPC_SLICE_LENGTH(slice); - size_t remaining; - if (len == 0) return; - remaining = st->max_frame_size + st->output_length_at_start_of_frame - - st->output->length; - if (len <= remaining) { - st->stats->header_bytes += len; - grpc_slice_buffer_add(st->output, slice); - } else { - st->stats->header_bytes += remaining; - grpc_slice_buffer_add(st->output, grpc_slice_split_head(&slice, remaining)); - finish_frame(st, 0, 0); - begin_frame(st); - add_header_data(st, slice); - } -} - -static uint8_t *add_tiny_header_data(framer_state *st, size_t len) { - ensure_space(st, len); - st->stats->header_bytes += len; - return grpc_slice_buffer_tiny_add(st->output, len); -} - -static void evict_entry(grpc_chttp2_hpack_compressor *c) { - c->tail_remote_index++; - GPR_ASSERT(c->tail_remote_index > 0); - GPR_ASSERT(c->table_size >= - c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); - GPR_ASSERT(c->table_elems > 0); - c->table_size = - (uint16_t)(c->table_size - - c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); - c->table_elems--; -} - -/* add an element to the decoder table */ -static void add_elem(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c, - grpc_mdelem elem) { - GPR_ASSERT(GRPC_MDELEM_IS_INTERNED(elem)); - - uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); - uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem)); - uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash); - uint32_t new_index = c->tail_remote_index + c->table_elems + 1; - size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem); - - GPR_ASSERT(elem_size < 65536); - - if (elem_size > c->max_table_size) { - while (c->table_size > 0) { - evict_entry(c); - } - return; - } - - /* Reserve space for this element in the remote table: if this overflows - the current table, drop elements until it fits, matching the decompressor - algorithm */ - while (c->table_size + elem_size > c->max_table_size) { - evict_entry(c); - } - GPR_ASSERT(c->table_elems < c->max_table_size); - c->table_elem_size[new_index % c->cap_table_elems] = (uint16_t)elem_size; - c->table_size = (uint16_t)(c->table_size + elem_size); - c->table_elems++; - - /* Store this element into {entries,indices}_elem */ - if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem)) { - /* already there: update with new index */ - c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; - } else if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)], - elem)) { - /* already there (cuckoo): update with new index */ - c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; - } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_2(elem_hash)])) { - /* not there, but a free element: add */ - c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; - } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_3(elem_hash)])) { - /* not there (cuckoo), but a free element: add */ - c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; - } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] < - c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) { - /* not there: replace oldest */ - GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[HASH_FRAGMENT_2(elem_hash)]); - c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; - } else { - /* not there: replace oldest */ - GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[HASH_FRAGMENT_3(elem_hash)]); - c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem); - c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; - } - - /* do exactly the same for the key (so we can find by that again too) */ - - if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)], - GRPC_MDKEY(elem))) { - c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; - } else if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)], - GRPC_MDKEY(elem))) { - c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; - } else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)].refcount == - &terminal_slice_refcount) { - c->entries_keys[HASH_FRAGMENT_2(key_hash)] = - grpc_slice_ref_internal(GRPC_MDKEY(elem)); - c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; - } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)].refcount == - &terminal_slice_refcount) { - c->entries_keys[HASH_FRAGMENT_3(key_hash)] = - grpc_slice_ref_internal(GRPC_MDKEY(elem)); - c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; - } else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] < - c->indices_keys[HASH_FRAGMENT_3(key_hash)]) { - grpc_slice_unref_internal(exec_ctx, - c->entries_keys[HASH_FRAGMENT_2(key_hash)]); - c->entries_keys[HASH_FRAGMENT_2(key_hash)] = - grpc_slice_ref_internal(GRPC_MDKEY(elem)); - c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; - } else { - grpc_slice_unref_internal(exec_ctx, - c->entries_keys[HASH_FRAGMENT_3(key_hash)]); - c->entries_keys[HASH_FRAGMENT_3(key_hash)] = - grpc_slice_ref_internal(GRPC_MDKEY(elem)); - c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; - } -} - -static void emit_indexed(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, uint32_t elem_index, - framer_state *st) { - GRPC_STATS_INC_HPACK_SEND_INDEXED(exec_ctx); - uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1); - GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len), - len); -} - -typedef struct { - grpc_slice data; - uint8_t huffman_prefix; - bool insert_null_before_wire_value; -} wire_value; - -static wire_value get_wire_value(grpc_exec_ctx *exec_ctx, grpc_mdelem elem, - bool true_binary_enabled) { - wire_value wire_val; - if (grpc_is_binary_header(GRPC_MDKEY(elem))) { - if (true_binary_enabled) { - GRPC_STATS_INC_HPACK_SEND_BINARY(exec_ctx); - wire_val.huffman_prefix = 0x00; - wire_val.insert_null_before_wire_value = true; - wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem)); - - } else { - GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64(exec_ctx); - wire_val.huffman_prefix = 0x80; - wire_val.insert_null_before_wire_value = false; - wire_val.data = - grpc_chttp2_base64_encode_and_huffman_compress(GRPC_MDVALUE(elem)); - } - } else { - /* TODO(ctiller): opportunistically compress non-binary headers */ - GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx); - wire_val.huffman_prefix = 0x00; - wire_val.insert_null_before_wire_value = false; - wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem)); - } - return wire_val; -} - -static size_t wire_value_length(wire_value v) { - return GPR_SLICE_LENGTH(v.data) + v.insert_null_before_wire_value; -} - -static void add_wire_value(framer_state *st, wire_value v) { - if (v.insert_null_before_wire_value) *add_tiny_header_data(st, 1) = 0; - add_header_data(st, v.data); -} - -static void emit_lithdr_incidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, - uint32_t key_index, grpc_mdelem elem, - framer_state *st) { - GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX(exec_ctx); - uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); - wire_value value = - get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); - size_t len_val = wire_value_length(value); - uint32_t len_val_len; - GPR_ASSERT(len_val <= UINT32_MAX); - len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); - GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, - add_tiny_header_data(st, len_pfx), len_pfx); - GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix, - add_tiny_header_data(st, len_val_len), len_val_len); - add_wire_value(st, value); -} - -static void emit_lithdr_noidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, - uint32_t key_index, grpc_mdelem elem, - framer_state *st) { - GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX(exec_ctx); - uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); - wire_value value = - get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); - size_t len_val = wire_value_length(value); - uint32_t len_val_len; - GPR_ASSERT(len_val <= UINT32_MAX); - len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); - GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, - add_tiny_header_data(st, len_pfx), len_pfx); - GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix, - add_tiny_header_data(st, len_val_len), len_val_len); - add_wire_value(st, value); -} - -static void emit_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, - grpc_mdelem elem, framer_state *st) { - GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V(exec_ctx); - GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx); - uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); - wire_value value = - get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); - uint32_t len_val = (uint32_t)wire_value_length(value); - uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); - uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); - GPR_ASSERT(len_key <= UINT32_MAX); - GPR_ASSERT(wire_value_length(value) <= UINT32_MAX); - *add_tiny_header_data(st, 1) = 0x40; - GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, - add_tiny_header_data(st, len_key_len), len_key_len); - add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem))); - GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, - add_tiny_header_data(st, len_val_len), len_val_len); - add_wire_value(st, value); -} - -static void emit_lithdr_noidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, - grpc_mdelem elem, framer_state *st) { - GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V(exec_ctx); - GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx); - uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); - wire_value value = - get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); - uint32_t len_val = (uint32_t)wire_value_length(value); - uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); - uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); - GPR_ASSERT(len_key <= UINT32_MAX); - GPR_ASSERT(wire_value_length(value) <= UINT32_MAX); - *add_tiny_header_data(st, 1) = 0x00; - GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, - add_tiny_header_data(st, len_key_len), len_key_len); - add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem))); - GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, - add_tiny_header_data(st, len_val_len), len_val_len); - add_wire_value(st, value); -} - -static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c, - framer_state *st) { - uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3); - GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20, - add_tiny_header_data(st, len), len); - c->advertise_table_size_change = 0; -} - -static uint32_t dynidx(grpc_chttp2_hpack_compressor *c, uint32_t elem_index) { - return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + - c->table_elems - elem_index; -} - -/* encode an mdelem */ -static void hpack_enc(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c, - grpc_mdelem elem, framer_state *st) { - GPR_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0); - if (GRPC_SLICE_START_PTR(GRPC_MDKEY(elem))[0] != ':') { /* regular header */ - st->seen_regular_header = 1; - } else { - GPR_ASSERT( - st->seen_regular_header == 0 && - "Reserved header (colon-prefixed) happening after regular ones."); - } - - if (GRPC_TRACER_ON(grpc_http_trace) && !GRPC_MDELEM_IS_INTERNED(elem)) { - char *k = grpc_slice_to_c_string(GRPC_MDKEY(elem)); - char *v = grpc_slice_to_c_string(GRPC_MDVALUE(elem)); - gpr_log( - GPR_DEBUG, - "Encode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d", - k, v, GRPC_MDELEM_IS_INTERNED(elem), GRPC_MDELEM_STORAGE(elem), - grpc_slice_is_interned(GRPC_MDKEY(elem)), - grpc_slice_is_interned(GRPC_MDVALUE(elem))); - gpr_free(k); - gpr_free(v); - } - if (!GRPC_MDELEM_IS_INTERNED(elem)) { - emit_lithdr_noidx_v(exec_ctx, c, elem, st); - return; - } - - uint32_t key_hash; - uint32_t value_hash; - uint32_t elem_hash; - size_t decoder_space_usage; - uint32_t indices_key; - int should_add_elem; - - key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); - value_hash = grpc_slice_hash(GRPC_MDVALUE(elem)); - elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash); - - inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems); - - /* is this elem currently in the decoders table? */ - - if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem) && - c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) { - /* HIT: complete element (first cuckoo hash) */ - emit_indexed(exec_ctx, c, - dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]), st); - return; - } - - if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)], elem) && - c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) { - /* HIT: complete element (second cuckoo hash) */ - emit_indexed(exec_ctx, c, - dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]), st); - return; - } - - /* should this elem be in the table? */ - decoder_space_usage = grpc_mdelem_get_size_in_hpack_table(elem); - should_add_elem = decoder_space_usage < MAX_DECODER_SPACE_USAGE && - c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >= - c->filter_elems_sum / ONE_ON_ADD_PROBABILITY; - - /* no hits for the elem... maybe there's a key? */ - - indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)]; - if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)], - GRPC_MDKEY(elem)) && - indices_key > c->tail_remote_index) { - /* HIT: key (first cuckoo hash) */ - if (should_add_elem) { - emit_lithdr_incidx(exec_ctx, c, dynidx(c, indices_key), elem, st); - add_elem(exec_ctx, c, elem); - return; - } else { - emit_lithdr_noidx(exec_ctx, c, dynidx(c, indices_key), elem, st); - return; - } - GPR_UNREACHABLE_CODE(return ); - } - - indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)]; - if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)], - GRPC_MDKEY(elem)) && - indices_key > c->tail_remote_index) { - /* HIT: key (first cuckoo hash) */ - if (should_add_elem) { - emit_lithdr_incidx(exec_ctx, c, dynidx(c, indices_key), elem, st); - add_elem(exec_ctx, c, elem); - return; - } else { - emit_lithdr_noidx(exec_ctx, c, dynidx(c, indices_key), elem, st); - return; - } - GPR_UNREACHABLE_CODE(return ); - } - - /* no elem, key in the table... fall back to literal emission */ - - if (should_add_elem) { - emit_lithdr_incidx_v(exec_ctx, c, elem, st); - add_elem(exec_ctx, c, elem); - return; - } else { - emit_lithdr_noidx_v(exec_ctx, c, elem, st); - return; - } - GPR_UNREACHABLE_CODE(return ); -} - -#define STRLEN_LIT(x) (sizeof(x) - 1) -#define TIMEOUT_KEY "grpc-timeout" - -static void deadline_enc(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, - framer_state *st) { - char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; - grpc_mdelem mdelem; - grpc_http2_encode_timeout( - gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str); - mdelem = grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_TIMEOUT, - grpc_slice_from_copied_string(timeout_str)); - hpack_enc(exec_ctx, c, mdelem, st); - GRPC_MDELEM_UNREF(exec_ctx, mdelem); -} - -static uint32_t elems_for_bytes(uint32_t bytes) { return (bytes + 31) / 32; } - -void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) { - memset(c, 0, sizeof(*c)); - c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; - c->cap_table_elems = elems_for_bytes(c->max_table_size); - c->max_table_elems = c->cap_table_elems; - c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; - c->table_elem_size = - (uint16_t *)gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems); - memset(c->table_elem_size, 0, - sizeof(*c->table_elem_size) * c->cap_table_elems); - for (size_t i = 0; i < GPR_ARRAY_SIZE(c->entries_keys); i++) { - c->entries_keys[i] = terminal_slice; - } -} - -void grpc_chttp2_hpack_compressor_destroy(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c) { - int i; - for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) { - if (c->entries_keys[i].refcount != &terminal_slice_refcount) { - grpc_slice_unref_internal(exec_ctx, c->entries_keys[i]); - } - GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[i]); - } - gpr_free(c->table_elem_size); -} - -void grpc_chttp2_hpack_compressor_set_max_usable_size( - grpc_chttp2_hpack_compressor *c, uint32_t max_table_size) { - c->max_usable_size = max_table_size; - grpc_chttp2_hpack_compressor_set_max_table_size( - c, GPR_MIN(c->max_table_size, max_table_size)); -} - -static void rebuild_elems(grpc_chttp2_hpack_compressor *c, uint32_t new_cap) { - uint16_t *table_elem_size = - (uint16_t *)gpr_malloc(sizeof(*table_elem_size) * new_cap); - uint32_t i; - - memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap); - GPR_ASSERT(c->table_elems <= new_cap); - - for (i = 0; i < c->table_elems; i++) { - uint32_t ofs = c->tail_remote_index + i + 1; - table_elem_size[ofs % new_cap] = - c->table_elem_size[ofs % c->cap_table_elems]; - } - - c->cap_table_elems = new_cap; - gpr_free(c->table_elem_size); - c->table_elem_size = table_elem_size; -} - -void grpc_chttp2_hpack_compressor_set_max_table_size( - grpc_chttp2_hpack_compressor *c, uint32_t max_table_size) { - max_table_size = GPR_MIN(max_table_size, c->max_usable_size); - if (max_table_size == c->max_table_size) { - return; - } - while (c->table_size > 0 && c->table_size > max_table_size) { - evict_entry(c); - } - c->max_table_size = max_table_size; - c->max_table_elems = elems_for_bytes(max_table_size); - if (c->max_table_elems > c->cap_table_elems) { - rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems)); - } else if (c->max_table_elems < c->cap_table_elems / 3) { - uint32_t new_cap = GPR_MAX(c->max_table_elems, 16); - if (new_cap != c->cap_table_elems) { - rebuild_elems(c, new_cap); - } - } - c->advertise_table_size_change = 1; - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size); - } -} - -void grpc_chttp2_encode_header(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_compressor *c, - grpc_mdelem **extra_headers, - size_t extra_headers_size, - grpc_metadata_batch *metadata, - const grpc_encode_header_options *options, - grpc_slice_buffer *outbuf) { - GPR_ASSERT(options->stream_id != 0); - - framer_state st; - st.seen_regular_header = 0; - st.stream_id = options->stream_id; - st.output = outbuf; - st.is_first_frame = 1; - st.stats = options->stats; - st.max_frame_size = options->max_frame_size; - st.use_true_binary_metadata = options->use_true_binary_metadata; - - /* Encode a metadata batch; store the returned values, representing - a metadata element that needs to be unreffed back into the metadata - slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got - updated). After this loop, we'll do a batch unref of elements. */ - begin_frame(&st); - if (c->advertise_table_size_change != 0) { - emit_advertise_table_size_change(c, &st); - } - for (size_t i = 0; i < extra_headers_size; ++i) { - hpack_enc(exec_ctx, c, *extra_headers[i], &st); - } - grpc_metadata_batch_assert_ok(metadata); - for (grpc_linked_mdelem *l = metadata->list.head; l; l = l->next) { - hpack_enc(exec_ctx, c, l->md, &st); - } - gpr_timespec deadline = metadata->deadline; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) != 0) { - deadline_enc(exec_ctx, c, deadline, &st); - } - - finish_frame(&st, 1, options->is_eof); -} diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc new file mode 100644 index 0000000000..a404b664e3 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -0,0 +1,669 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/hpack_encoder.h" + +#include +#include + +/* This is here for grpc_is_binary_header + * TODO(murgatroid99): Remove this + */ +#include + +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/ext/transport/chttp2/transport/hpack_table.h" +#include "src/core/ext/transport/chttp2/transport/varint.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/timeout_encoding.h" + +#define HASH_FRAGMENT_1(x) ((x)&255) +#define HASH_FRAGMENT_2(x) ((x >> 8) & 255) +#define HASH_FRAGMENT_3(x) ((x >> 16) & 255) +#define HASH_FRAGMENT_4(x) ((x >> 24) & 255) + +/* if the probability of this item being seen again is < 1/x then don't add + it to the table */ +#define ONE_ON_ADD_PROBABILITY 128 +/* don't consider adding anything bigger than this to the hpack table */ +#define MAX_DECODER_SPACE_USAGE 512 + +static grpc_slice_refcount terminal_slice_refcount = {NULL, NULL}; +static const grpc_slice terminal_slice = { + &terminal_slice_refcount, /* refcount */ + {{0, 0}} /* data.refcounted */ +}; + +extern grpc_tracer_flag grpc_http_trace; + +typedef struct { + int is_first_frame; + /* number of bytes in 'output' when we started the frame - used to calculate + frame length */ + size_t output_length_at_start_of_frame; + /* index (in output) of the header for the current frame */ + size_t header_idx; + /* have we seen a regular (non-colon-prefixed) header yet? */ + uint8_t seen_regular_header; + /* output stream id */ + uint32_t stream_id; + grpc_slice_buffer *output; + grpc_transport_one_way_stats *stats; + /* maximum size of a frame */ + size_t max_frame_size; + bool use_true_binary_metadata; +} framer_state; + +/* fills p (which is expected to be 9 bytes long) with a data frame header */ +static void fill_header(uint8_t *p, uint8_t type, uint32_t id, size_t len, + uint8_t flags) { + GPR_ASSERT(len < 16777316); + *p++ = (uint8_t)(len >> 16); + *p++ = (uint8_t)(len >> 8); + *p++ = (uint8_t)(len); + *p++ = type; + *p++ = flags; + *p++ = (uint8_t)(id >> 24); + *p++ = (uint8_t)(id >> 16); + *p++ = (uint8_t)(id >> 8); + *p++ = (uint8_t)(id); +} + +/* finish a frame - fill in the previously reserved header */ +static void finish_frame(framer_state *st, int is_header_boundary, + int is_last_in_stream) { + uint8_t type = 0xff; + type = st->is_first_frame ? GRPC_CHTTP2_FRAME_HEADER + : GRPC_CHTTP2_FRAME_CONTINUATION; + fill_header( + GRPC_SLICE_START_PTR(st->output->slices[st->header_idx]), type, + st->stream_id, st->output->length - st->output_length_at_start_of_frame, + (uint8_t)((is_last_in_stream ? GRPC_CHTTP2_DATA_FLAG_END_STREAM : 0) | + (is_header_boundary ? GRPC_CHTTP2_DATA_FLAG_END_HEADERS : 0))); + st->stats->framing_bytes += 9; + st->is_first_frame = 0; +} + +/* begin a new frame: reserve off header space, remember how many bytes we'd + output before beginning */ +static void begin_frame(framer_state *st) { + st->header_idx = + grpc_slice_buffer_add_indexed(st->output, GRPC_SLICE_MALLOC(9)); + st->output_length_at_start_of_frame = st->output->length; +} + +/* make sure that the current frame is of the type desired, and has sufficient + space to add at least about_to_add bytes -- finishes the current frame if + needed */ +static void ensure_space(framer_state *st, size_t need_bytes) { + if (st->output->length - st->output_length_at_start_of_frame + need_bytes <= + st->max_frame_size) { + return; + } + finish_frame(st, 0, 0); + begin_frame(st); +} + +/* increment a filter count, halve all counts if one element reaches max */ +static void inc_filter(uint8_t idx, uint32_t *sum, uint8_t *elems) { + elems[idx]++; + if (elems[idx] < 255) { + (*sum)++; + } else { + int i; + *sum = 0; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_FILTERS; i++) { + elems[i] /= 2; + (*sum) += elems[i]; + } + } +} + +static void add_header_data(framer_state *st, grpc_slice slice) { + size_t len = GRPC_SLICE_LENGTH(slice); + size_t remaining; + if (len == 0) return; + remaining = st->max_frame_size + st->output_length_at_start_of_frame - + st->output->length; + if (len <= remaining) { + st->stats->header_bytes += len; + grpc_slice_buffer_add(st->output, slice); + } else { + st->stats->header_bytes += remaining; + grpc_slice_buffer_add(st->output, grpc_slice_split_head(&slice, remaining)); + finish_frame(st, 0, 0); + begin_frame(st); + add_header_data(st, slice); + } +} + +static uint8_t *add_tiny_header_data(framer_state *st, size_t len) { + ensure_space(st, len); + st->stats->header_bytes += len; + return grpc_slice_buffer_tiny_add(st->output, len); +} + +static void evict_entry(grpc_chttp2_hpack_compressor *c) { + c->tail_remote_index++; + GPR_ASSERT(c->tail_remote_index > 0); + GPR_ASSERT(c->table_size >= + c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); + GPR_ASSERT(c->table_elems > 0); + c->table_size = + (uint16_t)(c->table_size - + c->table_elem_size[c->tail_remote_index % c->cap_table_elems]); + c->table_elems--; +} + +/* add an element to the decoder table */ +static void add_elem(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c, + grpc_mdelem elem) { + GPR_ASSERT(GRPC_MDELEM_IS_INTERNED(elem)); + + uint32_t key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); + uint32_t value_hash = grpc_slice_hash(GRPC_MDVALUE(elem)); + uint32_t elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash); + uint32_t new_index = c->tail_remote_index + c->table_elems + 1; + size_t elem_size = grpc_mdelem_get_size_in_hpack_table(elem); + + GPR_ASSERT(elem_size < 65536); + + if (elem_size > c->max_table_size) { + while (c->table_size > 0) { + evict_entry(c); + } + return; + } + + /* Reserve space for this element in the remote table: if this overflows + the current table, drop elements until it fits, matching the decompressor + algorithm */ + while (c->table_size + elem_size > c->max_table_size) { + evict_entry(c); + } + GPR_ASSERT(c->table_elems < c->max_table_size); + c->table_elem_size[new_index % c->cap_table_elems] = (uint16_t)elem_size; + c->table_size = (uint16_t)(c->table_size + elem_size); + c->table_elems++; + + /* Store this element into {entries,indices}_elem */ + if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem)) { + /* already there: update with new index */ + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + } else if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)], + elem)) { + /* already there (cuckoo): update with new index */ + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_2(elem_hash)])) { + /* not there, but a free element: add */ + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + } else if (GRPC_MDISNULL(c->entries_elems[HASH_FRAGMENT_3(elem_hash)])) { + /* not there (cuckoo), but a free element: add */ + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + } else if (c->indices_elems[HASH_FRAGMENT_2(elem_hash)] < + c->indices_elems[HASH_FRAGMENT_3(elem_hash)]) { + /* not there: replace oldest */ + GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[HASH_FRAGMENT_2(elem_hash)]); + c->entries_elems[HASH_FRAGMENT_2(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] = new_index; + } else { + /* not there: replace oldest */ + GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[HASH_FRAGMENT_3(elem_hash)]); + c->entries_elems[HASH_FRAGMENT_3(elem_hash)] = GRPC_MDELEM_REF(elem); + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] = new_index; + } + + /* do exactly the same for the key (so we can find by that again too) */ + + if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)], + GRPC_MDKEY(elem))) { + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)], + GRPC_MDKEY(elem))) { + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_2(key_hash)].refcount == + &terminal_slice_refcount) { + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else if (c->entries_keys[HASH_FRAGMENT_3(key_hash)].refcount == + &terminal_slice_refcount) { + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } else if (c->indices_keys[HASH_FRAGMENT_2(key_hash)] < + c->indices_keys[HASH_FRAGMENT_3(key_hash)]) { + grpc_slice_unref_internal(exec_ctx, + c->entries_keys[HASH_FRAGMENT_2(key_hash)]); + c->entries_keys[HASH_FRAGMENT_2(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_2(key_hash)] = new_index; + } else { + grpc_slice_unref_internal(exec_ctx, + c->entries_keys[HASH_FRAGMENT_3(key_hash)]); + c->entries_keys[HASH_FRAGMENT_3(key_hash)] = + grpc_slice_ref_internal(GRPC_MDKEY(elem)); + c->indices_keys[HASH_FRAGMENT_3(key_hash)] = new_index; + } +} + +static void emit_indexed(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, uint32_t elem_index, + framer_state *st) { + GRPC_STATS_INC_HPACK_SEND_INDEXED(exec_ctx); + uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(elem_index, 1); + GRPC_CHTTP2_WRITE_VARINT(elem_index, 1, 0x80, add_tiny_header_data(st, len), + len); +} + +typedef struct { + grpc_slice data; + uint8_t huffman_prefix; + bool insert_null_before_wire_value; +} wire_value; + +static wire_value get_wire_value(grpc_exec_ctx *exec_ctx, grpc_mdelem elem, + bool true_binary_enabled) { + wire_value wire_val; + if (grpc_is_binary_header(GRPC_MDKEY(elem))) { + if (true_binary_enabled) { + GRPC_STATS_INC_HPACK_SEND_BINARY(exec_ctx); + wire_val.huffman_prefix = 0x00; + wire_val.insert_null_before_wire_value = true; + wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem)); + + } else { + GRPC_STATS_INC_HPACK_SEND_BINARY_BASE64(exec_ctx); + wire_val.huffman_prefix = 0x80; + wire_val.insert_null_before_wire_value = false; + wire_val.data = + grpc_chttp2_base64_encode_and_huffman_compress(GRPC_MDVALUE(elem)); + } + } else { + /* TODO(ctiller): opportunistically compress non-binary headers */ + GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx); + wire_val.huffman_prefix = 0x00; + wire_val.insert_null_before_wire_value = false; + wire_val.data = grpc_slice_ref_internal(GRPC_MDVALUE(elem)); + } + return wire_val; +} + +static size_t wire_value_length(wire_value v) { + return GPR_SLICE_LENGTH(v.data) + v.insert_null_before_wire_value; +} + +static void add_wire_value(framer_state *st, wire_value v) { + if (v.insert_null_before_wire_value) *add_tiny_header_data(st, 1) = 0; + add_header_data(st, v.data); +} + +static void emit_lithdr_incidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, + uint32_t key_index, grpc_mdelem elem, + framer_state *st) { + GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX(exec_ctx); + uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 2); + wire_value value = + get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); + size_t len_val = wire_value_length(value); + uint32_t len_val_len; + GPR_ASSERT(len_val <= UINT32_MAX); + len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 2, 0x40, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_lithdr_noidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, + uint32_t key_index, grpc_mdelem elem, + framer_state *st) { + GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX(exec_ctx); + uint32_t len_pfx = GRPC_CHTTP2_VARINT_LENGTH(key_index, 4); + wire_value value = + get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); + size_t len_val = wire_value_length(value); + uint32_t len_val_len; + GPR_ASSERT(len_val <= UINT32_MAX); + len_val_len = GRPC_CHTTP2_VARINT_LENGTH((uint32_t)len_val, 1); + GRPC_CHTTP2_WRITE_VARINT(key_index, 4, 0x00, + add_tiny_header_data(st, len_pfx), len_pfx); + GRPC_CHTTP2_WRITE_VARINT((uint32_t)len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, + grpc_mdelem elem, framer_state *st) { + GRPC_STATS_INC_HPACK_SEND_LITHDR_INCIDX_V(exec_ctx); + GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx); + uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + wire_value value = + get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); + uint32_t len_val = (uint32_t)wire_value_length(value); + uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GPR_ASSERT(len_key <= UINT32_MAX); + GPR_ASSERT(wire_value_length(value) <= UINT32_MAX); + *add_tiny_header_data(st, 1) = 0x40; + GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, + add_tiny_header_data(st, len_key_len), len_key_len); + add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem))); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_lithdr_noidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, + grpc_mdelem elem, framer_state *st) { + GRPC_STATS_INC_HPACK_SEND_LITHDR_NOTIDX_V(exec_ctx); + GRPC_STATS_INC_HPACK_SEND_UNCOMPRESSED(exec_ctx); + uint32_t len_key = (uint32_t)GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + wire_value value = + get_wire_value(exec_ctx, elem, st->use_true_binary_metadata); + uint32_t len_val = (uint32_t)wire_value_length(value); + uint32_t len_key_len = GRPC_CHTTP2_VARINT_LENGTH(len_key, 1); + uint32_t len_val_len = GRPC_CHTTP2_VARINT_LENGTH(len_val, 1); + GPR_ASSERT(len_key <= UINT32_MAX); + GPR_ASSERT(wire_value_length(value) <= UINT32_MAX); + *add_tiny_header_data(st, 1) = 0x00; + GRPC_CHTTP2_WRITE_VARINT(len_key, 1, 0x00, + add_tiny_header_data(st, len_key_len), len_key_len); + add_header_data(st, grpc_slice_ref_internal(GRPC_MDKEY(elem))); + GRPC_CHTTP2_WRITE_VARINT(len_val, 1, value.huffman_prefix, + add_tiny_header_data(st, len_val_len), len_val_len); + add_wire_value(st, value); +} + +static void emit_advertise_table_size_change(grpc_chttp2_hpack_compressor *c, + framer_state *st) { + uint32_t len = GRPC_CHTTP2_VARINT_LENGTH(c->max_table_size, 3); + GRPC_CHTTP2_WRITE_VARINT(c->max_table_size, 3, 0x20, + add_tiny_header_data(st, len), len); + c->advertise_table_size_change = 0; +} + +static uint32_t dynidx(grpc_chttp2_hpack_compressor *c, uint32_t elem_index) { + return 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY + c->tail_remote_index + + c->table_elems - elem_index; +} + +/* encode an mdelem */ +static void hpack_enc(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_compressor *c, + grpc_mdelem elem, framer_state *st) { + GPR_ASSERT(GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)) > 0); + if (GRPC_SLICE_START_PTR(GRPC_MDKEY(elem))[0] != ':') { /* regular header */ + st->seen_regular_header = 1; + } else { + GPR_ASSERT( + st->seen_regular_header == 0 && + "Reserved header (colon-prefixed) happening after regular ones."); + } + + if (GRPC_TRACER_ON(grpc_http_trace) && !GRPC_MDELEM_IS_INTERNED(elem)) { + char *k = grpc_slice_to_c_string(GRPC_MDKEY(elem)); + char *v = grpc_slice_to_c_string(GRPC_MDVALUE(elem)); + gpr_log( + GPR_DEBUG, + "Encode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d", + k, v, GRPC_MDELEM_IS_INTERNED(elem), GRPC_MDELEM_STORAGE(elem), + grpc_slice_is_interned(GRPC_MDKEY(elem)), + grpc_slice_is_interned(GRPC_MDVALUE(elem))); + gpr_free(k); + gpr_free(v); + } + if (!GRPC_MDELEM_IS_INTERNED(elem)) { + emit_lithdr_noidx_v(exec_ctx, c, elem, st); + return; + } + + uint32_t key_hash; + uint32_t value_hash; + uint32_t elem_hash; + size_t decoder_space_usage; + uint32_t indices_key; + int should_add_elem; + + key_hash = grpc_slice_hash(GRPC_MDKEY(elem)); + value_hash = grpc_slice_hash(GRPC_MDVALUE(elem)); + elem_hash = GRPC_MDSTR_KV_HASH(key_hash, value_hash); + + inc_filter(HASH_FRAGMENT_1(elem_hash), &c->filter_elems_sum, c->filter_elems); + + /* is this elem currently in the decoders table? */ + + if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_2(elem_hash)], elem) && + c->indices_elems[HASH_FRAGMENT_2(elem_hash)] > c->tail_remote_index) { + /* HIT: complete element (first cuckoo hash) */ + emit_indexed(exec_ctx, c, + dynidx(c, c->indices_elems[HASH_FRAGMENT_2(elem_hash)]), st); + return; + } + + if (grpc_mdelem_eq(c->entries_elems[HASH_FRAGMENT_3(elem_hash)], elem) && + c->indices_elems[HASH_FRAGMENT_3(elem_hash)] > c->tail_remote_index) { + /* HIT: complete element (second cuckoo hash) */ + emit_indexed(exec_ctx, c, + dynidx(c, c->indices_elems[HASH_FRAGMENT_3(elem_hash)]), st); + return; + } + + /* should this elem be in the table? */ + decoder_space_usage = grpc_mdelem_get_size_in_hpack_table(elem); + should_add_elem = decoder_space_usage < MAX_DECODER_SPACE_USAGE && + c->filter_elems[HASH_FRAGMENT_1(elem_hash)] >= + c->filter_elems_sum / ONE_ON_ADD_PROBABILITY; + + /* no hits for the elem... maybe there's a key? */ + + indices_key = c->indices_keys[HASH_FRAGMENT_2(key_hash)]; + if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_2(key_hash)], + GRPC_MDKEY(elem)) && + indices_key > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + if (should_add_elem) { + emit_lithdr_incidx(exec_ctx, c, dynidx(c, indices_key), elem, st); + add_elem(exec_ctx, c, elem); + return; + } else { + emit_lithdr_noidx(exec_ctx, c, dynidx(c, indices_key), elem, st); + return; + } + GPR_UNREACHABLE_CODE(return ); + } + + indices_key = c->indices_keys[HASH_FRAGMENT_3(key_hash)]; + if (grpc_slice_eq(c->entries_keys[HASH_FRAGMENT_3(key_hash)], + GRPC_MDKEY(elem)) && + indices_key > c->tail_remote_index) { + /* HIT: key (first cuckoo hash) */ + if (should_add_elem) { + emit_lithdr_incidx(exec_ctx, c, dynidx(c, indices_key), elem, st); + add_elem(exec_ctx, c, elem); + return; + } else { + emit_lithdr_noidx(exec_ctx, c, dynidx(c, indices_key), elem, st); + return; + } + GPR_UNREACHABLE_CODE(return ); + } + + /* no elem, key in the table... fall back to literal emission */ + + if (should_add_elem) { + emit_lithdr_incidx_v(exec_ctx, c, elem, st); + add_elem(exec_ctx, c, elem); + return; + } else { + emit_lithdr_noidx_v(exec_ctx, c, elem, st); + return; + } + GPR_UNREACHABLE_CODE(return ); +} + +#define STRLEN_LIT(x) (sizeof(x) - 1) +#define TIMEOUT_KEY "grpc-timeout" + +static void deadline_enc(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, gpr_timespec deadline, + framer_state *st) { + char timeout_str[GRPC_HTTP2_TIMEOUT_ENCODE_MIN_BUFSIZE]; + grpc_mdelem mdelem; + grpc_http2_encode_timeout( + gpr_time_sub(deadline, gpr_now(deadline.clock_type)), timeout_str); + mdelem = grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_TIMEOUT, + grpc_slice_from_copied_string(timeout_str)); + hpack_enc(exec_ctx, c, mdelem, st); + GRPC_MDELEM_UNREF(exec_ctx, mdelem); +} + +static uint32_t elems_for_bytes(uint32_t bytes) { return (bytes + 31) / 32; } + +void grpc_chttp2_hpack_compressor_init(grpc_chttp2_hpack_compressor *c) { + memset(c, 0, sizeof(*c)); + c->max_table_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; + c->cap_table_elems = elems_for_bytes(c->max_table_size); + c->max_table_elems = c->cap_table_elems; + c->max_usable_size = GRPC_CHTTP2_HPACKC_INITIAL_TABLE_SIZE; + c->table_elem_size = + (uint16_t *)gpr_malloc(sizeof(*c->table_elem_size) * c->cap_table_elems); + memset(c->table_elem_size, 0, + sizeof(*c->table_elem_size) * c->cap_table_elems); + for (size_t i = 0; i < GPR_ARRAY_SIZE(c->entries_keys); i++) { + c->entries_keys[i] = terminal_slice; + } +} + +void grpc_chttp2_hpack_compressor_destroy(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c) { + int i; + for (i = 0; i < GRPC_CHTTP2_HPACKC_NUM_VALUES; i++) { + if (c->entries_keys[i].refcount != &terminal_slice_refcount) { + grpc_slice_unref_internal(exec_ctx, c->entries_keys[i]); + } + GRPC_MDELEM_UNREF(exec_ctx, c->entries_elems[i]); + } + gpr_free(c->table_elem_size); +} + +void grpc_chttp2_hpack_compressor_set_max_usable_size( + grpc_chttp2_hpack_compressor *c, uint32_t max_table_size) { + c->max_usable_size = max_table_size; + grpc_chttp2_hpack_compressor_set_max_table_size( + c, GPR_MIN(c->max_table_size, max_table_size)); +} + +static void rebuild_elems(grpc_chttp2_hpack_compressor *c, uint32_t new_cap) { + uint16_t *table_elem_size = + (uint16_t *)gpr_malloc(sizeof(*table_elem_size) * new_cap); + uint32_t i; + + memset(table_elem_size, 0, sizeof(*table_elem_size) * new_cap); + GPR_ASSERT(c->table_elems <= new_cap); + + for (i = 0; i < c->table_elems; i++) { + uint32_t ofs = c->tail_remote_index + i + 1; + table_elem_size[ofs % new_cap] = + c->table_elem_size[ofs % c->cap_table_elems]; + } + + c->cap_table_elems = new_cap; + gpr_free(c->table_elem_size); + c->table_elem_size = table_elem_size; +} + +void grpc_chttp2_hpack_compressor_set_max_table_size( + grpc_chttp2_hpack_compressor *c, uint32_t max_table_size) { + max_table_size = GPR_MIN(max_table_size, c->max_usable_size); + if (max_table_size == c->max_table_size) { + return; + } + while (c->table_size > 0 && c->table_size > max_table_size) { + evict_entry(c); + } + c->max_table_size = max_table_size; + c->max_table_elems = elems_for_bytes(max_table_size); + if (c->max_table_elems > c->cap_table_elems) { + rebuild_elems(c, GPR_MAX(c->max_table_elems, 2 * c->cap_table_elems)); + } else if (c->max_table_elems < c->cap_table_elems / 3) { + uint32_t new_cap = GPR_MAX(c->max_table_elems, 16); + if (new_cap != c->cap_table_elems) { + rebuild_elems(c, new_cap); + } + } + c->advertise_table_size_change = 1; + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_DEBUG, "set max table size from encoder to %d", max_table_size); + } +} + +void grpc_chttp2_encode_header(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_compressor *c, + grpc_mdelem **extra_headers, + size_t extra_headers_size, + grpc_metadata_batch *metadata, + const grpc_encode_header_options *options, + grpc_slice_buffer *outbuf) { + GPR_ASSERT(options->stream_id != 0); + + framer_state st; + st.seen_regular_header = 0; + st.stream_id = options->stream_id; + st.output = outbuf; + st.is_first_frame = 1; + st.stats = options->stats; + st.max_frame_size = options->max_frame_size; + st.use_true_binary_metadata = options->use_true_binary_metadata; + + /* Encode a metadata batch; store the returned values, representing + a metadata element that needs to be unreffed back into the metadata + slot. THIS MAY NOT BE THE SAME ELEMENT (if a decoder table slot got + updated). After this loop, we'll do a batch unref of elements. */ + begin_frame(&st); + if (c->advertise_table_size_change != 0) { + emit_advertise_table_size_change(c, &st); + } + for (size_t i = 0; i < extra_headers_size; ++i) { + hpack_enc(exec_ctx, c, *extra_headers[i], &st); + } + grpc_metadata_batch_assert_ok(metadata); + for (grpc_linked_mdelem *l = metadata->list.head; l; l = l->next) { + hpack_enc(exec_ctx, c, l->md, &st); + } + gpr_timespec deadline = metadata->deadline; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) != 0) { + deadline_enc(exec_ctx, c, deadline, &st); + } + + finish_frame(&st, 1, options->is_eof); +} diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.c b/src/core/ext/transport/chttp2/transport/hpack_parser.c deleted file mode 100644 index 3d1df19bc3..0000000000 --- a/src/core/ext/transport/chttp2/transport/hpack_parser.c +++ /dev/null @@ -1,1761 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/hpack_parser.h" -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/http2_errors.h" - -typedef enum { - NOT_BINARY, - BINARY_BEGIN, - B64_BYTE0, - B64_BYTE1, - B64_BYTE2, - B64_BYTE3 -} binary_state; - -/* How parsing works: - - The parser object keeps track of a function pointer which represents the - current parse state. - - Each time new bytes are presented, we call into the current state, which - recursively parses until all bytes in the given chunk are exhausted. - - The parse state that terminates then saves its function pointer to be the - current state so that it can resume when more bytes are available. - - It's expected that most optimizing compilers will turn this code into - a set of indirect jumps, and so not waste stack space. */ - -/* forward declarations for parsing states */ -static grpc_error *parse_begin(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_error(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end, grpc_error *error); -static grpc_error *still_parse_error(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_illegal_op(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); - -static grpc_error *parse_string_prefix(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_key_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_value_string_with_indexed_key( - grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_value_string_with_literal_key( - grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); - -static grpc_error *parse_value0(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_value1(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_value2(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_value3(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_value4(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_value5up(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); - -static grpc_error *parse_indexed_field(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_indexed_field_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_lithdr_incidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_lithdr_incidx_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_lithdr_notidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_lithdr_notidx_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_lithdr_notidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_lithdr_nvridx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_lithdr_nvridx_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end); -static grpc_error *parse_max_tbl_size(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); -static grpc_error *parse_max_tbl_size_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end); - -/* we translate the first byte of a hpack field into one of these decoding - cases, then use a lookup table to jump directly to the appropriate parser. - - _X => the integer index is all ones, meaning we need to do varint decoding - _V => the integer index is all zeros, meaning we need to decode an additional - string value */ -typedef enum { - INDEXED_FIELD, - INDEXED_FIELD_X, - LITHDR_INCIDX, - LITHDR_INCIDX_X, - LITHDR_INCIDX_V, - LITHDR_NOTIDX, - LITHDR_NOTIDX_X, - LITHDR_NOTIDX_V, - LITHDR_NVRIDX, - LITHDR_NVRIDX_X, - LITHDR_NVRIDX_V, - MAX_TBL_SIZE, - MAX_TBL_SIZE_X, - ILLEGAL -} first_byte_type; - -/* jump table of parse state functions -- order must match first_byte_type - above */ -static const grpc_chttp2_hpack_parser_state first_byte_action[] = { - parse_indexed_field, parse_indexed_field_x, parse_lithdr_incidx, - parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx, - parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx, - parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size, - parse_max_tbl_size_x, parse_illegal_op}; - -/* indexes the first byte to a parse state function - generated by - gen_hpack_tables.c */ -static const uint8_t first_byte_lut[256] = { - LITHDR_NOTIDX_V, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, - LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, - LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, - LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX_X, - LITHDR_NVRIDX_V, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, - LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, - LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, - LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, - MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE_X, - LITHDR_INCIDX_V, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, - LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX_X, - ILLEGAL, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, - INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD_X, -}; - -/* state table for huffman decoding: given a state, gives an index/16 into - next_sub_tbl. Taking that index and adding the value of the nibble being - considered returns the next state. - - generated by gen_hpack_tables.c */ -static const uint8_t next_tbl[256] = { - 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, 1, 1, - 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 14, 1, 15, 16, 1, 17, 1, 15, 2, 7, 3, 18, 19, 1, 1, 1, 1, 20, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 7, 21, 1, 22, 1, 1, 1, 1, 1, - 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, 23, 24, 25, 1, 1, 1, 1, 2, 2, 2, - 26, 3, 3, 27, 10, 28, 1, 1, 1, 1, 1, 1, 2, 3, 29, 10, 30, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 2, 2, 2, 2, 2, 2, 32, 1, 1, 15, 33, 1, 34, 35, 9, 36, 1, 1, 1, - 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 26, 9, - 38, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 26, 3, 3, 39, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 7, 3, 3, 3, 40, 2, - 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, 3, 18, 47, 2, -}; - -/* next state, based upon current state and the current nibble: see above. - generated by gen_hpack_tables.c */ -static const int16_t next_sub_tbl[48 * 16] = { - 1, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, - 218, 2, 6, 10, 13, 14, 15, 16, 17, 2, 6, 10, 13, 14, 15, - 16, 17, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, 24, 3, - 7, 11, 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, - 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, - 199, 200, 201, 202, 203, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 9, 133, 134, 135, 136, 137, 138, 139, 140, - 141, 142, 143, 144, 145, 146, 147, 3, 7, 11, 24, 3, 7, 11, 24, - 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 12, 132, 4, 8, 4, 8, 4, 8, - 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 21, 4, 8, 4, - 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 22, 23, 91, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 3, - 7, 11, 24, 3, 7, 11, 24, 0, 0, 0, 0, 0, 41, 42, 43, - 2, 6, 10, 13, 14, 15, 16, 17, 3, 7, 11, 24, 3, 7, 11, - 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, - 44, 45, 2, 6, 10, 13, 14, 15, 16, 17, 46, 47, 48, 49, 50, - 51, 52, 57, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 73, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 3, 7, 11, 24, 3, 7, 11, - 24, 3, 7, 11, 24, 0, 0, 0, 0, 3, 7, 11, 24, 3, 7, - 11, 24, 4, 8, 4, 8, 0, 0, 0, 92, 0, 0, 0, 93, 94, - 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 7, 11, 24, - 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, - 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4, - 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, - 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, - 131, 2, 6, 10, 13, 14, 15, 16, 17, 4, 8, 4, 8, 4, 8, - 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 148, - 149, 150, 151, 3, 7, 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, - 0, 0, 152, 153, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, - 24, 154, 155, 156, 164, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, - 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, - 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, - 188, 189, 190, 191, 192, 193, 194, 195, 196, 4, 8, 4, 8, 4, 8, - 4, 8, 4, 8, 4, 8, 4, 8, 197, 198, 4, 8, 4, 8, 4, - 8, 4, 8, 0, 0, 0, 0, 0, 0, 219, 220, 3, 7, 11, 24, - 4, 8, 4, 8, 4, 8, 0, 0, 221, 222, 223, 224, 3, 7, 11, - 24, 3, 7, 11, 24, 4, 8, 4, 8, 4, 8, 225, 228, 4, 8, - 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 229, - 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, - 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 245, 246, 247, 248, 249, 250, 251, 252, - 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 255, -}; - -/* emission table: indexed like next_tbl, ultimately gives the byte to be - emitted, or -1 for no byte, or 256 for end of stream - - generated by gen_hpack_tables.c */ -static const uint16_t emit_tbl[256] = { - 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, - 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 0, 55, 56, - 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 0, - 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, - 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, - 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, - 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, - 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, - 0, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, - 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, - 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, - 219, 220, 221, 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, - 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, - 248, -}; - -/* generated by gen_hpack_tables.c */ -static const int16_t emit_sub_tbl[249 * 16] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, - 49, 49, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 97, - 97, 97, 97, 48, 48, 49, 49, 50, 50, 97, 97, 99, 99, 101, 101, - 105, 105, 111, 111, 48, 49, 50, 97, 99, 101, 105, 111, 115, 116, -1, - -1, -1, -1, -1, -1, 32, 32, 32, 32, 32, 32, 32, 32, 37, 37, - 37, 37, 37, 37, 37, 37, 99, 99, 99, 99, 101, 101, 101, 101, 105, - 105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32, 37, 45, 46, - 47, 51, 52, 53, 54, 55, 56, 57, 61, 61, 61, 61, 61, 61, 61, - 61, 65, 65, 65, 65, 65, 65, 65, 65, 115, 115, 115, 115, 116, 116, - 116, 116, 32, 32, 37, 37, 45, 45, 46, 46, 61, 65, 95, 98, 100, - 102, 103, 104, 108, 109, 110, 112, 114, 117, -1, -1, 58, 58, 58, 58, - 58, 58, 58, 58, 66, 66, 66, 66, 66, 66, 66, 66, 47, 47, 51, - 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 61, 61, - 65, 65, 95, 95, 98, 98, 100, 100, 102, 102, 103, 103, 104, 104, 108, - 108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58, 66, 67, 68, - 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, - 84, 85, 86, 87, 89, 106, 107, 113, 118, 119, 120, 121, 122, -1, -1, - -1, -1, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42, - 42, 42, 42, 44, 44, 44, 44, 44, 44, 44, 44, 59, 59, 59, 59, - 59, 59, 59, 59, 88, 88, 88, 88, 88, 88, 88, 88, 90, 90, 90, - 90, 90, 90, 90, 90, 33, 33, 34, 34, 40, 40, 41, 41, 63, 63, - 39, 43, 124, -1, -1, -1, 35, 35, 35, 35, 35, 35, 35, 35, 62, - 62, 62, 62, 62, 62, 62, 62, 0, 0, 0, 0, 36, 36, 36, 36, - 64, 64, 64, 64, 91, 91, 91, 91, 69, 69, 69, 69, 69, 69, 69, - 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, - 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, - 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, - 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, - 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, - 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81, - 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, - 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, - 84, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, - 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 89, 89, 89, 89, 89, - 89, 89, 89, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, - 107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118, - 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, - 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122, - 122, 122, 122, 122, 122, 122, 122, 38, 38, 38, 38, 42, 42, 42, 42, - 44, 44, 44, 44, 59, 59, 59, 59, 88, 88, 88, 88, 90, 90, 90, - 90, 33, 34, 40, 41, 63, -1, -1, -1, 39, 39, 39, 39, 39, 39, - 39, 39, 43, 43, 43, 43, 43, 43, 43, 43, 124, 124, 124, 124, 124, - 124, 124, 124, 35, 35, 35, 35, 62, 62, 62, 62, 0, 0, 36, 36, - 64, 64, 91, 91, 93, 93, 126, 126, 94, 125, -1, -1, 60, 60, 60, - 60, 60, 60, 60, 60, 96, 96, 96, 96, 96, 96, 96, 96, 123, 123, - 123, 123, 123, 123, 123, 123, -1, -1, -1, -1, -1, -1, -1, -1, 92, - 92, 92, 92, 92, 92, 92, 92, 195, 195, 195, 195, 195, 195, 195, 195, - 208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130, - 130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194, - 194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167, - 167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217, - 227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160, - 163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228, - 232, 233, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 135, - 135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137, - 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, - 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, - 141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147, - 147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, - 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, - 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157, - 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165, - 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, - 168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174, - 174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180, - 180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, - 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191, - 191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231, - 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9, 9, - 9, 9, 142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148, - 148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206, - 215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237, - 237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205, - 210, 213, 218, 219, 238, 240, 242, 243, 255, -1, 203, 203, 203, 203, 203, - 203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211, - 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214, - 214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, - 222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241, - 241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244, - 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, - 246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248, - 248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, - 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, - 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2, 2, 2, - 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, - 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 12, - 12, 12, 12, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, - 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, - 20, 21, 21, 21, 21, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, - 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, - 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 127, 127, 127, 127, - 220, 220, 220, 220, 249, 249, 249, 249, 10, 13, 22, 256, 93, 93, 93, - 93, 126, 126, 126, 126, 94, 94, 125, 125, 60, 96, 123, -1, 92, 195, - 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 128, - 128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130, - 131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162, - 162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194, - 194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226, - 226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167, - 172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179, - 179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227, - 227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133, - 133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163, - 164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186, - 186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232, - 233, 233, 1, 135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152, - 155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231, - 239, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, 9, - 9, 9, 9, 9, 9, 142, 142, 142, 142, 142, 142, 142, 142, 144, 144, - 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148, - 148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159, - 171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206, - 206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225, - 225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, - 237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234, - 235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205, - 205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242, - 243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245, - 246, 247, 248, 250, 251, 252, 253, 254, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, - 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, - 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, - 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, - 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, - 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, - 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, - 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, - 20, 21, 21, 21, 21, 21, 21, 21, 21, 23, 23, 23, 23, 23, 23, - 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, - 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, - 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, - 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, - 31, 31, 31, 31, 31, 31, 127, 127, 127, 127, 127, 127, 127, 127, 220, - 220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249, - 10, 10, 13, 13, 22, 22, 256, 256, 67, 67, 67, 67, 67, 67, 67, - 67, 68, 68, 68, 68, 68, 68, 68, 68, 95, 95, 95, 95, 95, 95, - 95, 95, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100, 100, 100, - 100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, - 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108, - 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, - 110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114, - 114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117, - 58, 58, 58, 58, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, - 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, - 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, - 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, - 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, - 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, - 87, 87, 89, 89, 89, 89, 106, 106, 106, 106, 107, 107, 107, 107, 113, - 113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120, - 121, 121, 121, 121, 122, 122, 122, 122, 38, 38, 42, 42, 44, 44, 59, - 59, 88, 88, 90, 90, -1, -1, -1, -1, 33, 33, 33, 33, 33, 33, - 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, - 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 63, 63, 63, 63, - 63, 63, 63, 63, 39, 39, 39, 39, 43, 43, 43, 43, 124, 124, 124, - 124, 35, 35, 62, 62, 0, 36, 64, 91, 93, 126, -1, -1, 94, 94, - 94, 94, 94, 94, 94, 94, 125, 125, 125, 125, 125, 125, 125, 125, 60, - 60, 60, 60, 96, 96, 96, 96, 123, 123, 123, 123, -1, -1, -1, -1, - 92, 92, 92, 92, 195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130, - 130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161, - 167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1, -1, -1, -1, - -1, -1, -1, 129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132, - 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, - 134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146, - 146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156, - 156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160, - 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, - 164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, - 170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178, - 178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185, - 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, - 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, - 190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198, - 198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228, - 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233, - 233, 1, 1, 1, 1, 135, 135, 135, 135, 137, 137, 137, 137, 138, 138, - 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143, - 143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150, - 151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157, - 157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168, - 168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182, - 182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191, - 197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9, 9, 142, - 142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215, - 225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192, - 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200, - 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202, - 202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210, - 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218, - 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, - 238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240, - 240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, - 243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204, - 204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214, - 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241, - 241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, - 247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252, - 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2, 2, 3, 3, - 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 11, 11, 12, 12, 14, - 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, - 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, - 30, 31, 31, 127, 127, 220, 220, 249, 249, -1, -1, 10, 10, 10, 10, - 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 22, 22, 22, - 22, 22, 22, 22, 22, 256, 256, 256, 256, 256, 256, 256, 256, 45, 45, - 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, - 47, 47, 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, - 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, - 53, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, - 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, - 57, 57, 57, 50, 50, 50, 50, 50, 50, 50, 50, 97, 97, 97, 97, - 97, 97, 97, 97, 99, 99, 99, 99, 99, 99, 99, 99, 101, 101, 101, - 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111, - 111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116, - 116, 116, 116, 116, 116, 116, 116, 32, 32, 32, 32, 37, 37, 37, 37, - 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 51, 51, 51, - 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, - 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 61, 61, 61, 61, 65, - 65, 65, 65, 95, 95, 95, 95, 98, 98, 98, 98, 100, 100, 100, 100, - 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108, - 108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114, - 114, 114, 117, 117, 117, 117, 58, 58, 66, 66, 67, 67, 68, 68, 69, - 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, - 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, - 84, 85, 85, 86, 86, 87, 87, 89, 89, 106, 106, 107, 107, 113, 113, - 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38, 42, 44, 59, 88, - 90, -1, -1, 33, 33, 33, 33, 34, 34, 34, 34, 40, 40, 40, 40, - 41, 41, 41, 41, 63, 63, 63, 63, 39, 39, 43, 43, 124, 124, 35, - 62, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, - 36, 36, 36, 36, 36, 36, 64, 64, 64, 64, 64, 64, 64, 64, 91, - 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93, - 126, 126, 126, 126, 126, 126, 126, 126, 94, 94, 94, 94, 125, 125, 125, - 125, 60, 60, 96, 96, 123, 123, -1, -1, 92, 92, 195, 195, 208, 208, - 128, 130, 131, 162, 184, 194, 224, 226, -1, -1, 153, 153, 153, 153, 153, - 153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167, - 167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176, - 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179, - 179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216, - 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217, - 227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229, - 229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132, - 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146, - 146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160, - 163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170, - 170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185, - 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190, - 190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228, - 232, 232, 232, 232, 233, 233, 233, 233, 1, 1, 135, 135, 137, 137, 138, - 138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150, - 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168, - 168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191, - 197, 197, 231, 231, 239, 239, 9, 142, 144, 145, 148, 159, 171, 206, 215, - 225, 236, 237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199, 199, - 199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234, - 234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235, - 192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201, - 201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213, - 213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240, - 240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255, - 203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223, - 223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250, - 251, 251, 252, 252, 253, 253, 254, 254, 2, 3, 4, 5, 6, 7, 8, - 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 127, 220, 249, -1, 10, 10, 10, 10, 13, 13, 13, - 13, 22, 22, 22, 22, 256, 256, 256, 256, -}; - -static const uint8_t inverse_base64[256] = { - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, - 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, - 255, 64, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, - 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, -}; - -/* emission helpers */ -static grpc_error *on_hdr(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, - grpc_mdelem md, int add_to_table) { - if (GRPC_TRACER_ON(grpc_http_trace) && !GRPC_MDELEM_IS_INTERNED(md)) { - char *k = grpc_slice_to_c_string(GRPC_MDKEY(md)); - char *v = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log( - GPR_DEBUG, - "Decode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d", - k, v, GRPC_MDELEM_IS_INTERNED(md), GRPC_MDELEM_STORAGE(md), - grpc_slice_is_interned(GRPC_MDKEY(md)), - grpc_slice_is_interned(GRPC_MDVALUE(md))); - gpr_free(k); - gpr_free(v); - } - if (add_to_table) { - GPR_ASSERT(GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_INTERNED || - GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC); - grpc_error *err = grpc_chttp2_hptbl_add(exec_ctx, &p->table, md); - if (err != GRPC_ERROR_NONE) return err; - } - if (p->on_header == NULL) { - GRPC_MDELEM_UNREF(exec_ctx, md); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("on_header callback not set"); - } - p->on_header(exec_ctx, p->on_header_user_data, md); - return GRPC_ERROR_NONE; -} - -static grpc_slice take_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - grpc_chttp2_hpack_parser_string *str, - bool intern) { - grpc_slice s; - if (!str->copied) { - if (intern) { - s = grpc_slice_intern(str->data.referenced); - grpc_slice_unref_internal(exec_ctx, str->data.referenced); - } else { - s = str->data.referenced; - } - str->copied = true; - str->data.referenced = grpc_empty_slice(); - } else if (intern) { - s = grpc_slice_intern(grpc_slice_from_static_buffer( - str->data.copied.str, str->data.copied.length)); - } else { - s = grpc_slice_from_copied_buffer(str->data.copied.str, - str->data.copied.length); - } - str->data.copied.length = 0; - return s; -} - -/* jump to the next state */ -static grpc_error *parse_next(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - p->state = *p->next_state++; - return p->state(exec_ctx, p, cur, end); -} - -/* begin parsing a header: all functionality is encoded into lookup tables - above */ -static grpc_error *parse_begin(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - if (cur == end) { - p->state = parse_begin; - return GRPC_ERROR_NONE; - } - - return first_byte_action[first_byte_lut[*cur]](exec_ctx, p, cur, end); -} - -/* stream dependency and prioritization data: we just skip it */ -static grpc_error *parse_stream_weight(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (cur == end) { - p->state = parse_stream_weight; - return GRPC_ERROR_NONE; - } - - return p->after_prioritization(exec_ctx, p, cur + 1, end); -} - -static grpc_error *parse_stream_dep3(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (cur == end) { - p->state = parse_stream_dep3; - return GRPC_ERROR_NONE; - } - - return parse_stream_weight(exec_ctx, p, cur + 1, end); -} - -static grpc_error *parse_stream_dep2(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (cur == end) { - p->state = parse_stream_dep2; - return GRPC_ERROR_NONE; - } - - return parse_stream_dep3(exec_ctx, p, cur + 1, end); -} - -static grpc_error *parse_stream_dep1(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (cur == end) { - p->state = parse_stream_dep1; - return GRPC_ERROR_NONE; - } - - return parse_stream_dep2(exec_ctx, p, cur + 1, end); -} - -static grpc_error *parse_stream_dep0(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (cur == end) { - p->state = parse_stream_dep0; - return GRPC_ERROR_NONE; - } - - return parse_stream_dep1(exec_ctx, p, cur + 1, end); -} - -/* emit an indexed field; jumps to begin the next field on completion */ -static grpc_error *finish_indexed_field(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - if (GRPC_MDISNULL(md)) { - return grpc_error_set_int( - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid HPACK index received"), - GRPC_ERROR_INT_INDEX, (intptr_t)p->index), - GRPC_ERROR_INT_SIZE, (intptr_t)p->table.num_ents); - } - GRPC_MDELEM_REF(md); - GRPC_STATS_INC_HPACK_RECV_INDEXED(exec_ctx); - grpc_error *err = on_hdr(exec_ctx, p, md, 0); - if (err != GRPC_ERROR_NONE) return err; - return parse_begin(exec_ctx, p, cur, end); -} - -/* parse an indexed field with index < 127 */ -static grpc_error *parse_indexed_field(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - p->dynamic_table_update_allowed = 0; - p->index = (*cur) & 0x7f; - return finish_indexed_field(exec_ctx, p, cur + 1, end); -} - -/* parse an indexed field with index >= 127 */ -static grpc_error *parse_indexed_field_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - finish_indexed_field}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = 0x7f; - p->parsing.value = &p->index; - return parse_value0(exec_ctx, p, cur + 1, end); -} - -/* finish a literal header with incremental indexing */ -static grpc_error *finish_lithdr_incidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ - GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX(exec_ctx); - grpc_error *err = on_hdr( - exec_ctx, p, - grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)), - take_string(exec_ctx, p, &p->value, true)), - 1); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* finish a literal header with incremental indexing with no index */ -static grpc_error *finish_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V(exec_ctx); - grpc_error *err = on_hdr( - exec_ctx, p, - grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true), - take_string(exec_ctx, p, &p->value, true)), - 1); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* parse a literal header with incremental indexing; index < 63 */ -static grpc_error *parse_lithdr_incidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_value_string_with_indexed_key, finish_lithdr_incidx}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = (*cur) & 0x3f; - return parse_string_prefix(exec_ctx, p, cur + 1, end); -} - -/* parse a literal header with incremental indexing; index >= 63 */ -static grpc_error *parse_lithdr_incidx_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_string_prefix, parse_value_string_with_indexed_key, - finish_lithdr_incidx}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = 0x3f; - p->parsing.value = &p->index; - return parse_value0(exec_ctx, p, cur + 1, end); -} - -/* parse a literal header with incremental indexing; index = 0 */ -static grpc_error *parse_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_key_string, parse_string_prefix, - parse_value_string_with_literal_key, finish_lithdr_incidx_v}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - return parse_string_prefix(exec_ctx, p, cur + 1, end); -} - -/* finish a literal header without incremental indexing */ -static grpc_error *finish_lithdr_notidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ - GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX(exec_ctx); - grpc_error *err = on_hdr( - exec_ctx, p, - grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)), - take_string(exec_ctx, p, &p->value, false)), - 0); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* finish a literal header without incremental indexing with index = 0 */ -static grpc_error *finish_lithdr_notidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V(exec_ctx); - grpc_error *err = on_hdr( - exec_ctx, p, - grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true), - take_string(exec_ctx, p, &p->value, false)), - 0); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* parse a literal header without incremental indexing; index < 15 */ -static grpc_error *parse_lithdr_notidx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_value_string_with_indexed_key, finish_lithdr_notidx}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = (*cur) & 0xf; - return parse_string_prefix(exec_ctx, p, cur + 1, end); -} - -/* parse a literal header without incremental indexing; index >= 15 */ -static grpc_error *parse_lithdr_notidx_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_string_prefix, parse_value_string_with_indexed_key, - finish_lithdr_notidx}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = 0xf; - p->parsing.value = &p->index; - return parse_value0(exec_ctx, p, cur + 1, end); -} - -/* parse a literal header without incremental indexing; index == 0 */ -static grpc_error *parse_lithdr_notidx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_key_string, parse_string_prefix, - parse_value_string_with_literal_key, finish_lithdr_notidx_v}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - return parse_string_prefix(exec_ctx, p, cur + 1, end); -} - -/* finish a literal header that is never indexed */ -static grpc_error *finish_lithdr_nvridx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); - GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ - GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX(exec_ctx); - grpc_error *err = on_hdr( - exec_ctx, p, - grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)), - take_string(exec_ctx, p, &p->value, false)), - 0); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* finish a literal header that is never indexed with an extra value */ -static grpc_error *finish_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V(exec_ctx); - grpc_error *err = on_hdr( - exec_ctx, p, - grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true), - take_string(exec_ctx, p, &p->value, false)), - 0); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* parse a literal header that is never indexed; index < 15 */ -static grpc_error *parse_lithdr_nvridx(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_value_string_with_indexed_key, finish_lithdr_nvridx}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = (*cur) & 0xf; - return parse_string_prefix(exec_ctx, p, cur + 1, end); -} - -/* parse a literal header that is never indexed; index >= 15 */ -static grpc_error *parse_lithdr_nvridx_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_string_prefix, parse_value_string_with_indexed_key, - finish_lithdr_nvridx}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - p->index = 0xf; - p->parsing.value = &p->index; - return parse_value0(exec_ctx, p, cur + 1, end); -} - -/* parse a literal header that is never indexed; index == 0 */ -static grpc_error *parse_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - parse_key_string, parse_string_prefix, - parse_value_string_with_literal_key, finish_lithdr_nvridx_v}; - p->dynamic_table_update_allowed = 0; - p->next_state = and_then; - return parse_string_prefix(exec_ctx, p, cur + 1, end); -} - -/* finish parsing a max table size change */ -static grpc_error *finish_max_tbl_size(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); - } - grpc_error *err = - grpc_chttp2_hptbl_set_current_table_size(exec_ctx, &p->table, p->index); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_begin(exec_ctx, p, cur, end); -} - -/* parse a max table size change, max size < 15 */ -static grpc_error *parse_max_tbl_size(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (p->dynamic_table_update_allowed == 0) { - return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "More than two max table size changes in a single frame")); - } - p->dynamic_table_update_allowed--; - p->index = (*cur) & 0x1f; - return finish_max_tbl_size(exec_ctx, p, cur + 1, end); -} - -/* parse a max table size change, max size >= 15 */ -static grpc_error *parse_max_tbl_size_x(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, - const uint8_t *end) { - static const grpc_chttp2_hpack_parser_state and_then[] = { - finish_max_tbl_size}; - if (p->dynamic_table_update_allowed == 0) { - return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "More than two max table size changes in a single frame")); - } - p->dynamic_table_update_allowed--; - p->next_state = and_then; - p->index = 0x1f; - p->parsing.value = &p->index; - return parse_value0(exec_ctx, p, cur + 1, end); -} - -/* a parse error: jam the parse state into parse_error, and return error */ -static grpc_error *parse_error(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end, grpc_error *err) { - GPR_ASSERT(err != GRPC_ERROR_NONE); - if (p->last_error == GRPC_ERROR_NONE) { - p->last_error = GRPC_ERROR_REF(err); - } - p->state = still_parse_error; - return err; -} - -static grpc_error *still_parse_error(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - return GRPC_ERROR_REF(p->last_error); -} - -static grpc_error *parse_illegal_op(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - GPR_ASSERT(cur != end); - char *msg; - gpr_asprintf(&msg, "Illegal hpack op code %d", *cur); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return parse_error(exec_ctx, p, cur, end, err); -} - -/* parse the 1st byte of a varint into p->parsing.value - no overflow is possible */ -static grpc_error *parse_value0(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - if (cur == end) { - p->state = parse_value0; - return GRPC_ERROR_NONE; - } - - *p->parsing.value += (*cur) & 0x7f; - - if ((*cur) & 0x80) { - return parse_value1(exec_ctx, p, cur + 1, end); - } else { - return parse_next(exec_ctx, p, cur + 1, end); - } -} - -/* parse the 2nd byte of a varint into p->parsing.value - no overflow is possible */ -static grpc_error *parse_value1(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - if (cur == end) { - p->state = parse_value1; - return GRPC_ERROR_NONE; - } - - *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 7; - - if ((*cur) & 0x80) { - return parse_value2(exec_ctx, p, cur + 1, end); - } else { - return parse_next(exec_ctx, p, cur + 1, end); - } -} - -/* parse the 3rd byte of a varint into p->parsing.value - no overflow is possible */ -static grpc_error *parse_value2(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - if (cur == end) { - p->state = parse_value2; - return GRPC_ERROR_NONE; - } - - *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 14; - - if ((*cur) & 0x80) { - return parse_value3(exec_ctx, p, cur + 1, end); - } else { - return parse_next(exec_ctx, p, cur + 1, end); - } -} - -/* parse the 4th byte of a varint into p->parsing.value - no overflow is possible */ -static grpc_error *parse_value3(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - if (cur == end) { - p->state = parse_value3; - return GRPC_ERROR_NONE; - } - - *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 21; - - if ((*cur) & 0x80) { - return parse_value4(exec_ctx, p, cur + 1, end); - } else { - return parse_next(exec_ctx, p, cur + 1, end); - } -} - -/* parse the 5th byte of a varint into p->parsing.value - depending on the byte, we may overflow, and care must be taken */ -static grpc_error *parse_value4(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - uint8_t c; - uint32_t cur_value; - uint32_t add_value; - char *msg; - - if (cur == end) { - p->state = parse_value4; - return GRPC_ERROR_NONE; - } - - c = (*cur) & 0x7f; - if (c > 0xf) { - goto error; - } - - cur_value = *p->parsing.value; - add_value = ((uint32_t)c) << 28; - if (add_value > 0xffffffffu - cur_value) { - goto error; - } - - *p->parsing.value = cur_value + add_value; - - if ((*cur) & 0x80) { - return parse_value5up(exec_ctx, p, cur + 1, end); - } else { - return parse_next(exec_ctx, p, cur + 1, end); - } - -error: - gpr_asprintf(&msg, - "integer overflow in hpack integer decoding: have 0x%08x, " - "got byte 0x%02x on byte 5", - *p->parsing.value, *cur); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return parse_error(exec_ctx, p, cur, end, err); -} - -/* parse any trailing bytes in a varint: it's possible to append an arbitrary - number of 0x80's and not affect the value - a zero will terminate - and - anything else will overflow */ -static grpc_error *parse_value5up(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - while (cur != end && *cur == 0x80) { - ++cur; - } - - if (cur == end) { - p->state = parse_value5up; - return GRPC_ERROR_NONE; - } - - if (*cur == 0) { - return parse_next(exec_ctx, p, cur + 1, end); - } - - char *msg; - gpr_asprintf(&msg, - "integer overflow in hpack integer decoding: have 0x%08x, " - "got byte 0x%02x sometime after byte 5", - *p->parsing.value, *cur); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return parse_error(exec_ctx, p, cur, end, err); -} - -/* parse a string prefix */ -static grpc_error *parse_string_prefix(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (cur == end) { - p->state = parse_string_prefix; - return GRPC_ERROR_NONE; - } - - p->strlen = (*cur) & 0x7f; - p->huff = (*cur) >> 7; - if (p->strlen == 0x7f) { - p->parsing.value = &p->strlen; - return parse_value0(exec_ctx, p, cur + 1, end); - } else { - return parse_next(exec_ctx, p, cur + 1, end); - } -} - -/* append some bytes to a string */ -static void append_bytes(grpc_chttp2_hpack_parser_string *str, - const uint8_t *data, size_t length) { - if (length == 0) return; - if (length + str->data.copied.length > str->data.copied.capacity) { - GPR_ASSERT(str->data.copied.length + length <= UINT32_MAX); - str->data.copied.capacity = (uint32_t)(str->data.copied.length + length); - str->data.copied.str = - (char *)gpr_realloc(str->data.copied.str, str->data.copied.capacity); - } - memcpy(str->data.copied.str + str->data.copied.length, data, length); - GPR_ASSERT(length <= UINT32_MAX - str->data.copied.length); - str->data.copied.length += (uint32_t)length; -} - -static grpc_error *append_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - grpc_chttp2_hpack_parser_string *str = p->parsing.str; - uint32_t bits; - uint8_t decoded[3]; - switch ((binary_state)p->binary) { - case NOT_BINARY: - append_bytes(str, cur, (size_t)(end - cur)); - return GRPC_ERROR_NONE; - case BINARY_BEGIN: - if (cur == end) { - p->binary = BINARY_BEGIN; - return GRPC_ERROR_NONE; - } - if (*cur == 0) { - /* 'true-binary' case */ - ++cur; - p->binary = NOT_BINARY; - GRPC_STATS_INC_HPACK_RECV_BINARY(exec_ctx); - append_bytes(str, cur, (size_t)(end - cur)); - return GRPC_ERROR_NONE; - } - GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64(exec_ctx); - /* fallthrough */ - b64_byte0: - case B64_BYTE0: - if (cur == end) { - p->binary = B64_BYTE0; - return GRPC_ERROR_NONE; - } - bits = inverse_base64[*cur]; - ++cur; - if (bits == 255) - return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); - else if (bits == 64) - goto b64_byte0; - p->base64_buffer = bits << 18; - /* fallthrough */ - b64_byte1: - case B64_BYTE1: - if (cur == end) { - p->binary = B64_BYTE1; - return GRPC_ERROR_NONE; - } - bits = inverse_base64[*cur]; - ++cur; - if (bits == 255) - return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); - else if (bits == 64) - goto b64_byte1; - p->base64_buffer |= bits << 12; - /* fallthrough */ - b64_byte2: - case B64_BYTE2: - if (cur == end) { - p->binary = B64_BYTE2; - return GRPC_ERROR_NONE; - } - bits = inverse_base64[*cur]; - ++cur; - if (bits == 255) - return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); - else if (bits == 64) - goto b64_byte2; - p->base64_buffer |= bits << 6; - /* fallthrough */ - b64_byte3: - case B64_BYTE3: - if (cur == end) { - p->binary = B64_BYTE3; - return GRPC_ERROR_NONE; - } - bits = inverse_base64[*cur]; - ++cur; - if (bits == 255) - return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); - else if (bits == 64) - goto b64_byte3; - p->base64_buffer |= bits; - bits = p->base64_buffer; - decoded[0] = (uint8_t)(bits >> 16); - decoded[1] = (uint8_t)(bits >> 8); - decoded[2] = (uint8_t)(bits); - append_bytes(str, decoded, 3); - goto b64_byte0; - } - GPR_UNREACHABLE_CODE(return parse_error( - exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here"))); -} - -static grpc_error *finish_str(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - uint8_t decoded[2]; - uint32_t bits; - grpc_chttp2_hpack_parser_string *str = p->parsing.str; - switch ((binary_state)p->binary) { - case NOT_BINARY: - break; - case BINARY_BEGIN: - break; - case B64_BYTE0: - break; - case B64_BYTE1: - return parse_error(exec_ctx, p, cur, end, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "illegal base64 encoding")); /* illegal encoding */ - case B64_BYTE2: - bits = p->base64_buffer; - if (bits & 0xffff) { - char *msg; - gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%04x", - bits & 0xffff); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return parse_error(exec_ctx, p, cur, end, err); - } - decoded[0] = (uint8_t)(bits >> 16); - append_bytes(str, decoded, 1); - break; - case B64_BYTE3: - bits = p->base64_buffer; - if (bits & 0xff) { - char *msg; - gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%02x", - bits & 0xff); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return parse_error(exec_ctx, p, cur, end, err); - } - decoded[0] = (uint8_t)(bits >> 16); - decoded[1] = (uint8_t)(bits >> 8); - append_bytes(str, decoded, 2); - break; - } - return GRPC_ERROR_NONE; -} - -/* decode a nibble from a huffman encoded stream */ -static grpc_error *huff_nibble(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, uint8_t nibble) { - int16_t emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble]; - int16_t next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; - if (emit != -1) { - if (emit >= 0 && emit < 256) { - uint8_t c = (uint8_t)emit; - grpc_error *err = append_string(exec_ctx, p, &c, (&c) + 1); - if (err != GRPC_ERROR_NONE) return err; - } else { - assert(emit == 256); - } - } - p->huff_state = next; - return GRPC_ERROR_NONE; -} - -/* decode full bytes from a huffman encoded stream */ -static grpc_error *add_huff_bytes(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - for (; cur != end; ++cur) { - grpc_error *err = huff_nibble(exec_ctx, p, *cur >> 4); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - err = huff_nibble(exec_ctx, p, *cur & 0xf); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - } - return GRPC_ERROR_NONE; -} - -/* decode some string bytes based on the current decoding mode - (huffman or not) */ -static grpc_error *add_str_bytes(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - if (p->huff) { - return add_huff_bytes(exec_ctx, p, cur, end); - } else { - return append_string(exec_ctx, p, cur, end); - } -} - -/* parse a string - tries to do large chunks at a time */ -static grpc_error *parse_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - size_t remaining = p->strlen - p->strgot; - size_t given = (size_t)(end - cur); - if (remaining <= given) { - grpc_error *err = add_str_bytes(exec_ctx, p, cur, cur + remaining); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - err = finish_str(exec_ctx, p, cur + remaining, end); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_next(exec_ctx, p, cur + remaining, end); - } else { - grpc_error *err = add_str_bytes(exec_ctx, p, cur, cur + given); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - GPR_ASSERT(given <= UINT32_MAX - p->strgot); - p->strgot += (uint32_t)given; - p->state = parse_string; - return GRPC_ERROR_NONE; - } -} - -/* begin parsing a string - performs setup, calls parse_string */ -static grpc_error *begin_parse_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end, - uint8_t binary, - grpc_chttp2_hpack_parser_string *str) { - if (!p->huff && binary == NOT_BINARY && (end - cur) >= (intptr_t)p->strlen && - p->current_slice_refcount != NULL) { - GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx); - str->copied = false; - str->data.referenced.refcount = p->current_slice_refcount; - str->data.referenced.data.refcounted.bytes = (uint8_t *)cur; - str->data.referenced.data.refcounted.length = p->strlen; - grpc_slice_ref_internal(str->data.referenced); - return parse_next(exec_ctx, p, cur + p->strlen, end); - } - p->strgot = 0; - str->copied = true; - str->data.copied.length = 0; - p->parsing.str = str; - p->huff_state = 0; - p->binary = binary; - switch (p->binary) { - case NOT_BINARY: - if (p->huff) { - GRPC_STATS_INC_HPACK_RECV_HUFFMAN(exec_ctx); - } else { - GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx); - } - break; - case BINARY_BEGIN: - /* stats incremented later: don't know true binary or not */ - break; - default: - abort(); - } - return parse_string(exec_ctx, p, cur, end); -} - -/* parse the key string */ -static grpc_error *parse_key_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end) { - return begin_parse_string(exec_ctx, p, cur, end, NOT_BINARY, &p->key); -} - -/* check if a key represents a binary header or not */ - -static bool is_binary_literal_header(grpc_chttp2_hpack_parser *p) { - return grpc_is_binary_header( - p->key.copied ? grpc_slice_from_static_buffer(p->key.data.copied.str, - p->key.data.copied.length) - : p->key.data.referenced); -} - -static grpc_error *is_binary_indexed_header(grpc_chttp2_hpack_parser *p, - bool *is) { - grpc_mdelem elem = grpc_chttp2_hptbl_lookup(&p->table, p->index); - if (GRPC_MDISNULL(elem)) { - return grpc_error_set_int( - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid HPACK index received"), - GRPC_ERROR_INT_INDEX, (intptr_t)p->index), - GRPC_ERROR_INT_SIZE, (intptr_t)p->table.num_ents); - } - *is = grpc_is_binary_header(GRPC_MDKEY(elem)); - return GRPC_ERROR_NONE; -} - -/* parse the value string */ -static grpc_error *parse_value_string(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - const uint8_t *cur, const uint8_t *end, - bool is_binary) { - return begin_parse_string(exec_ctx, p, cur, end, - is_binary ? BINARY_BEGIN : NOT_BINARY, &p->value); -} - -static grpc_error *parse_value_string_with_indexed_key( - grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - bool is_binary = false; - grpc_error *err = is_binary_indexed_header(p, &is_binary); - if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); - return parse_value_string(exec_ctx, p, cur, end, is_binary); -} - -static grpc_error *parse_value_string_with_literal_key( - grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, - const uint8_t *end) { - return parse_value_string(exec_ctx, p, cur, end, is_binary_literal_header(p)); -} - -/* PUBLIC INTERFACE */ - -void grpc_chttp2_hpack_parser_init(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p) { - p->on_header = NULL; - p->on_header_user_data = NULL; - p->state = parse_begin; - p->key.data.referenced = grpc_empty_slice(); - p->key.data.copied.str = NULL; - p->key.data.copied.capacity = 0; - p->key.data.copied.length = 0; - p->value.data.referenced = grpc_empty_slice(); - p->value.data.copied.str = NULL; - p->value.data.copied.capacity = 0; - p->value.data.copied.length = 0; - p->dynamic_table_update_allowed = 2; - p->last_error = GRPC_ERROR_NONE; - grpc_chttp2_hptbl_init(exec_ctx, &p->table); -} - -void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) { - p->after_prioritization = p->state; - p->state = parse_stream_dep0; -} - -void grpc_chttp2_hpack_parser_destroy(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p) { - grpc_chttp2_hptbl_destroy(exec_ctx, &p->table); - GRPC_ERROR_UNREF(p->last_error); - grpc_slice_unref_internal(exec_ctx, p->key.data.referenced); - grpc_slice_unref_internal(exec_ctx, p->value.data.referenced); - gpr_free(p->key.data.copied.str); - gpr_free(p->value.data.copied.str); -} - -grpc_error *grpc_chttp2_hpack_parser_parse(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hpack_parser *p, - grpc_slice slice) { -/* max number of bytes to parse at a time... limits call stack depth on - * compilers without TCO */ -#define MAX_PARSE_LENGTH 1024 - p->current_slice_refcount = slice.refcount; - uint8_t *start = GRPC_SLICE_START_PTR(slice); - uint8_t *end = GRPC_SLICE_END_PTR(slice); - grpc_error *error = GRPC_ERROR_NONE; - while (start != end && error == GRPC_ERROR_NONE) { - uint8_t *target = start + GPR_MIN(MAX_PARSE_LENGTH, end - start); - error = p->state(exec_ctx, p, start, target); - start = target; - } - p->current_slice_refcount = NULL; - return error; -} - -typedef void (*maybe_complete_func_type)(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s); -static const maybe_complete_func_type maybe_complete_funcs[] = { - grpc_chttp2_maybe_complete_recv_initial_metadata, - grpc_chttp2_maybe_complete_recv_trailing_metadata}; - -static void force_client_rst_stream(grpc_exec_ctx *exec_ctx, void *sp, - grpc_error *error) { - grpc_chttp2_stream *s = (grpc_chttp2_stream *)sp; - grpc_chttp2_transport *t = s->t; - if (!s->write_closed) { - grpc_slice_buffer_add( - &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, - &s->stats.outgoing)); - grpc_chttp2_initiate_write(exec_ctx, t, - GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM); - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, GRPC_ERROR_NONE); - } - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "final_rst"); -} - -static void parse_stream_compression_md(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_metadata_batch *initial_metadata) { - if (initial_metadata->idx.named.content_encoding == NULL || - grpc_stream_compression_method_parse( - GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md), false, - &s->stream_decompression_method) == 0) { - s->stream_decompression_method = - GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; - } -} - -grpc_error *grpc_chttp2_header_parser_parse(grpc_exec_ctx *exec_ctx, - void *hpack_parser, - grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - grpc_chttp2_hpack_parser *parser = (grpc_chttp2_hpack_parser *)hpack_parser; - GPR_TIMER_BEGIN("grpc_chttp2_hpack_parser_parse", 0); - if (s != NULL) { - s->stats.incoming.header_bytes += GRPC_SLICE_LENGTH(slice); - } - grpc_error *error = grpc_chttp2_hpack_parser_parse(exec_ctx, parser, slice); - if (error != GRPC_ERROR_NONE) { - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return error; - } - if (is_last) { - if (parser->is_boundary && parser->state != parse_begin) { - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "end of header frame not aligned with a hpack record boundary"); - } - /* need to check for null stream: this can occur if we receive an invalid - stream id on a header */ - if (s != NULL) { - if (parser->is_boundary) { - if (s->header_frames_received == GPR_ARRAY_SIZE(s->metadata_buffer)) { - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Too many trailer frames"); - } - /* Process stream compression md element if it exists */ - if (s->header_frames_received == - 0) { /* Only acts on initial metadata */ - parse_stream_compression_md(exec_ctx, t, s, - &s->metadata_buffer[0].batch); - } - s->published_metadata[s->header_frames_received] = - GRPC_METADATA_PUBLISHED_FROM_WIRE; - maybe_complete_funcs[s->header_frames_received](exec_ctx, t, s); - s->header_frames_received++; - } - if (parser->is_eof) { - if (t->is_client && !s->write_closed) { - /* server eof ==> complete closure; we may need to forcefully close - the stream. Wait until the combiner lock is ready to be released - however -- it might be that we receive a RST_STREAM following this - and can avoid the extra write */ - GRPC_CHTTP2_STREAM_REF(s, "final_rst"); - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_CREATE(force_client_rst_stream, s, - grpc_combiner_finally_scheduler(t->combiner)), - GRPC_ERROR_NONE); - } - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, - GRPC_ERROR_NONE); - } - } - parser->on_header = NULL; - parser->on_header_user_data = NULL; - parser->is_boundary = 0xde; - parser->is_eof = 0xde; - parser->dynamic_table_update_allowed = 2; - } - GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); - return GRPC_ERROR_NONE; -} diff --git a/src/core/ext/transport/chttp2/transport/hpack_parser.cc b/src/core/ext/transport/chttp2/transport/hpack_parser.cc new file mode 100644 index 0000000000..3d1df19bc3 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/hpack_parser.cc @@ -0,0 +1,1761 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/hpack_parser.h" +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/bin_encoder.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/http2_errors.h" + +typedef enum { + NOT_BINARY, + BINARY_BEGIN, + B64_BYTE0, + B64_BYTE1, + B64_BYTE2, + B64_BYTE3 +} binary_state; + +/* How parsing works: + + The parser object keeps track of a function pointer which represents the + current parse state. + + Each time new bytes are presented, we call into the current state, which + recursively parses until all bytes in the given chunk are exhausted. + + The parse state that terminates then saves its function pointer to be the + current state so that it can resume when more bytes are available. + + It's expected that most optimizing compilers will turn this code into + a set of indirect jumps, and so not waste stack space. */ + +/* forward declarations for parsing states */ +static grpc_error *parse_begin(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_error(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end, grpc_error *error); +static grpc_error *still_parse_error(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_illegal_op(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); + +static grpc_error *parse_string_prefix(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_key_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_value_string_with_indexed_key( + grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_value_string_with_literal_key( + grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); + +static grpc_error *parse_value0(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_value1(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_value2(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_value3(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_value4(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_value5up(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); + +static grpc_error *parse_indexed_field(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_indexed_field_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_lithdr_incidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_lithdr_incidx_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_lithdr_notidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_lithdr_notidx_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_lithdr_notidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_lithdr_nvridx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_lithdr_nvridx_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end); +static grpc_error *parse_max_tbl_size(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); +static grpc_error *parse_max_tbl_size_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end); + +/* we translate the first byte of a hpack field into one of these decoding + cases, then use a lookup table to jump directly to the appropriate parser. + + _X => the integer index is all ones, meaning we need to do varint decoding + _V => the integer index is all zeros, meaning we need to decode an additional + string value */ +typedef enum { + INDEXED_FIELD, + INDEXED_FIELD_X, + LITHDR_INCIDX, + LITHDR_INCIDX_X, + LITHDR_INCIDX_V, + LITHDR_NOTIDX, + LITHDR_NOTIDX_X, + LITHDR_NOTIDX_V, + LITHDR_NVRIDX, + LITHDR_NVRIDX_X, + LITHDR_NVRIDX_V, + MAX_TBL_SIZE, + MAX_TBL_SIZE_X, + ILLEGAL +} first_byte_type; + +/* jump table of parse state functions -- order must match first_byte_type + above */ +static const grpc_chttp2_hpack_parser_state first_byte_action[] = { + parse_indexed_field, parse_indexed_field_x, parse_lithdr_incidx, + parse_lithdr_incidx_x, parse_lithdr_incidx_v, parse_lithdr_notidx, + parse_lithdr_notidx_x, parse_lithdr_notidx_v, parse_lithdr_nvridx, + parse_lithdr_nvridx_x, parse_lithdr_nvridx_v, parse_max_tbl_size, + parse_max_tbl_size_x, parse_illegal_op}; + +/* indexes the first byte to a parse state function - generated by + gen_hpack_tables.c */ +static const uint8_t first_byte_lut[256] = { + LITHDR_NOTIDX_V, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, + LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX, LITHDR_NOTIDX_X, + LITHDR_NVRIDX_V, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, + LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX, LITHDR_NVRIDX_X, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, + MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE, MAX_TBL_SIZE_X, + LITHDR_INCIDX_V, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, + LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX, LITHDR_INCIDX_X, + ILLEGAL, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, + INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD, INDEXED_FIELD_X, +}; + +/* state table for huffman decoding: given a state, gives an index/16 into + next_sub_tbl. Taking that index and adding the value of the nibble being + considered returns the next state. + + generated by gen_hpack_tables.c */ +static const uint8_t next_tbl[256] = { + 0, 1, 2, 3, 4, 1, 2, 5, 6, 1, 7, 8, 1, 3, 3, 9, 10, 11, 1, 1, + 1, 12, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 14, 1, 15, 16, 1, 17, 1, 15, 2, 7, 3, 18, 19, 1, 1, 1, 1, 20, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 7, 21, 1, 22, 1, 1, 1, 1, 1, + 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, 23, 24, 25, 1, 1, 1, 1, 2, 2, 2, + 26, 3, 3, 27, 10, 28, 1, 1, 1, 1, 1, 1, 2, 3, 29, 10, 30, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 32, 1, 1, 15, 33, 1, 34, 35, 9, 36, 1, 1, 1, + 1, 1, 1, 1, 37, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 26, 9, + 38, 1, 1, 1, 1, 1, 1, 1, 15, 2, 2, 2, 2, 26, 3, 3, 39, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 7, 3, 3, 3, 40, 2, + 41, 1, 1, 1, 42, 43, 1, 1, 44, 1, 1, 1, 1, 15, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 45, 46, 1, 1, 2, 2, 2, 35, 3, 3, 18, 47, 2, +}; + +/* next state, based upon current state and the current nibble: see above. + generated by gen_hpack_tables.c */ +static const int16_t next_sub_tbl[48 * 16] = { + 1, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 218, 2, 6, 10, 13, 14, 15, 16, 17, 2, 6, 10, 13, 14, 15, + 16, 17, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, 24, 3, + 7, 11, 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 199, 200, 201, 202, 203, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 9, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 3, 7, 11, 24, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 12, 132, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 18, 19, 20, 21, 4, 8, 4, + 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 22, 23, 91, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 3, + 7, 11, 24, 3, 7, 11, 24, 0, 0, 0, 0, 0, 41, 42, 43, + 2, 6, 10, 13, 14, 15, 16, 17, 3, 7, 11, 24, 3, 7, 11, + 24, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 44, 45, 2, 6, 10, 13, 14, 15, 16, 17, 46, 47, 48, 49, 50, + 51, 52, 57, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 54, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 72, 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 73, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 3, 7, 11, 24, 3, 7, 11, + 24, 3, 7, 11, 24, 0, 0, 0, 0, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 92, 0, 0, 0, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, + 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 4, + 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 2, 6, 10, 13, 14, 15, 16, 17, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 148, + 149, 150, 151, 3, 7, 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, + 0, 0, 152, 153, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, 11, + 24, 154, 155, 156, 164, 3, 7, 11, 24, 3, 7, 11, 24, 3, 7, + 11, 24, 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 161, 162, 163, 165, 166, 167, 168, 169, 170, 171, 172, + 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 4, 8, 4, 8, 4, 8, + 4, 8, 4, 8, 4, 8, 4, 8, 197, 198, 4, 8, 4, 8, 4, + 8, 4, 8, 0, 0, 0, 0, 0, 0, 219, 220, 3, 7, 11, 24, + 4, 8, 4, 8, 4, 8, 0, 0, 221, 222, 223, 224, 3, 7, 11, + 24, 3, 7, 11, 24, 4, 8, 4, 8, 4, 8, 225, 228, 4, 8, + 4, 8, 4, 8, 0, 0, 0, 0, 0, 0, 0, 0, 226, 227, 229, + 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 245, 246, 247, 248, 249, 250, 251, 252, + 253, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 255, +}; + +/* emission table: indexed like next_tbl, ultimately gives the byte to be + emitted, or -1 for no byte, or 256 for end of stream + + generated by gen_hpack_tables.c */ +static const uint16_t emit_tbl[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 0, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 0, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 0, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 0, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, + 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 0, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, + 248, +}; + +/* generated by gen_hpack_tables.c */ +static const int16_t emit_sub_tbl[249 * 16] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, + 49, 49, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 97, + 97, 97, 97, 48, 48, 49, 49, 50, 50, 97, 97, 99, 99, 101, 101, + 105, 105, 111, 111, 48, 49, 50, 97, 99, 101, 105, 111, 115, 116, -1, + -1, -1, -1, -1, -1, 32, 32, 32, 32, 32, 32, 32, 32, 37, 37, + 37, 37, 37, 37, 37, 37, 99, 99, 99, 99, 101, 101, 101, 101, 105, + 105, 105, 105, 111, 111, 111, 111, 115, 115, 116, 116, 32, 37, 45, 46, + 47, 51, 52, 53, 54, 55, 56, 57, 61, 61, 61, 61, 61, 61, 61, + 61, 65, 65, 65, 65, 65, 65, 65, 65, 115, 115, 115, 115, 116, 116, + 116, 116, 32, 32, 37, 37, 45, 45, 46, 46, 61, 65, 95, 98, 100, + 102, 103, 104, 108, 109, 110, 112, 114, 117, -1, -1, 58, 58, 58, 58, + 58, 58, 58, 58, 66, 66, 66, 66, 66, 66, 66, 66, 47, 47, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 61, 61, + 65, 65, 95, 95, 98, 98, 100, 100, 102, 102, 103, 103, 104, 104, 108, + 108, 109, 109, 110, 110, 112, 112, 114, 114, 117, 117, 58, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 89, 106, 107, 113, 118, 119, 120, 121, 122, -1, -1, + -1, -1, 38, 38, 38, 38, 38, 38, 38, 38, 42, 42, 42, 42, 42, + 42, 42, 42, 44, 44, 44, 44, 44, 44, 44, 44, 59, 59, 59, 59, + 59, 59, 59, 59, 88, 88, 88, 88, 88, 88, 88, 88, 90, 90, 90, + 90, 90, 90, 90, 90, 33, 33, 34, 34, 40, 40, 41, 41, 63, 63, + 39, 43, 124, -1, -1, -1, 35, 35, 35, 35, 35, 35, 35, 35, 62, + 62, 62, 62, 62, 62, 62, 62, 0, 0, 0, 0, 36, 36, 36, 36, + 64, 64, 64, 64, 91, 91, 91, 91, 69, 69, 69, 69, 69, 69, 69, + 69, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71, + 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 73, 73, 73, 73, 73, + 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, + 75, 75, 75, 75, 76, 76, 76, 76, 76, 76, 76, 76, 77, 77, 77, + 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, + 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80, 81, + 81, 81, 81, 81, 81, 81, 81, 82, 82, 82, 82, 82, 82, 82, 82, + 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, + 84, 85, 85, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, + 86, 86, 87, 87, 87, 87, 87, 87, 87, 87, 89, 89, 89, 89, 89, + 89, 89, 89, 106, 106, 106, 106, 106, 106, 106, 106, 107, 107, 107, 107, + 107, 107, 107, 107, 113, 113, 113, 113, 113, 113, 113, 113, 118, 118, 118, + 118, 118, 118, 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, 120, 120, + 120, 120, 120, 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, 121, 122, + 122, 122, 122, 122, 122, 122, 122, 38, 38, 38, 38, 42, 42, 42, 42, + 44, 44, 44, 44, 59, 59, 59, 59, 88, 88, 88, 88, 90, 90, 90, + 90, 33, 34, 40, 41, 63, -1, -1, -1, 39, 39, 39, 39, 39, 39, + 39, 39, 43, 43, 43, 43, 43, 43, 43, 43, 124, 124, 124, 124, 124, + 124, 124, 124, 35, 35, 35, 35, 62, 62, 62, 62, 0, 0, 36, 36, + 64, 64, 91, 91, 93, 93, 126, 126, 94, 125, -1, -1, 60, 60, 60, + 60, 60, 60, 60, 60, 96, 96, 96, 96, 96, 96, 96, 96, 123, 123, + 123, 123, 123, 123, 123, 123, -1, -1, -1, -1, -1, -1, -1, -1, 92, + 92, 92, 92, 92, 92, 92, 92, 195, 195, 195, 195, 195, 195, 195, 195, + 208, 208, 208, 208, 208, 208, 208, 208, 128, 128, 128, 128, 130, 130, 130, + 130, 131, 131, 131, 131, 162, 162, 162, 162, 184, 184, 184, 184, 194, 194, + 194, 194, 224, 224, 224, 224, 226, 226, 226, 226, 153, 153, 161, 161, 167, + 167, 172, 172, 176, 176, 177, 177, 179, 179, 209, 209, 216, 216, 217, 217, + 227, 227, 229, 229, 230, 230, 129, 132, 133, 134, 136, 146, 154, 156, 160, + 163, 164, 169, 170, 173, 178, 181, 185, 186, 187, 189, 190, 196, 198, 228, + 232, 233, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 135, + 135, 135, 135, 135, 135, 135, 135, 137, 137, 137, 137, 137, 137, 137, 137, + 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, + 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, + 141, 141, 143, 143, 143, 143, 143, 143, 143, 143, 147, 147, 147, 147, 147, + 147, 147, 147, 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, 150, 150, + 150, 150, 150, 150, 151, 151, 151, 151, 151, 151, 151, 151, 152, 152, 152, + 152, 152, 152, 152, 152, 155, 155, 155, 155, 155, 155, 155, 155, 157, 157, + 157, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 158, 158, 158, 165, + 165, 165, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 166, 166, 166, + 168, 168, 168, 168, 168, 168, 168, 168, 174, 174, 174, 174, 174, 174, 174, + 174, 175, 175, 175, 175, 175, 175, 175, 175, 180, 180, 180, 180, 180, 180, + 180, 180, 182, 182, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183, 183, + 183, 183, 183, 188, 188, 188, 188, 188, 188, 188, 188, 191, 191, 191, 191, + 191, 191, 191, 191, 197, 197, 197, 197, 197, 197, 197, 197, 231, 231, 231, + 231, 231, 231, 231, 231, 239, 239, 239, 239, 239, 239, 239, 239, 9, 9, + 9, 9, 142, 142, 142, 142, 144, 144, 144, 144, 145, 145, 145, 145, 148, + 148, 148, 148, 159, 159, 159, 159, 171, 171, 171, 171, 206, 206, 206, 206, + 215, 215, 215, 215, 225, 225, 225, 225, 236, 236, 236, 236, 237, 237, 237, + 237, 199, 199, 207, 207, 234, 234, 235, 235, 192, 193, 200, 201, 202, 205, + 210, 213, 218, 219, 238, 240, 242, 243, 255, -1, 203, 203, 203, 203, 203, + 203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 204, 211, 211, 211, 211, + 211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 214, 214, 214, + 214, 214, 214, 214, 214, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, + 222, 222, 222, 222, 222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 241, + 241, 241, 241, 241, 241, 241, 241, 244, 244, 244, 244, 244, 244, 244, 244, + 245, 245, 245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, + 246, 247, 247, 247, 247, 247, 247, 247, 247, 248, 248, 248, 248, 248, 248, + 248, 248, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, + 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 2, 2, 2, + 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, + 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 11, 11, 11, 11, 12, + 12, 12, 12, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, + 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, + 20, 21, 21, 21, 21, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, + 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, + 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 127, 127, 127, 127, + 220, 220, 220, 220, 249, 249, 249, 249, 10, 13, 22, 256, 93, 93, 93, + 93, 126, 126, 126, 126, 94, 94, 125, 125, 60, 96, 123, -1, 92, 195, + 208, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 128, + 128, 128, 128, 128, 128, 128, 128, 130, 130, 130, 130, 130, 130, 130, 130, + 131, 131, 131, 131, 131, 131, 131, 131, 162, 162, 162, 162, 162, 162, 162, + 162, 184, 184, 184, 184, 184, 184, 184, 184, 194, 194, 194, 194, 194, 194, + 194, 194, 224, 224, 224, 224, 224, 224, 224, 224, 226, 226, 226, 226, 226, + 226, 226, 226, 153, 153, 153, 153, 161, 161, 161, 161, 167, 167, 167, 167, + 172, 172, 172, 172, 176, 176, 176, 176, 177, 177, 177, 177, 179, 179, 179, + 179, 209, 209, 209, 209, 216, 216, 216, 216, 217, 217, 217, 217, 227, 227, + 227, 227, 229, 229, 229, 229, 230, 230, 230, 230, 129, 129, 132, 132, 133, + 133, 134, 134, 136, 136, 146, 146, 154, 154, 156, 156, 160, 160, 163, 163, + 164, 164, 169, 169, 170, 170, 173, 173, 178, 178, 181, 181, 185, 185, 186, + 186, 187, 187, 189, 189, 190, 190, 196, 196, 198, 198, 228, 228, 232, 232, + 233, 233, 1, 135, 137, 138, 139, 140, 141, 143, 147, 149, 150, 151, 152, + 155, 157, 158, 165, 166, 168, 174, 175, 180, 182, 183, 188, 191, 197, 231, + 239, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 9, 9, + 9, 9, 9, 9, 9, 142, 142, 142, 142, 142, 142, 142, 142, 144, 144, + 144, 144, 144, 144, 144, 144, 145, 145, 145, 145, 145, 145, 145, 145, 148, + 148, 148, 148, 148, 148, 148, 148, 159, 159, 159, 159, 159, 159, 159, 159, + 171, 171, 171, 171, 171, 171, 171, 171, 206, 206, 206, 206, 206, 206, 206, + 206, 215, 215, 215, 215, 215, 215, 215, 215, 225, 225, 225, 225, 225, 225, + 225, 225, 236, 236, 236, 236, 236, 236, 236, 236, 237, 237, 237, 237, 237, + 237, 237, 237, 199, 199, 199, 199, 207, 207, 207, 207, 234, 234, 234, 234, + 235, 235, 235, 235, 192, 192, 193, 193, 200, 200, 201, 201, 202, 202, 205, + 205, 210, 210, 213, 213, 218, 218, 219, 219, 238, 238, 240, 240, 242, 242, + 243, 243, 255, 255, 203, 204, 211, 212, 214, 221, 222, 223, 241, 244, 245, + 246, 247, 248, 250, 251, 252, 253, 254, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, + 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, + 12, 12, 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, + 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, + 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, + 20, 21, 21, 21, 21, 21, 21, 21, 21, 23, 23, 23, 23, 23, 23, + 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, + 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, + 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, + 29, 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, + 31, 31, 31, 31, 31, 31, 127, 127, 127, 127, 127, 127, 127, 127, 220, + 220, 220, 220, 220, 220, 220, 220, 249, 249, 249, 249, 249, 249, 249, 249, + 10, 10, 13, 13, 22, 22, 256, 256, 67, 67, 67, 67, 67, 67, 67, + 67, 68, 68, 68, 68, 68, 68, 68, 68, 95, 95, 95, 95, 95, 95, + 95, 95, 98, 98, 98, 98, 98, 98, 98, 98, 100, 100, 100, 100, 100, + 100, 100, 100, 102, 102, 102, 102, 102, 102, 102, 102, 103, 103, 103, 103, + 103, 103, 103, 103, 104, 104, 104, 104, 104, 104, 104, 104, 108, 108, 108, + 108, 108, 108, 108, 108, 109, 109, 109, 109, 109, 109, 109, 109, 110, 110, + 110, 110, 110, 110, 110, 110, 112, 112, 112, 112, 112, 112, 112, 112, 114, + 114, 114, 114, 114, 114, 114, 114, 117, 117, 117, 117, 117, 117, 117, 117, + 58, 58, 58, 58, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, + 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, + 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, + 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, + 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, + 83, 84, 84, 84, 84, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, + 87, 87, 89, 89, 89, 89, 106, 106, 106, 106, 107, 107, 107, 107, 113, + 113, 113, 113, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120, + 121, 121, 121, 121, 122, 122, 122, 122, 38, 38, 42, 42, 44, 44, 59, + 59, 88, 88, 90, 90, -1, -1, -1, -1, 33, 33, 33, 33, 33, 33, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 40, 40, 40, 40, 40, + 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 63, 63, 63, 63, + 63, 63, 63, 63, 39, 39, 39, 39, 43, 43, 43, 43, 124, 124, 124, + 124, 35, 35, 62, 62, 0, 36, 64, 91, 93, 126, -1, -1, 94, 94, + 94, 94, 94, 94, 94, 94, 125, 125, 125, 125, 125, 125, 125, 125, 60, + 60, 60, 60, 96, 96, 96, 96, 123, 123, 123, 123, -1, -1, -1, -1, + 92, 92, 92, 92, 195, 195, 195, 195, 208, 208, 208, 208, 128, 128, 130, + 130, 131, 131, 162, 162, 184, 184, 194, 194, 224, 224, 226, 226, 153, 161, + 167, 172, 176, 177, 179, 209, 216, 217, 227, 229, 230, -1, -1, -1, -1, + -1, -1, -1, 129, 129, 129, 129, 129, 129, 129, 129, 132, 132, 132, 132, + 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 133, 133, 134, 134, 134, + 134, 134, 134, 134, 134, 136, 136, 136, 136, 136, 136, 136, 136, 146, 146, + 146, 146, 146, 146, 146, 146, 154, 154, 154, 154, 154, 154, 154, 154, 156, + 156, 156, 156, 156, 156, 156, 156, 160, 160, 160, 160, 160, 160, 160, 160, + 163, 163, 163, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 164, 164, + 164, 169, 169, 169, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170, 170, + 170, 170, 173, 173, 173, 173, 173, 173, 173, 173, 178, 178, 178, 178, 178, + 178, 178, 178, 181, 181, 181, 181, 181, 181, 181, 181, 185, 185, 185, 185, + 185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, + 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 189, 189, 190, 190, + 190, 190, 190, 190, 190, 190, 196, 196, 196, 196, 196, 196, 196, 196, 198, + 198, 198, 198, 198, 198, 198, 198, 228, 228, 228, 228, 228, 228, 228, 228, + 232, 232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233, + 233, 1, 1, 1, 1, 135, 135, 135, 135, 137, 137, 137, 137, 138, 138, + 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 143, + 143, 143, 143, 147, 147, 147, 147, 149, 149, 149, 149, 150, 150, 150, 150, + 151, 151, 151, 151, 152, 152, 152, 152, 155, 155, 155, 155, 157, 157, 157, + 157, 158, 158, 158, 158, 165, 165, 165, 165, 166, 166, 166, 166, 168, 168, + 168, 168, 174, 174, 174, 174, 175, 175, 175, 175, 180, 180, 180, 180, 182, + 182, 182, 182, 183, 183, 183, 183, 188, 188, 188, 188, 191, 191, 191, 191, + 197, 197, 197, 197, 231, 231, 231, 231, 239, 239, 239, 239, 9, 9, 142, + 142, 144, 144, 145, 145, 148, 148, 159, 159, 171, 171, 206, 206, 215, 215, + 225, 225, 236, 236, 237, 237, 199, 207, 234, 235, 192, 192, 192, 192, 192, + 192, 192, 192, 193, 193, 193, 193, 193, 193, 193, 193, 200, 200, 200, 200, + 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 201, 202, 202, 202, + 202, 202, 202, 202, 202, 205, 205, 205, 205, 205, 205, 205, 205, 210, 210, + 210, 210, 210, 210, 210, 210, 213, 213, 213, 213, 213, 213, 213, 213, 218, + 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 219, + 238, 238, 238, 238, 238, 238, 238, 238, 240, 240, 240, 240, 240, 240, 240, + 240, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243, 243, + 243, 243, 255, 255, 255, 255, 255, 255, 255, 255, 203, 203, 203, 203, 204, + 204, 204, 204, 211, 211, 211, 211, 212, 212, 212, 212, 214, 214, 214, 214, + 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 241, 241, 241, + 241, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, + 247, 247, 248, 248, 248, 248, 250, 250, 250, 250, 251, 251, 251, 251, 252, + 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 11, 11, 12, 12, 14, + 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, + 30, 31, 31, 127, 127, 220, 220, 249, 249, -1, -1, 10, 10, 10, 10, + 10, 10, 10, 10, 13, 13, 13, 13, 13, 13, 13, 13, 22, 22, 22, + 22, 22, 22, 22, 22, 256, 256, 256, 256, 256, 256, 256, 256, 45, 45, + 45, 45, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 47, + 47, 47, 47, 47, 47, 47, 47, 51, 51, 51, 51, 51, 51, 51, 51, + 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, + 53, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, + 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, + 57, 57, 57, 50, 50, 50, 50, 50, 50, 50, 50, 97, 97, 97, 97, + 97, 97, 97, 97, 99, 99, 99, 99, 99, 99, 99, 99, 101, 101, 101, + 101, 101, 101, 101, 101, 105, 105, 105, 105, 105, 105, 105, 105, 111, 111, + 111, 111, 111, 111, 111, 111, 115, 115, 115, 115, 115, 115, 115, 115, 116, + 116, 116, 116, 116, 116, 116, 116, 32, 32, 32, 32, 37, 37, 37, 37, + 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 51, 51, 51, + 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, + 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 61, 61, 61, 61, 65, + 65, 65, 65, 95, 95, 95, 95, 98, 98, 98, 98, 100, 100, 100, 100, + 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 108, 108, 108, + 108, 109, 109, 109, 109, 110, 110, 110, 110, 112, 112, 112, 112, 114, 114, + 114, 114, 117, 117, 117, 117, 58, 58, 66, 66, 67, 67, 68, 68, 69, + 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, + 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, + 84, 85, 85, 86, 86, 87, 87, 89, 89, 106, 106, 107, 107, 113, 113, + 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 38, 42, 44, 59, 88, + 90, -1, -1, 33, 33, 33, 33, 34, 34, 34, 34, 40, 40, 40, 40, + 41, 41, 41, 41, 63, 63, 63, 63, 39, 39, 43, 43, 124, 124, 35, + 62, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 36, 36, + 36, 36, 36, 36, 36, 36, 64, 64, 64, 64, 64, 64, 64, 64, 91, + 91, 91, 91, 91, 91, 91, 91, 93, 93, 93, 93, 93, 93, 93, 93, + 126, 126, 126, 126, 126, 126, 126, 126, 94, 94, 94, 94, 125, 125, 125, + 125, 60, 60, 96, 96, 123, 123, -1, -1, 92, 92, 195, 195, 208, 208, + 128, 130, 131, 162, 184, 194, 224, 226, -1, -1, 153, 153, 153, 153, 153, + 153, 153, 153, 161, 161, 161, 161, 161, 161, 161, 161, 167, 167, 167, 167, + 167, 167, 167, 167, 172, 172, 172, 172, 172, 172, 172, 172, 176, 176, 176, + 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 177, 177, 179, 179, + 179, 179, 179, 179, 179, 179, 209, 209, 209, 209, 209, 209, 209, 209, 216, + 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 217, + 227, 227, 227, 227, 227, 227, 227, 227, 229, 229, 229, 229, 229, 229, 229, + 229, 230, 230, 230, 230, 230, 230, 230, 230, 129, 129, 129, 129, 132, 132, + 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 136, 136, 136, 136, 146, + 146, 146, 146, 154, 154, 154, 154, 156, 156, 156, 156, 160, 160, 160, 160, + 163, 163, 163, 163, 164, 164, 164, 164, 169, 169, 169, 169, 170, 170, 170, + 170, 173, 173, 173, 173, 178, 178, 178, 178, 181, 181, 181, 181, 185, 185, + 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 189, 189, 189, 189, 190, + 190, 190, 190, 196, 196, 196, 196, 198, 198, 198, 198, 228, 228, 228, 228, + 232, 232, 232, 232, 233, 233, 233, 233, 1, 1, 135, 135, 137, 137, 138, + 138, 139, 139, 140, 140, 141, 141, 143, 143, 147, 147, 149, 149, 150, 150, + 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 165, 165, 166, 166, 168, + 168, 174, 174, 175, 175, 180, 180, 182, 182, 183, 183, 188, 188, 191, 191, + 197, 197, 231, 231, 239, 239, 9, 142, 144, 145, 148, 159, 171, 206, 215, + 225, 236, 237, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 199, 199, + 199, 199, 199, 199, 199, 199, 207, 207, 207, 207, 207, 207, 207, 207, 234, + 234, 234, 234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235, + 192, 192, 192, 192, 193, 193, 193, 193, 200, 200, 200, 200, 201, 201, 201, + 201, 202, 202, 202, 202, 205, 205, 205, 205, 210, 210, 210, 210, 213, 213, + 213, 213, 218, 218, 218, 218, 219, 219, 219, 219, 238, 238, 238, 238, 240, + 240, 240, 240, 242, 242, 242, 242, 243, 243, 243, 243, 255, 255, 255, 255, + 203, 203, 204, 204, 211, 211, 212, 212, 214, 214, 221, 221, 222, 222, 223, + 223, 241, 241, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 250, 250, + 251, 251, 252, 252, 253, 253, 254, 254, 2, 3, 4, 5, 6, 7, 8, + 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 127, 220, 249, -1, 10, 10, 10, 10, 13, 13, 13, + 13, 22, 22, 22, 22, 256, 256, 256, 256, +}; + +static const uint8_t inverse_base64[256] = { + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, + 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, + 255, 64, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, +}; + +/* emission helpers */ +static grpc_error *on_hdr(grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, + grpc_mdelem md, int add_to_table) { + if (GRPC_TRACER_ON(grpc_http_trace) && !GRPC_MDELEM_IS_INTERNED(md)) { + char *k = grpc_slice_to_c_string(GRPC_MDKEY(md)); + char *v = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log( + GPR_DEBUG, + "Decode: '%s: %s', elem_interned=%d [%d], k_interned=%d, v_interned=%d", + k, v, GRPC_MDELEM_IS_INTERNED(md), GRPC_MDELEM_STORAGE(md), + grpc_slice_is_interned(GRPC_MDKEY(md)), + grpc_slice_is_interned(GRPC_MDVALUE(md))); + gpr_free(k); + gpr_free(v); + } + if (add_to_table) { + GPR_ASSERT(GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_INTERNED || + GRPC_MDELEM_STORAGE(md) == GRPC_MDELEM_STORAGE_STATIC); + grpc_error *err = grpc_chttp2_hptbl_add(exec_ctx, &p->table, md); + if (err != GRPC_ERROR_NONE) return err; + } + if (p->on_header == NULL) { + GRPC_MDELEM_UNREF(exec_ctx, md); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("on_header callback not set"); + } + p->on_header(exec_ctx, p->on_header_user_data, md); + return GRPC_ERROR_NONE; +} + +static grpc_slice take_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + grpc_chttp2_hpack_parser_string *str, + bool intern) { + grpc_slice s; + if (!str->copied) { + if (intern) { + s = grpc_slice_intern(str->data.referenced); + grpc_slice_unref_internal(exec_ctx, str->data.referenced); + } else { + s = str->data.referenced; + } + str->copied = true; + str->data.referenced = grpc_empty_slice(); + } else if (intern) { + s = grpc_slice_intern(grpc_slice_from_static_buffer( + str->data.copied.str, str->data.copied.length)); + } else { + s = grpc_slice_from_copied_buffer(str->data.copied.str, + str->data.copied.length); + } + str->data.copied.length = 0; + return s; +} + +/* jump to the next state */ +static grpc_error *parse_next(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + p->state = *p->next_state++; + return p->state(exec_ctx, p, cur, end); +} + +/* begin parsing a header: all functionality is encoded into lookup tables + above */ +static grpc_error *parse_begin(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + if (cur == end) { + p->state = parse_begin; + return GRPC_ERROR_NONE; + } + + return first_byte_action[first_byte_lut[*cur]](exec_ctx, p, cur, end); +} + +/* stream dependency and prioritization data: we just skip it */ +static grpc_error *parse_stream_weight(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (cur == end) { + p->state = parse_stream_weight; + return GRPC_ERROR_NONE; + } + + return p->after_prioritization(exec_ctx, p, cur + 1, end); +} + +static grpc_error *parse_stream_dep3(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (cur == end) { + p->state = parse_stream_dep3; + return GRPC_ERROR_NONE; + } + + return parse_stream_weight(exec_ctx, p, cur + 1, end); +} + +static grpc_error *parse_stream_dep2(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (cur == end) { + p->state = parse_stream_dep2; + return GRPC_ERROR_NONE; + } + + return parse_stream_dep3(exec_ctx, p, cur + 1, end); +} + +static grpc_error *parse_stream_dep1(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (cur == end) { + p->state = parse_stream_dep1; + return GRPC_ERROR_NONE; + } + + return parse_stream_dep2(exec_ctx, p, cur + 1, end); +} + +static grpc_error *parse_stream_dep0(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (cur == end) { + p->state = parse_stream_dep0; + return GRPC_ERROR_NONE; + } + + return parse_stream_dep1(exec_ctx, p, cur + 1, end); +} + +/* emit an indexed field; jumps to begin the next field on completion */ +static grpc_error *finish_indexed_field(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + if (GRPC_MDISNULL(md)) { + return grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid HPACK index received"), + GRPC_ERROR_INT_INDEX, (intptr_t)p->index), + GRPC_ERROR_INT_SIZE, (intptr_t)p->table.num_ents); + } + GRPC_MDELEM_REF(md); + GRPC_STATS_INC_HPACK_RECV_INDEXED(exec_ctx); + grpc_error *err = on_hdr(exec_ctx, p, md, 0); + if (err != GRPC_ERROR_NONE) return err; + return parse_begin(exec_ctx, p, cur, end); +} + +/* parse an indexed field with index < 127 */ +static grpc_error *parse_indexed_field(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + p->dynamic_table_update_allowed = 0; + p->index = (*cur) & 0x7f; + return finish_indexed_field(exec_ctx, p, cur + 1, end); +} + +/* parse an indexed field with index >= 127 */ +static grpc_error *parse_indexed_field_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + finish_indexed_field}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = 0x7f; + p->parsing.value = &p->index; + return parse_value0(exec_ctx, p, cur + 1, end); +} + +/* finish a literal header with incremental indexing */ +static grpc_error *finish_lithdr_incidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ + GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX(exec_ctx); + grpc_error *err = on_hdr( + exec_ctx, p, + grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)), + take_string(exec_ctx, p, &p->value, true)), + 1); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* finish a literal header with incremental indexing with no index */ +static grpc_error *finish_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + GRPC_STATS_INC_HPACK_RECV_LITHDR_INCIDX_V(exec_ctx); + grpc_error *err = on_hdr( + exec_ctx, p, + grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true), + take_string(exec_ctx, p, &p->value, true)), + 1); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* parse a literal header with incremental indexing; index < 63 */ +static grpc_error *parse_lithdr_incidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string_with_indexed_key, finish_lithdr_incidx}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = (*cur) & 0x3f; + return parse_string_prefix(exec_ctx, p, cur + 1, end); +} + +/* parse a literal header with incremental indexing; index >= 63 */ +static grpc_error *parse_lithdr_incidx_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_incidx}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = 0x3f; + p->parsing.value = &p->index; + return parse_value0(exec_ctx, p, cur + 1, end); +} + +/* parse a literal header with incremental indexing; index = 0 */ +static grpc_error *parse_lithdr_incidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_incidx_v}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + return parse_string_prefix(exec_ctx, p, cur + 1, end); +} + +/* finish a literal header without incremental indexing */ +static grpc_error *finish_lithdr_notidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ + GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX(exec_ctx); + grpc_error *err = on_hdr( + exec_ctx, p, + grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)), + take_string(exec_ctx, p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* finish a literal header without incremental indexing with index = 0 */ +static grpc_error *finish_lithdr_notidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + GRPC_STATS_INC_HPACK_RECV_LITHDR_NOTIDX_V(exec_ctx); + grpc_error *err = on_hdr( + exec_ctx, p, + grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true), + take_string(exec_ctx, p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* parse a literal header without incremental indexing; index < 15 */ +static grpc_error *parse_lithdr_notidx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string_with_indexed_key, finish_lithdr_notidx}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = (*cur) & 0xf; + return parse_string_prefix(exec_ctx, p, cur + 1, end); +} + +/* parse a literal header without incremental indexing; index >= 15 */ +static grpc_error *parse_lithdr_notidx_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_notidx}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(exec_ctx, p, cur + 1, end); +} + +/* parse a literal header without incremental indexing; index == 0 */ +static grpc_error *parse_lithdr_notidx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_notidx_v}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + return parse_string_prefix(exec_ctx, p, cur + 1, end); +} + +/* finish a literal header that is never indexed */ +static grpc_error *finish_lithdr_nvridx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + grpc_mdelem md = grpc_chttp2_hptbl_lookup(&p->table, p->index); + GPR_ASSERT(!GRPC_MDISNULL(md)); /* handled in string parsing */ + GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX(exec_ctx); + grpc_error *err = on_hdr( + exec_ctx, p, + grpc_mdelem_from_slices(exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(md)), + take_string(exec_ctx, p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* finish a literal header that is never indexed with an extra value */ +static grpc_error *finish_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + GRPC_STATS_INC_HPACK_RECV_LITHDR_NVRIDX_V(exec_ctx); + grpc_error *err = on_hdr( + exec_ctx, p, + grpc_mdelem_from_slices(exec_ctx, take_string(exec_ctx, p, &p->key, true), + take_string(exec_ctx, p, &p->value, false)), + 0); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* parse a literal header that is never indexed; index < 15 */ +static grpc_error *parse_lithdr_nvridx(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_value_string_with_indexed_key, finish_lithdr_nvridx}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = (*cur) & 0xf; + return parse_string_prefix(exec_ctx, p, cur + 1, end); +} + +/* parse a literal header that is never indexed; index >= 15 */ +static grpc_error *parse_lithdr_nvridx_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_string_prefix, parse_value_string_with_indexed_key, + finish_lithdr_nvridx}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + p->index = 0xf; + p->parsing.value = &p->index; + return parse_value0(exec_ctx, p, cur + 1, end); +} + +/* parse a literal header that is never indexed; index == 0 */ +static grpc_error *parse_lithdr_nvridx_v(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + parse_key_string, parse_string_prefix, + parse_value_string_with_literal_key, finish_lithdr_nvridx_v}; + p->dynamic_table_update_allowed = 0; + p->next_state = and_then; + return parse_string_prefix(exec_ctx, p, cur + 1, end); +} + +/* finish parsing a max table size change */ +static grpc_error *finish_max_tbl_size(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_INFO, "MAX TABLE SIZE: %d", p->index); + } + grpc_error *err = + grpc_chttp2_hptbl_set_current_table_size(exec_ctx, &p->table, p->index); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_begin(exec_ctx, p, cur, end); +} + +/* parse a max table size change, max size < 15 */ +static grpc_error *parse_max_tbl_size(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (p->dynamic_table_update_allowed == 0) { + return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "More than two max table size changes in a single frame")); + } + p->dynamic_table_update_allowed--; + p->index = (*cur) & 0x1f; + return finish_max_tbl_size(exec_ctx, p, cur + 1, end); +} + +/* parse a max table size change, max size >= 15 */ +static grpc_error *parse_max_tbl_size_x(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, + const uint8_t *end) { + static const grpc_chttp2_hpack_parser_state and_then[] = { + finish_max_tbl_size}; + if (p->dynamic_table_update_allowed == 0) { + return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "More than two max table size changes in a single frame")); + } + p->dynamic_table_update_allowed--; + p->next_state = and_then; + p->index = 0x1f; + p->parsing.value = &p->index; + return parse_value0(exec_ctx, p, cur + 1, end); +} + +/* a parse error: jam the parse state into parse_error, and return error */ +static grpc_error *parse_error(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end, grpc_error *err) { + GPR_ASSERT(err != GRPC_ERROR_NONE); + if (p->last_error == GRPC_ERROR_NONE) { + p->last_error = GRPC_ERROR_REF(err); + } + p->state = still_parse_error; + return err; +} + +static grpc_error *still_parse_error(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + return GRPC_ERROR_REF(p->last_error); +} + +static grpc_error *parse_illegal_op(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + GPR_ASSERT(cur != end); + char *msg; + gpr_asprintf(&msg, "Illegal hpack op code %d", *cur); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(exec_ctx, p, cur, end, err); +} + +/* parse the 1st byte of a varint into p->parsing.value + no overflow is possible */ +static grpc_error *parse_value0(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + if (cur == end) { + p->state = parse_value0; + return GRPC_ERROR_NONE; + } + + *p->parsing.value += (*cur) & 0x7f; + + if ((*cur) & 0x80) { + return parse_value1(exec_ctx, p, cur + 1, end); + } else { + return parse_next(exec_ctx, p, cur + 1, end); + } +} + +/* parse the 2nd byte of a varint into p->parsing.value + no overflow is possible */ +static grpc_error *parse_value1(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + if (cur == end) { + p->state = parse_value1; + return GRPC_ERROR_NONE; + } + + *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 7; + + if ((*cur) & 0x80) { + return parse_value2(exec_ctx, p, cur + 1, end); + } else { + return parse_next(exec_ctx, p, cur + 1, end); + } +} + +/* parse the 3rd byte of a varint into p->parsing.value + no overflow is possible */ +static grpc_error *parse_value2(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + if (cur == end) { + p->state = parse_value2; + return GRPC_ERROR_NONE; + } + + *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 14; + + if ((*cur) & 0x80) { + return parse_value3(exec_ctx, p, cur + 1, end); + } else { + return parse_next(exec_ctx, p, cur + 1, end); + } +} + +/* parse the 4th byte of a varint into p->parsing.value + no overflow is possible */ +static grpc_error *parse_value3(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + if (cur == end) { + p->state = parse_value3; + return GRPC_ERROR_NONE; + } + + *p->parsing.value += (((uint32_t)*cur) & 0x7f) << 21; + + if ((*cur) & 0x80) { + return parse_value4(exec_ctx, p, cur + 1, end); + } else { + return parse_next(exec_ctx, p, cur + 1, end); + } +} + +/* parse the 5th byte of a varint into p->parsing.value + depending on the byte, we may overflow, and care must be taken */ +static grpc_error *parse_value4(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + uint8_t c; + uint32_t cur_value; + uint32_t add_value; + char *msg; + + if (cur == end) { + p->state = parse_value4; + return GRPC_ERROR_NONE; + } + + c = (*cur) & 0x7f; + if (c > 0xf) { + goto error; + } + + cur_value = *p->parsing.value; + add_value = ((uint32_t)c) << 28; + if (add_value > 0xffffffffu - cur_value) { + goto error; + } + + *p->parsing.value = cur_value + add_value; + + if ((*cur) & 0x80) { + return parse_value5up(exec_ctx, p, cur + 1, end); + } else { + return parse_next(exec_ctx, p, cur + 1, end); + } + +error: + gpr_asprintf(&msg, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x on byte 5", + *p->parsing.value, *cur); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(exec_ctx, p, cur, end, err); +} + +/* parse any trailing bytes in a varint: it's possible to append an arbitrary + number of 0x80's and not affect the value - a zero will terminate - and + anything else will overflow */ +static grpc_error *parse_value5up(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + while (cur != end && *cur == 0x80) { + ++cur; + } + + if (cur == end) { + p->state = parse_value5up; + return GRPC_ERROR_NONE; + } + + if (*cur == 0) { + return parse_next(exec_ctx, p, cur + 1, end); + } + + char *msg; + gpr_asprintf(&msg, + "integer overflow in hpack integer decoding: have 0x%08x, " + "got byte 0x%02x sometime after byte 5", + *p->parsing.value, *cur); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(exec_ctx, p, cur, end, err); +} + +/* parse a string prefix */ +static grpc_error *parse_string_prefix(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (cur == end) { + p->state = parse_string_prefix; + return GRPC_ERROR_NONE; + } + + p->strlen = (*cur) & 0x7f; + p->huff = (*cur) >> 7; + if (p->strlen == 0x7f) { + p->parsing.value = &p->strlen; + return parse_value0(exec_ctx, p, cur + 1, end); + } else { + return parse_next(exec_ctx, p, cur + 1, end); + } +} + +/* append some bytes to a string */ +static void append_bytes(grpc_chttp2_hpack_parser_string *str, + const uint8_t *data, size_t length) { + if (length == 0) return; + if (length + str->data.copied.length > str->data.copied.capacity) { + GPR_ASSERT(str->data.copied.length + length <= UINT32_MAX); + str->data.copied.capacity = (uint32_t)(str->data.copied.length + length); + str->data.copied.str = + (char *)gpr_realloc(str->data.copied.str, str->data.copied.capacity); + } + memcpy(str->data.copied.str + str->data.copied.length, data, length); + GPR_ASSERT(length <= UINT32_MAX - str->data.copied.length); + str->data.copied.length += (uint32_t)length; +} + +static grpc_error *append_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + grpc_chttp2_hpack_parser_string *str = p->parsing.str; + uint32_t bits; + uint8_t decoded[3]; + switch ((binary_state)p->binary) { + case NOT_BINARY: + append_bytes(str, cur, (size_t)(end - cur)); + return GRPC_ERROR_NONE; + case BINARY_BEGIN: + if (cur == end) { + p->binary = BINARY_BEGIN; + return GRPC_ERROR_NONE; + } + if (*cur == 0) { + /* 'true-binary' case */ + ++cur; + p->binary = NOT_BINARY; + GRPC_STATS_INC_HPACK_RECV_BINARY(exec_ctx); + append_bytes(str, cur, (size_t)(end - cur)); + return GRPC_ERROR_NONE; + } + GRPC_STATS_INC_HPACK_RECV_BINARY_BASE64(exec_ctx); + /* fallthrough */ + b64_byte0: + case B64_BYTE0: + if (cur == end) { + p->binary = B64_BYTE0; + return GRPC_ERROR_NONE; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); + else if (bits == 64) + goto b64_byte0; + p->base64_buffer = bits << 18; + /* fallthrough */ + b64_byte1: + case B64_BYTE1: + if (cur == end) { + p->binary = B64_BYTE1; + return GRPC_ERROR_NONE; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); + else if (bits == 64) + goto b64_byte1; + p->base64_buffer |= bits << 12; + /* fallthrough */ + b64_byte2: + case B64_BYTE2: + if (cur == end) { + p->binary = B64_BYTE2; + return GRPC_ERROR_NONE; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); + else if (bits == 64) + goto b64_byte2; + p->base64_buffer |= bits << 6; + /* fallthrough */ + b64_byte3: + case B64_BYTE3: + if (cur == end) { + p->binary = B64_BYTE3; + return GRPC_ERROR_NONE; + } + bits = inverse_base64[*cur]; + ++cur; + if (bits == 255) + return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal base64 character")); + else if (bits == 64) + goto b64_byte3; + p->base64_buffer |= bits; + bits = p->base64_buffer; + decoded[0] = (uint8_t)(bits >> 16); + decoded[1] = (uint8_t)(bits >> 8); + decoded[2] = (uint8_t)(bits); + append_bytes(str, decoded, 3); + goto b64_byte0; + } + GPR_UNREACHABLE_CODE(return parse_error( + exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here"))); +} + +static grpc_error *finish_str(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + uint8_t decoded[2]; + uint32_t bits; + grpc_chttp2_hpack_parser_string *str = p->parsing.str; + switch ((binary_state)p->binary) { + case NOT_BINARY: + break; + case BINARY_BEGIN: + break; + case B64_BYTE0: + break; + case B64_BYTE1: + return parse_error(exec_ctx, p, cur, end, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "illegal base64 encoding")); /* illegal encoding */ + case B64_BYTE2: + bits = p->base64_buffer; + if (bits & 0xffff) { + char *msg; + gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%04x", + bits & 0xffff); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(exec_ctx, p, cur, end, err); + } + decoded[0] = (uint8_t)(bits >> 16); + append_bytes(str, decoded, 1); + break; + case B64_BYTE3: + bits = p->base64_buffer; + if (bits & 0xff) { + char *msg; + gpr_asprintf(&msg, "trailing bits in base64 encoding: 0x%02x", + bits & 0xff); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return parse_error(exec_ctx, p, cur, end, err); + } + decoded[0] = (uint8_t)(bits >> 16); + decoded[1] = (uint8_t)(bits >> 8); + append_bytes(str, decoded, 2); + break; + } + return GRPC_ERROR_NONE; +} + +/* decode a nibble from a huffman encoded stream */ +static grpc_error *huff_nibble(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, uint8_t nibble) { + int16_t emit = emit_sub_tbl[16 * emit_tbl[p->huff_state] + nibble]; + int16_t next = next_sub_tbl[16 * next_tbl[p->huff_state] + nibble]; + if (emit != -1) { + if (emit >= 0 && emit < 256) { + uint8_t c = (uint8_t)emit; + grpc_error *err = append_string(exec_ctx, p, &c, (&c) + 1); + if (err != GRPC_ERROR_NONE) return err; + } else { + assert(emit == 256); + } + } + p->huff_state = next; + return GRPC_ERROR_NONE; +} + +/* decode full bytes from a huffman encoded stream */ +static grpc_error *add_huff_bytes(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + for (; cur != end; ++cur) { + grpc_error *err = huff_nibble(exec_ctx, p, *cur >> 4); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + err = huff_nibble(exec_ctx, p, *cur & 0xf); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + } + return GRPC_ERROR_NONE; +} + +/* decode some string bytes based on the current decoding mode + (huffman or not) */ +static grpc_error *add_str_bytes(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + if (p->huff) { + return add_huff_bytes(exec_ctx, p, cur, end); + } else { + return append_string(exec_ctx, p, cur, end); + } +} + +/* parse a string - tries to do large chunks at a time */ +static grpc_error *parse_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + size_t remaining = p->strlen - p->strgot; + size_t given = (size_t)(end - cur); + if (remaining <= given) { + grpc_error *err = add_str_bytes(exec_ctx, p, cur, cur + remaining); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + err = finish_str(exec_ctx, p, cur + remaining, end); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_next(exec_ctx, p, cur + remaining, end); + } else { + grpc_error *err = add_str_bytes(exec_ctx, p, cur, cur + given); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + GPR_ASSERT(given <= UINT32_MAX - p->strgot); + p->strgot += (uint32_t)given; + p->state = parse_string; + return GRPC_ERROR_NONE; + } +} + +/* begin parsing a string - performs setup, calls parse_string */ +static grpc_error *begin_parse_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end, + uint8_t binary, + grpc_chttp2_hpack_parser_string *str) { + if (!p->huff && binary == NOT_BINARY && (end - cur) >= (intptr_t)p->strlen && + p->current_slice_refcount != NULL) { + GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx); + str->copied = false; + str->data.referenced.refcount = p->current_slice_refcount; + str->data.referenced.data.refcounted.bytes = (uint8_t *)cur; + str->data.referenced.data.refcounted.length = p->strlen; + grpc_slice_ref_internal(str->data.referenced); + return parse_next(exec_ctx, p, cur + p->strlen, end); + } + p->strgot = 0; + str->copied = true; + str->data.copied.length = 0; + p->parsing.str = str; + p->huff_state = 0; + p->binary = binary; + switch (p->binary) { + case NOT_BINARY: + if (p->huff) { + GRPC_STATS_INC_HPACK_RECV_HUFFMAN(exec_ctx); + } else { + GRPC_STATS_INC_HPACK_RECV_UNCOMPRESSED(exec_ctx); + } + break; + case BINARY_BEGIN: + /* stats incremented later: don't know true binary or not */ + break; + default: + abort(); + } + return parse_string(exec_ctx, p, cur, end); +} + +/* parse the key string */ +static grpc_error *parse_key_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end) { + return begin_parse_string(exec_ctx, p, cur, end, NOT_BINARY, &p->key); +} + +/* check if a key represents a binary header or not */ + +static bool is_binary_literal_header(grpc_chttp2_hpack_parser *p) { + return grpc_is_binary_header( + p->key.copied ? grpc_slice_from_static_buffer(p->key.data.copied.str, + p->key.data.copied.length) + : p->key.data.referenced); +} + +static grpc_error *is_binary_indexed_header(grpc_chttp2_hpack_parser *p, + bool *is) { + grpc_mdelem elem = grpc_chttp2_hptbl_lookup(&p->table, p->index); + if (GRPC_MDISNULL(elem)) { + return grpc_error_set_int( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid HPACK index received"), + GRPC_ERROR_INT_INDEX, (intptr_t)p->index), + GRPC_ERROR_INT_SIZE, (intptr_t)p->table.num_ents); + } + *is = grpc_is_binary_header(GRPC_MDKEY(elem)); + return GRPC_ERROR_NONE; +} + +/* parse the value string */ +static grpc_error *parse_value_string(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + const uint8_t *cur, const uint8_t *end, + bool is_binary) { + return begin_parse_string(exec_ctx, p, cur, end, + is_binary ? BINARY_BEGIN : NOT_BINARY, &p->value); +} + +static grpc_error *parse_value_string_with_indexed_key( + grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + bool is_binary = false; + grpc_error *err = is_binary_indexed_header(p, &is_binary); + if (err != GRPC_ERROR_NONE) return parse_error(exec_ctx, p, cur, end, err); + return parse_value_string(exec_ctx, p, cur, end, is_binary); +} + +static grpc_error *parse_value_string_with_literal_key( + grpc_exec_ctx *exec_ctx, grpc_chttp2_hpack_parser *p, const uint8_t *cur, + const uint8_t *end) { + return parse_value_string(exec_ctx, p, cur, end, is_binary_literal_header(p)); +} + +/* PUBLIC INTERFACE */ + +void grpc_chttp2_hpack_parser_init(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p) { + p->on_header = NULL; + p->on_header_user_data = NULL; + p->state = parse_begin; + p->key.data.referenced = grpc_empty_slice(); + p->key.data.copied.str = NULL; + p->key.data.copied.capacity = 0; + p->key.data.copied.length = 0; + p->value.data.referenced = grpc_empty_slice(); + p->value.data.copied.str = NULL; + p->value.data.copied.capacity = 0; + p->value.data.copied.length = 0; + p->dynamic_table_update_allowed = 2; + p->last_error = GRPC_ERROR_NONE; + grpc_chttp2_hptbl_init(exec_ctx, &p->table); +} + +void grpc_chttp2_hpack_parser_set_has_priority(grpc_chttp2_hpack_parser *p) { + p->after_prioritization = p->state; + p->state = parse_stream_dep0; +} + +void grpc_chttp2_hpack_parser_destroy(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p) { + grpc_chttp2_hptbl_destroy(exec_ctx, &p->table); + GRPC_ERROR_UNREF(p->last_error); + grpc_slice_unref_internal(exec_ctx, p->key.data.referenced); + grpc_slice_unref_internal(exec_ctx, p->value.data.referenced); + gpr_free(p->key.data.copied.str); + gpr_free(p->value.data.copied.str); +} + +grpc_error *grpc_chttp2_hpack_parser_parse(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hpack_parser *p, + grpc_slice slice) { +/* max number of bytes to parse at a time... limits call stack depth on + * compilers without TCO */ +#define MAX_PARSE_LENGTH 1024 + p->current_slice_refcount = slice.refcount; + uint8_t *start = GRPC_SLICE_START_PTR(slice); + uint8_t *end = GRPC_SLICE_END_PTR(slice); + grpc_error *error = GRPC_ERROR_NONE; + while (start != end && error == GRPC_ERROR_NONE) { + uint8_t *target = start + GPR_MIN(MAX_PARSE_LENGTH, end - start); + error = p->state(exec_ctx, p, start, target); + start = target; + } + p->current_slice_refcount = NULL; + return error; +} + +typedef void (*maybe_complete_func_type)(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s); +static const maybe_complete_func_type maybe_complete_funcs[] = { + grpc_chttp2_maybe_complete_recv_initial_metadata, + grpc_chttp2_maybe_complete_recv_trailing_metadata}; + +static void force_client_rst_stream(grpc_exec_ctx *exec_ctx, void *sp, + grpc_error *error) { + grpc_chttp2_stream *s = (grpc_chttp2_stream *)sp; + grpc_chttp2_transport *t = s->t; + if (!s->write_closed) { + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(s->id, GRPC_HTTP2_NO_ERROR, + &s->stats.outgoing)); + grpc_chttp2_initiate_write(exec_ctx, t, + GRPC_CHTTP2_INITIATE_WRITE_FORCE_RST_STREAM); + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, true, GRPC_ERROR_NONE); + } + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "final_rst"); +} + +static void parse_stream_compression_md(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_metadata_batch *initial_metadata) { + if (initial_metadata->idx.named.content_encoding == NULL || + grpc_stream_compression_method_parse( + GRPC_MDVALUE(initial_metadata->idx.named.content_encoding->md), false, + &s->stream_decompression_method) == 0) { + s->stream_decompression_method = + GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; + } +} + +grpc_error *grpc_chttp2_header_parser_parse(grpc_exec_ctx *exec_ctx, + void *hpack_parser, + grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + grpc_chttp2_hpack_parser *parser = (grpc_chttp2_hpack_parser *)hpack_parser; + GPR_TIMER_BEGIN("grpc_chttp2_hpack_parser_parse", 0); + if (s != NULL) { + s->stats.incoming.header_bytes += GRPC_SLICE_LENGTH(slice); + } + grpc_error *error = grpc_chttp2_hpack_parser_parse(exec_ctx, parser, slice); + if (error != GRPC_ERROR_NONE) { + GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); + return error; + } + if (is_last) { + if (parser->is_boundary && parser->state != parse_begin) { + GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "end of header frame not aligned with a hpack record boundary"); + } + /* need to check for null stream: this can occur if we receive an invalid + stream id on a header */ + if (s != NULL) { + if (parser->is_boundary) { + if (s->header_frames_received == GPR_ARRAY_SIZE(s->metadata_buffer)) { + GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Too many trailer frames"); + } + /* Process stream compression md element if it exists */ + if (s->header_frames_received == + 0) { /* Only acts on initial metadata */ + parse_stream_compression_md(exec_ctx, t, s, + &s->metadata_buffer[0].batch); + } + s->published_metadata[s->header_frames_received] = + GRPC_METADATA_PUBLISHED_FROM_WIRE; + maybe_complete_funcs[s->header_frames_received](exec_ctx, t, s); + s->header_frames_received++; + } + if (parser->is_eof) { + if (t->is_client && !s->write_closed) { + /* server eof ==> complete closure; we may need to forcefully close + the stream. Wait until the combiner lock is ready to be released + however -- it might be that we receive a RST_STREAM following this + and can avoid the extra write */ + GRPC_CHTTP2_STREAM_REF(s, "final_rst"); + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_CREATE(force_client_rst_stream, s, + grpc_combiner_finally_scheduler(t->combiner)), + GRPC_ERROR_NONE); + } + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, + GRPC_ERROR_NONE); + } + } + parser->on_header = NULL; + parser->on_header_user_data = NULL; + parser->is_boundary = 0xde; + parser->is_eof = 0xde; + parser->dynamic_table_update_allowed = 2; + } + GPR_TIMER_END("grpc_chttp2_hpack_parser_parse", 0); + return GRPC_ERROR_NONE; +} diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.c b/src/core/ext/transport/chttp2/transport/hpack_table.c deleted file mode 100644 index bbd135a318..0000000000 --- a/src/core/ext/transport/chttp2/transport/hpack_table.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/hpack_table.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/support/murmur_hash.h" - -extern grpc_tracer_flag grpc_http_trace; - -static struct { - const char *key; - const char *value; -} static_table[] = { - /* 0: */ - {NULL, NULL}, - /* 1: */ - {":authority", ""}, - /* 2: */ - {":method", "GET"}, - /* 3: */ - {":method", "POST"}, - /* 4: */ - {":path", "/"}, - /* 5: */ - {":path", "/index.html"}, - /* 6: */ - {":scheme", "http"}, - /* 7: */ - {":scheme", "https"}, - /* 8: */ - {":status", "200"}, - /* 9: */ - {":status", "204"}, - /* 10: */ - {":status", "206"}, - /* 11: */ - {":status", "304"}, - /* 12: */ - {":status", "400"}, - /* 13: */ - {":status", "404"}, - /* 14: */ - {":status", "500"}, - /* 15: */ - {"accept-charset", ""}, - /* 16: */ - {"accept-encoding", "gzip, deflate"}, - /* 17: */ - {"accept-language", ""}, - /* 18: */ - {"accept-ranges", ""}, - /* 19: */ - {"accept", ""}, - /* 20: */ - {"access-control-allow-origin", ""}, - /* 21: */ - {"age", ""}, - /* 22: */ - {"allow", ""}, - /* 23: */ - {"authorization", ""}, - /* 24: */ - {"cache-control", ""}, - /* 25: */ - {"content-disposition", ""}, - /* 26: */ - {"content-encoding", ""}, - /* 27: */ - {"content-language", ""}, - /* 28: */ - {"content-length", ""}, - /* 29: */ - {"content-location", ""}, - /* 30: */ - {"content-range", ""}, - /* 31: */ - {"content-type", ""}, - /* 32: */ - {"cookie", ""}, - /* 33: */ - {"date", ""}, - /* 34: */ - {"etag", ""}, - /* 35: */ - {"expect", ""}, - /* 36: */ - {"expires", ""}, - /* 37: */ - {"from", ""}, - /* 38: */ - {"host", ""}, - /* 39: */ - {"if-match", ""}, - /* 40: */ - {"if-modified-since", ""}, - /* 41: */ - {"if-none-match", ""}, - /* 42: */ - {"if-range", ""}, - /* 43: */ - {"if-unmodified-since", ""}, - /* 44: */ - {"last-modified", ""}, - /* 45: */ - {"link", ""}, - /* 46: */ - {"location", ""}, - /* 47: */ - {"max-forwards", ""}, - /* 48: */ - {"proxy-authenticate", ""}, - /* 49: */ - {"proxy-authorization", ""}, - /* 50: */ - {"range", ""}, - /* 51: */ - {"referer", ""}, - /* 52: */ - {"refresh", ""}, - /* 53: */ - {"retry-after", ""}, - /* 54: */ - {"server", ""}, - /* 55: */ - {"set-cookie", ""}, - /* 56: */ - {"strict-transport-security", ""}, - /* 57: */ - {"transfer-encoding", ""}, - /* 58: */ - {"user-agent", ""}, - /* 59: */ - {"vary", ""}, - /* 60: */ - {"via", ""}, - /* 61: */ - {"www-authenticate", ""}, -}; - -static uint32_t entries_for_bytes(uint32_t bytes) { - return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / - GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; -} - -void grpc_chttp2_hptbl_init(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) { - size_t i; - - memset(tbl, 0, sizeof(*tbl)); - tbl->current_table_bytes = tbl->max_bytes = - GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE; - tbl->max_entries = tbl->cap_entries = - entries_for_bytes(tbl->current_table_bytes); - tbl->ents = (grpc_mdelem *)gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries); - memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries); - for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { - tbl->static_ents[i - 1] = grpc_mdelem_from_slices( - exec_ctx, - grpc_slice_intern(grpc_slice_from_static_string(static_table[i].key)), - grpc_slice_intern( - grpc_slice_from_static_string(static_table[i].value))); - } -} - -void grpc_chttp2_hptbl_destroy(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hptbl *tbl) { - size_t i; - for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { - GRPC_MDELEM_UNREF(exec_ctx, tbl->static_ents[i]); - } - for (i = 0; i < tbl->num_ents; i++) { - GRPC_MDELEM_UNREF(exec_ctx, - tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]); - } - gpr_free(tbl->ents); -} - -grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, - uint32_t tbl_index) { - /* Static table comes first, just return an entry from it */ - if (tbl_index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { - return tbl->static_ents[tbl_index - 1]; - } - /* Otherwise, find the value in the list of valid entries */ - tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); - if (tbl_index < tbl->num_ents) { - uint32_t offset = - (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries; - return tbl->ents[offset]; - } - /* Invalid entry: return error */ - return GRPC_MDNULL; -} - -/* Evict one element from the table */ -static void evict1(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) { - grpc_mdelem first_ent = tbl->ents[tbl->first_ent]; - size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(first_ent)) + - GRPC_SLICE_LENGTH(GRPC_MDVALUE(first_ent)) + - GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; - GPR_ASSERT(elem_bytes <= tbl->mem_used); - tbl->mem_used -= (uint32_t)elem_bytes; - tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries); - tbl->num_ents--; - GRPC_MDELEM_UNREF(exec_ctx, first_ent); -} - -static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) { - grpc_mdelem *ents = (grpc_mdelem *)gpr_malloc(sizeof(*ents) * new_cap); - uint32_t i; - - for (i = 0; i < tbl->num_ents; i++) { - ents[i] = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]; - } - gpr_free(tbl->ents); - tbl->ents = ents; - tbl->cap_entries = new_cap; - tbl->first_ent = 0; -} - -void grpc_chttp2_hptbl_set_max_bytes(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hptbl *tbl, - uint32_t max_bytes) { - if (tbl->max_bytes == max_bytes) { - return; - } - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes); - } - while (tbl->mem_used > max_bytes) { - evict1(exec_ctx, tbl); - } - tbl->max_bytes = max_bytes; -} - -grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hptbl *tbl, - uint32_t bytes) { - if (tbl->current_table_bytes == bytes) { - return GRPC_ERROR_NONE; - } - if (bytes > tbl->max_bytes) { - char *msg; - gpr_asprintf(&msg, - "Attempt to make hpack table %d bytes when max is %d bytes", - bytes, tbl->max_bytes); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes); - } - while (tbl->mem_used > bytes) { - evict1(exec_ctx, tbl); - } - tbl->current_table_bytes = bytes; - tbl->max_entries = entries_for_bytes(bytes); - if (tbl->max_entries > tbl->cap_entries) { - rebuild_ents(tbl, GPR_MAX(tbl->max_entries, 2 * tbl->cap_entries)); - } else if (tbl->max_entries < tbl->cap_entries / 3) { - uint32_t new_cap = GPR_MAX(tbl->max_entries, 16u); - if (new_cap != tbl->cap_entries) { - rebuild_ents(tbl, new_cap); - } - } - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_chttp2_hptbl_add(grpc_exec_ctx *exec_ctx, - grpc_chttp2_hptbl *tbl, grpc_mdelem md) { - /* determine how many bytes of buffer this entry represents */ - size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(md)) + - GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)) + - GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; - - if (tbl->current_table_bytes > tbl->max_bytes) { - char *msg; - gpr_asprintf( - &msg, - "HPACK max table size reduced to %d but not reflected by hpack " - "stream (still at %d)", - tbl->max_bytes, tbl->current_table_bytes); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - - /* we can't add elements bigger than the max table size */ - if (elem_bytes > tbl->current_table_bytes) { - /* HPACK draft 10 section 4.4 states: - * If the size of the new entry is less than or equal to the maximum - * size, that entry is added to the table. It is not an error to - * attempt to add an entry that is larger than the maximum size; an - * attempt to add an entry larger than the entire table causes - * the table - * to be emptied of all existing entries, and results in an - * empty table. - */ - while (tbl->num_ents) { - evict1(exec_ctx, tbl); - } - return GRPC_ERROR_NONE; - } - - /* evict entries to ensure no overflow */ - while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) { - evict1(exec_ctx, tbl); - } - - /* copy the finalized entry in */ - tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] = - GRPC_MDELEM_REF(md); - - /* update accounting values */ - tbl->num_ents++; - tbl->mem_used += (uint32_t)elem_bytes; - return GRPC_ERROR_NONE; -} - -grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( - const grpc_chttp2_hptbl *tbl, grpc_mdelem md) { - grpc_chttp2_hptbl_find_result r = {0, 0}; - uint32_t i; - - /* See if the string is in the static table */ - for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { - grpc_mdelem ent = tbl->static_ents[i]; - if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue; - r.index = i + 1u; - r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent)); - if (r.has_value) return r; - } - - /* Scan the dynamic table */ - for (i = 0; i < tbl->num_ents; i++) { - uint32_t idx = - (uint32_t)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY); - grpc_mdelem ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]; - if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue; - r.index = idx; - r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent)); - if (r.has_value) return r; - } - - return r; -} diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc new file mode 100644 index 0000000000..bbd135a318 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc @@ -0,0 +1,369 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/hpack_table.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/support/murmur_hash.h" + +extern grpc_tracer_flag grpc_http_trace; + +static struct { + const char *key; + const char *value; +} static_table[] = { + /* 0: */ + {NULL, NULL}, + /* 1: */ + {":authority", ""}, + /* 2: */ + {":method", "GET"}, + /* 3: */ + {":method", "POST"}, + /* 4: */ + {":path", "/"}, + /* 5: */ + {":path", "/index.html"}, + /* 6: */ + {":scheme", "http"}, + /* 7: */ + {":scheme", "https"}, + /* 8: */ + {":status", "200"}, + /* 9: */ + {":status", "204"}, + /* 10: */ + {":status", "206"}, + /* 11: */ + {":status", "304"}, + /* 12: */ + {":status", "400"}, + /* 13: */ + {":status", "404"}, + /* 14: */ + {":status", "500"}, + /* 15: */ + {"accept-charset", ""}, + /* 16: */ + {"accept-encoding", "gzip, deflate"}, + /* 17: */ + {"accept-language", ""}, + /* 18: */ + {"accept-ranges", ""}, + /* 19: */ + {"accept", ""}, + /* 20: */ + {"access-control-allow-origin", ""}, + /* 21: */ + {"age", ""}, + /* 22: */ + {"allow", ""}, + /* 23: */ + {"authorization", ""}, + /* 24: */ + {"cache-control", ""}, + /* 25: */ + {"content-disposition", ""}, + /* 26: */ + {"content-encoding", ""}, + /* 27: */ + {"content-language", ""}, + /* 28: */ + {"content-length", ""}, + /* 29: */ + {"content-location", ""}, + /* 30: */ + {"content-range", ""}, + /* 31: */ + {"content-type", ""}, + /* 32: */ + {"cookie", ""}, + /* 33: */ + {"date", ""}, + /* 34: */ + {"etag", ""}, + /* 35: */ + {"expect", ""}, + /* 36: */ + {"expires", ""}, + /* 37: */ + {"from", ""}, + /* 38: */ + {"host", ""}, + /* 39: */ + {"if-match", ""}, + /* 40: */ + {"if-modified-since", ""}, + /* 41: */ + {"if-none-match", ""}, + /* 42: */ + {"if-range", ""}, + /* 43: */ + {"if-unmodified-since", ""}, + /* 44: */ + {"last-modified", ""}, + /* 45: */ + {"link", ""}, + /* 46: */ + {"location", ""}, + /* 47: */ + {"max-forwards", ""}, + /* 48: */ + {"proxy-authenticate", ""}, + /* 49: */ + {"proxy-authorization", ""}, + /* 50: */ + {"range", ""}, + /* 51: */ + {"referer", ""}, + /* 52: */ + {"refresh", ""}, + /* 53: */ + {"retry-after", ""}, + /* 54: */ + {"server", ""}, + /* 55: */ + {"set-cookie", ""}, + /* 56: */ + {"strict-transport-security", ""}, + /* 57: */ + {"transfer-encoding", ""}, + /* 58: */ + {"user-agent", ""}, + /* 59: */ + {"vary", ""}, + /* 60: */ + {"via", ""}, + /* 61: */ + {"www-authenticate", ""}, +}; + +static uint32_t entries_for_bytes(uint32_t bytes) { + return (bytes + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD - 1) / + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; +} + +void grpc_chttp2_hptbl_init(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) { + size_t i; + + memset(tbl, 0, sizeof(*tbl)); + tbl->current_table_bytes = tbl->max_bytes = + GRPC_CHTTP2_INITIAL_HPACK_TABLE_SIZE; + tbl->max_entries = tbl->cap_entries = + entries_for_bytes(tbl->current_table_bytes); + tbl->ents = (grpc_mdelem *)gpr_malloc(sizeof(*tbl->ents) * tbl->cap_entries); + memset(tbl->ents, 0, sizeof(*tbl->ents) * tbl->cap_entries); + for (i = 1; i <= GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + tbl->static_ents[i - 1] = grpc_mdelem_from_slices( + exec_ctx, + grpc_slice_intern(grpc_slice_from_static_string(static_table[i].key)), + grpc_slice_intern( + grpc_slice_from_static_string(static_table[i].value))); + } +} + +void grpc_chttp2_hptbl_destroy(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hptbl *tbl) { + size_t i; + for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + GRPC_MDELEM_UNREF(exec_ctx, tbl->static_ents[i]); + } + for (i = 0; i < tbl->num_ents; i++) { + GRPC_MDELEM_UNREF(exec_ctx, + tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]); + } + gpr_free(tbl->ents); +} + +grpc_mdelem grpc_chttp2_hptbl_lookup(const grpc_chttp2_hptbl *tbl, + uint32_t tbl_index) { + /* Static table comes first, just return an entry from it */ + if (tbl_index <= GRPC_CHTTP2_LAST_STATIC_ENTRY) { + return tbl->static_ents[tbl_index - 1]; + } + /* Otherwise, find the value in the list of valid entries */ + tbl_index -= (GRPC_CHTTP2_LAST_STATIC_ENTRY + 1); + if (tbl_index < tbl->num_ents) { + uint32_t offset = + (tbl->num_ents - 1u - tbl_index + tbl->first_ent) % tbl->cap_entries; + return tbl->ents[offset]; + } + /* Invalid entry: return error */ + return GRPC_MDNULL; +} + +/* Evict one element from the table */ +static void evict1(grpc_exec_ctx *exec_ctx, grpc_chttp2_hptbl *tbl) { + grpc_mdelem first_ent = tbl->ents[tbl->first_ent]; + size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(first_ent)) + + GRPC_SLICE_LENGTH(GRPC_MDVALUE(first_ent)) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + GPR_ASSERT(elem_bytes <= tbl->mem_used); + tbl->mem_used -= (uint32_t)elem_bytes; + tbl->first_ent = ((tbl->first_ent + 1) % tbl->cap_entries); + tbl->num_ents--; + GRPC_MDELEM_UNREF(exec_ctx, first_ent); +} + +static void rebuild_ents(grpc_chttp2_hptbl *tbl, uint32_t new_cap) { + grpc_mdelem *ents = (grpc_mdelem *)gpr_malloc(sizeof(*ents) * new_cap); + uint32_t i; + + for (i = 0; i < tbl->num_ents; i++) { + ents[i] = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]; + } + gpr_free(tbl->ents); + tbl->ents = ents; + tbl->cap_entries = new_cap; + tbl->first_ent = 0; +} + +void grpc_chttp2_hptbl_set_max_bytes(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hptbl *tbl, + uint32_t max_bytes) { + if (tbl->max_bytes == max_bytes) { + return; + } + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_DEBUG, "Update hpack parser max size to %d", max_bytes); + } + while (tbl->mem_used > max_bytes) { + evict1(exec_ctx, tbl); + } + tbl->max_bytes = max_bytes; +} + +grpc_error *grpc_chttp2_hptbl_set_current_table_size(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hptbl *tbl, + uint32_t bytes) { + if (tbl->current_table_bytes == bytes) { + return GRPC_ERROR_NONE; + } + if (bytes > tbl->max_bytes) { + char *msg; + gpr_asprintf(&msg, + "Attempt to make hpack table %d bytes when max is %d bytes", + bytes, tbl->max_bytes); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_DEBUG, "Update hpack parser table size to %d", bytes); + } + while (tbl->mem_used > bytes) { + evict1(exec_ctx, tbl); + } + tbl->current_table_bytes = bytes; + tbl->max_entries = entries_for_bytes(bytes); + if (tbl->max_entries > tbl->cap_entries) { + rebuild_ents(tbl, GPR_MAX(tbl->max_entries, 2 * tbl->cap_entries)); + } else if (tbl->max_entries < tbl->cap_entries / 3) { + uint32_t new_cap = GPR_MAX(tbl->max_entries, 16u); + if (new_cap != tbl->cap_entries) { + rebuild_ents(tbl, new_cap); + } + } + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_chttp2_hptbl_add(grpc_exec_ctx *exec_ctx, + grpc_chttp2_hptbl *tbl, grpc_mdelem md) { + /* determine how many bytes of buffer this entry represents */ + size_t elem_bytes = GRPC_SLICE_LENGTH(GRPC_MDKEY(md)) + + GRPC_SLICE_LENGTH(GRPC_MDVALUE(md)) + + GRPC_CHTTP2_HPACK_ENTRY_OVERHEAD; + + if (tbl->current_table_bytes > tbl->max_bytes) { + char *msg; + gpr_asprintf( + &msg, + "HPACK max table size reduced to %d but not reflected by hpack " + "stream (still at %d)", + tbl->max_bytes, tbl->current_table_bytes); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + + /* we can't add elements bigger than the max table size */ + if (elem_bytes > tbl->current_table_bytes) { + /* HPACK draft 10 section 4.4 states: + * If the size of the new entry is less than or equal to the maximum + * size, that entry is added to the table. It is not an error to + * attempt to add an entry that is larger than the maximum size; an + * attempt to add an entry larger than the entire table causes + * the table + * to be emptied of all existing entries, and results in an + * empty table. + */ + while (tbl->num_ents) { + evict1(exec_ctx, tbl); + } + return GRPC_ERROR_NONE; + } + + /* evict entries to ensure no overflow */ + while (elem_bytes > (size_t)tbl->current_table_bytes - tbl->mem_used) { + evict1(exec_ctx, tbl); + } + + /* copy the finalized entry in */ + tbl->ents[(tbl->first_ent + tbl->num_ents) % tbl->cap_entries] = + GRPC_MDELEM_REF(md); + + /* update accounting values */ + tbl->num_ents++; + tbl->mem_used += (uint32_t)elem_bytes; + return GRPC_ERROR_NONE; +} + +grpc_chttp2_hptbl_find_result grpc_chttp2_hptbl_find( + const grpc_chttp2_hptbl *tbl, grpc_mdelem md) { + grpc_chttp2_hptbl_find_result r = {0, 0}; + uint32_t i; + + /* See if the string is in the static table */ + for (i = 0; i < GRPC_CHTTP2_LAST_STATIC_ENTRY; i++) { + grpc_mdelem ent = tbl->static_ents[i]; + if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue; + r.index = i + 1u; + r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent)); + if (r.has_value) return r; + } + + /* Scan the dynamic table */ + for (i = 0; i < tbl->num_ents; i++) { + uint32_t idx = + (uint32_t)(tbl->num_ents - i + GRPC_CHTTP2_LAST_STATIC_ENTRY); + grpc_mdelem ent = tbl->ents[(tbl->first_ent + i) % tbl->cap_entries]; + if (!grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDKEY(ent))) continue; + r.index = idx; + r.has_value = grpc_slice_eq(GRPC_MDVALUE(md), GRPC_MDVALUE(ent)); + if (r.has_value) return r; + } + + return r; +} diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.c b/src/core/ext/transport/chttp2/transport/http2_settings.c deleted file mode 100644 index 46b7c0c49c..0000000000 --- a/src/core/ext/transport/chttp2/transport/http2_settings.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Automatically generated by tools/codegen/core/gen_settings_ids.py - */ - -#include "src/core/ext/transport/chttp2/transport/http2_settings.h" - -#include -#include "src/core/lib/transport/http2_errors.h" - -const uint16_t grpc_setting_id_to_wire_id[] = {1, 2, 3, 4, 5, 6, 65027}; - -bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id *out) { - uint32_t i = wire_id - 1; - uint32_t x = i % 256; - uint32_t y = i / 256; - uint32_t h = x; - switch (y) { - case 254: - h += 4; - break; - } - *out = (grpc_chttp2_setting_id)h; - return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) && - grpc_setting_id_to_wire_id[h] == wire_id; -} - -const grpc_chttp2_setting_parameters - grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = { - {"HEADER_TABLE_SIZE", 4096u, 0u, 4294967295u, - GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, - {"ENABLE_PUSH", 1u, 0u, 1u, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, - GRPC_HTTP2_PROTOCOL_ERROR}, - {"MAX_CONCURRENT_STREAMS", 4294967295u, 0u, 4294967295u, - GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, - {"INITIAL_WINDOW_SIZE", 65535u, 0u, 2147483647u, - GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, - GRPC_HTTP2_FLOW_CONTROL_ERROR}, - {"MAX_FRAME_SIZE", 16384u, 16384u, 16777215u, - GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, - {"MAX_HEADER_LIST_SIZE", 16777216u, 0u, 16777216u, - GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, - {"GRPC_ALLOW_TRUE_BINARY_METADATA", 0u, 0u, 1u, - GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, -}; diff --git a/src/core/ext/transport/chttp2/transport/http2_settings.cc b/src/core/ext/transport/chttp2/transport/http2_settings.cc new file mode 100644 index 0000000000..46b7c0c49c --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/http2_settings.cc @@ -0,0 +1,60 @@ +/* + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Automatically generated by tools/codegen/core/gen_settings_ids.py + */ + +#include "src/core/ext/transport/chttp2/transport/http2_settings.h" + +#include +#include "src/core/lib/transport/http2_errors.h" + +const uint16_t grpc_setting_id_to_wire_id[] = {1, 2, 3, 4, 5, 6, 65027}; + +bool grpc_wire_id_to_setting_id(uint32_t wire_id, grpc_chttp2_setting_id *out) { + uint32_t i = wire_id - 1; + uint32_t x = i % 256; + uint32_t y = i / 256; + uint32_t h = x; + switch (y) { + case 254: + h += 4; + break; + } + *out = (grpc_chttp2_setting_id)h; + return h < GPR_ARRAY_SIZE(grpc_setting_id_to_wire_id) && + grpc_setting_id_to_wire_id[h] == wire_id; +} + +const grpc_chttp2_setting_parameters + grpc_chttp2_settings_parameters[GRPC_CHTTP2_NUM_SETTINGS] = { + {"HEADER_TABLE_SIZE", 4096u, 0u, 4294967295u, + GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"ENABLE_PUSH", 1u, 0u, 1u, GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, + GRPC_HTTP2_PROTOCOL_ERROR}, + {"MAX_CONCURRENT_STREAMS", 4294967295u, 0u, 4294967295u, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"INITIAL_WINDOW_SIZE", 65535u, 0u, 2147483647u, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, + GRPC_HTTP2_FLOW_CONTROL_ERROR}, + {"MAX_FRAME_SIZE", 16384u, 16384u, 16777215u, + GRPC_CHTTP2_DISCONNECT_ON_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"MAX_HEADER_LIST_SIZE", 16777216u, 0u, 16777216u, + GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, + {"GRPC_ALLOW_TRUE_BINARY_METADATA", 0u, 0u, 1u, + GRPC_CHTTP2_CLAMP_INVALID_VALUE, GRPC_HTTP2_PROTOCOL_ERROR}, +}; diff --git a/src/core/ext/transport/chttp2/transport/huffsyms.c b/src/core/ext/transport/chttp2/transport/huffsyms.c deleted file mode 100644 index f28d8cc30a..0000000000 --- a/src/core/ext/transport/chttp2/transport/huffsyms.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/huffsyms.h" - -/* Constants pulled from the HPACK spec, and converted to C using the vim - command: - :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */ -const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = { - {0x1ff8, 13}, {0x7fffd8, 23}, {0xfffffe2, 28}, {0xfffffe3, 28}, - {0xfffffe4, 28}, {0xfffffe5, 28}, {0xfffffe6, 28}, {0xfffffe7, 28}, - {0xfffffe8, 28}, {0xffffea, 24}, {0x3ffffffc, 30}, {0xfffffe9, 28}, - {0xfffffea, 28}, {0x3ffffffd, 30}, {0xfffffeb, 28}, {0xfffffec, 28}, - {0xfffffed, 28}, {0xfffffee, 28}, {0xfffffef, 28}, {0xffffff0, 28}, - {0xffffff1, 28}, {0xffffff2, 28}, {0x3ffffffe, 30}, {0xffffff3, 28}, - {0xffffff4, 28}, {0xffffff5, 28}, {0xffffff6, 28}, {0xffffff7, 28}, - {0xffffff8, 28}, {0xffffff9, 28}, {0xffffffa, 28}, {0xffffffb, 28}, - {0x14, 6}, {0x3f8, 10}, {0x3f9, 10}, {0xffa, 12}, - {0x1ff9, 13}, {0x15, 6}, {0xf8, 8}, {0x7fa, 11}, - {0x3fa, 10}, {0x3fb, 10}, {0xf9, 8}, {0x7fb, 11}, - {0xfa, 8}, {0x16, 6}, {0x17, 6}, {0x18, 6}, - {0x0, 5}, {0x1, 5}, {0x2, 5}, {0x19, 6}, - {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, - {0x1e, 6}, {0x1f, 6}, {0x5c, 7}, {0xfb, 8}, - {0x7ffc, 15}, {0x20, 6}, {0xffb, 12}, {0x3fc, 10}, - {0x1ffa, 13}, {0x21, 6}, {0x5d, 7}, {0x5e, 7}, - {0x5f, 7}, {0x60, 7}, {0x61, 7}, {0x62, 7}, - {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, - {0x67, 7}, {0x68, 7}, {0x69, 7}, {0x6a, 7}, - {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, {0x6e, 7}, - {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, - {0xfc, 8}, {0x73, 7}, {0xfd, 8}, {0x1ffb, 13}, - {0x7fff0, 19}, {0x1ffc, 13}, {0x3ffc, 14}, {0x22, 6}, - {0x7ffd, 15}, {0x3, 5}, {0x23, 6}, {0x4, 5}, - {0x24, 6}, {0x5, 5}, {0x25, 6}, {0x26, 6}, - {0x27, 6}, {0x6, 5}, {0x74, 7}, {0x75, 7}, - {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, - {0x2b, 6}, {0x76, 7}, {0x2c, 6}, {0x8, 5}, - {0x9, 5}, {0x2d, 6}, {0x77, 7}, {0x78, 7}, - {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x7ffe, 15}, - {0x7fc, 11}, {0x3ffd, 14}, {0x1ffd, 13}, {0xffffffc, 28}, - {0xfffe6, 20}, {0x3fffd2, 22}, {0xfffe7, 20}, {0xfffe8, 20}, - {0x3fffd3, 22}, {0x3fffd4, 22}, {0x3fffd5, 22}, {0x7fffd9, 23}, - {0x3fffd6, 22}, {0x7fffda, 23}, {0x7fffdb, 23}, {0x7fffdc, 23}, - {0x7fffdd, 23}, {0x7fffde, 23}, {0xffffeb, 24}, {0x7fffdf, 23}, - {0xffffec, 24}, {0xffffed, 24}, {0x3fffd7, 22}, {0x7fffe0, 23}, - {0xffffee, 24}, {0x7fffe1, 23}, {0x7fffe2, 23}, {0x7fffe3, 23}, - {0x7fffe4, 23}, {0x1fffdc, 21}, {0x3fffd8, 22}, {0x7fffe5, 23}, - {0x3fffd9, 22}, {0x7fffe6, 23}, {0x7fffe7, 23}, {0xffffef, 24}, - {0x3fffda, 22}, {0x1fffdd, 21}, {0xfffe9, 20}, {0x3fffdb, 22}, - {0x3fffdc, 22}, {0x7fffe8, 23}, {0x7fffe9, 23}, {0x1fffde, 21}, - {0x7fffea, 23}, {0x3fffdd, 22}, {0x3fffde, 22}, {0xfffff0, 24}, - {0x1fffdf, 21}, {0x3fffdf, 22}, {0x7fffeb, 23}, {0x7fffec, 23}, - {0x1fffe0, 21}, {0x1fffe1, 21}, {0x3fffe0, 22}, {0x1fffe2, 21}, - {0x7fffed, 23}, {0x3fffe1, 22}, {0x7fffee, 23}, {0x7fffef, 23}, - {0xfffea, 20}, {0x3fffe2, 22}, {0x3fffe3, 22}, {0x3fffe4, 22}, - {0x7ffff0, 23}, {0x3fffe5, 22}, {0x3fffe6, 22}, {0x7ffff1, 23}, - {0x3ffffe0, 26}, {0x3ffffe1, 26}, {0xfffeb, 20}, {0x7fff1, 19}, - {0x3fffe7, 22}, {0x7ffff2, 23}, {0x3fffe8, 22}, {0x1ffffec, 25}, - {0x3ffffe2, 26}, {0x3ffffe3, 26}, {0x3ffffe4, 26}, {0x7ffffde, 27}, - {0x7ffffdf, 27}, {0x3ffffe5, 26}, {0xfffff1, 24}, {0x1ffffed, 25}, - {0x7fff2, 19}, {0x1fffe3, 21}, {0x3ffffe6, 26}, {0x7ffffe0, 27}, - {0x7ffffe1, 27}, {0x3ffffe7, 26}, {0x7ffffe2, 27}, {0xfffff2, 24}, - {0x1fffe4, 21}, {0x1fffe5, 21}, {0x3ffffe8, 26}, {0x3ffffe9, 26}, - {0xffffffd, 28}, {0x7ffffe3, 27}, {0x7ffffe4, 27}, {0x7ffffe5, 27}, - {0xfffec, 20}, {0xfffff3, 24}, {0xfffed, 20}, {0x1fffe6, 21}, - {0x3fffe9, 22}, {0x1fffe7, 21}, {0x1fffe8, 21}, {0x7ffff3, 23}, - {0x3fffea, 22}, {0x3fffeb, 22}, {0x1ffffee, 25}, {0x1ffffef, 25}, - {0xfffff4, 24}, {0xfffff5, 24}, {0x3ffffea, 26}, {0x7ffff4, 23}, - {0x3ffffeb, 26}, {0x7ffffe6, 27}, {0x3ffffec, 26}, {0x3ffffed, 26}, - {0x7ffffe7, 27}, {0x7ffffe8, 27}, {0x7ffffe9, 27}, {0x7ffffea, 27}, - {0x7ffffeb, 27}, {0xffffffe, 28}, {0x7ffffec, 27}, {0x7ffffed, 27}, - {0x7ffffee, 27}, {0x7ffffef, 27}, {0x7fffff0, 27}, {0x3ffffee, 26}, - {0x3fffffff, 30}, -}; diff --git a/src/core/ext/transport/chttp2/transport/huffsyms.cc b/src/core/ext/transport/chttp2/transport/huffsyms.cc new file mode 100644 index 0000000000..f28d8cc30a --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/huffsyms.cc @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/huffsyms.h" + +/* Constants pulled from the HPACK spec, and converted to C using the vim + command: + :%s/.* \([0-9a-f]\+\) \[ *\([0-9]\+\)\]/{0x\1, \2},/g */ +const grpc_chttp2_huffsym grpc_chttp2_huffsyms[GRPC_CHTTP2_NUM_HUFFSYMS] = { + {0x1ff8, 13}, {0x7fffd8, 23}, {0xfffffe2, 28}, {0xfffffe3, 28}, + {0xfffffe4, 28}, {0xfffffe5, 28}, {0xfffffe6, 28}, {0xfffffe7, 28}, + {0xfffffe8, 28}, {0xffffea, 24}, {0x3ffffffc, 30}, {0xfffffe9, 28}, + {0xfffffea, 28}, {0x3ffffffd, 30}, {0xfffffeb, 28}, {0xfffffec, 28}, + {0xfffffed, 28}, {0xfffffee, 28}, {0xfffffef, 28}, {0xffffff0, 28}, + {0xffffff1, 28}, {0xffffff2, 28}, {0x3ffffffe, 30}, {0xffffff3, 28}, + {0xffffff4, 28}, {0xffffff5, 28}, {0xffffff6, 28}, {0xffffff7, 28}, + {0xffffff8, 28}, {0xffffff9, 28}, {0xffffffa, 28}, {0xffffffb, 28}, + {0x14, 6}, {0x3f8, 10}, {0x3f9, 10}, {0xffa, 12}, + {0x1ff9, 13}, {0x15, 6}, {0xf8, 8}, {0x7fa, 11}, + {0x3fa, 10}, {0x3fb, 10}, {0xf9, 8}, {0x7fb, 11}, + {0xfa, 8}, {0x16, 6}, {0x17, 6}, {0x18, 6}, + {0x0, 5}, {0x1, 5}, {0x2, 5}, {0x19, 6}, + {0x1a, 6}, {0x1b, 6}, {0x1c, 6}, {0x1d, 6}, + {0x1e, 6}, {0x1f, 6}, {0x5c, 7}, {0xfb, 8}, + {0x7ffc, 15}, {0x20, 6}, {0xffb, 12}, {0x3fc, 10}, + {0x1ffa, 13}, {0x21, 6}, {0x5d, 7}, {0x5e, 7}, + {0x5f, 7}, {0x60, 7}, {0x61, 7}, {0x62, 7}, + {0x63, 7}, {0x64, 7}, {0x65, 7}, {0x66, 7}, + {0x67, 7}, {0x68, 7}, {0x69, 7}, {0x6a, 7}, + {0x6b, 7}, {0x6c, 7}, {0x6d, 7}, {0x6e, 7}, + {0x6f, 7}, {0x70, 7}, {0x71, 7}, {0x72, 7}, + {0xfc, 8}, {0x73, 7}, {0xfd, 8}, {0x1ffb, 13}, + {0x7fff0, 19}, {0x1ffc, 13}, {0x3ffc, 14}, {0x22, 6}, + {0x7ffd, 15}, {0x3, 5}, {0x23, 6}, {0x4, 5}, + {0x24, 6}, {0x5, 5}, {0x25, 6}, {0x26, 6}, + {0x27, 6}, {0x6, 5}, {0x74, 7}, {0x75, 7}, + {0x28, 6}, {0x29, 6}, {0x2a, 6}, {0x7, 5}, + {0x2b, 6}, {0x76, 7}, {0x2c, 6}, {0x8, 5}, + {0x9, 5}, {0x2d, 6}, {0x77, 7}, {0x78, 7}, + {0x79, 7}, {0x7a, 7}, {0x7b, 7}, {0x7ffe, 15}, + {0x7fc, 11}, {0x3ffd, 14}, {0x1ffd, 13}, {0xffffffc, 28}, + {0xfffe6, 20}, {0x3fffd2, 22}, {0xfffe7, 20}, {0xfffe8, 20}, + {0x3fffd3, 22}, {0x3fffd4, 22}, {0x3fffd5, 22}, {0x7fffd9, 23}, + {0x3fffd6, 22}, {0x7fffda, 23}, {0x7fffdb, 23}, {0x7fffdc, 23}, + {0x7fffdd, 23}, {0x7fffde, 23}, {0xffffeb, 24}, {0x7fffdf, 23}, + {0xffffec, 24}, {0xffffed, 24}, {0x3fffd7, 22}, {0x7fffe0, 23}, + {0xffffee, 24}, {0x7fffe1, 23}, {0x7fffe2, 23}, {0x7fffe3, 23}, + {0x7fffe4, 23}, {0x1fffdc, 21}, {0x3fffd8, 22}, {0x7fffe5, 23}, + {0x3fffd9, 22}, {0x7fffe6, 23}, {0x7fffe7, 23}, {0xffffef, 24}, + {0x3fffda, 22}, {0x1fffdd, 21}, {0xfffe9, 20}, {0x3fffdb, 22}, + {0x3fffdc, 22}, {0x7fffe8, 23}, {0x7fffe9, 23}, {0x1fffde, 21}, + {0x7fffea, 23}, {0x3fffdd, 22}, {0x3fffde, 22}, {0xfffff0, 24}, + {0x1fffdf, 21}, {0x3fffdf, 22}, {0x7fffeb, 23}, {0x7fffec, 23}, + {0x1fffe0, 21}, {0x1fffe1, 21}, {0x3fffe0, 22}, {0x1fffe2, 21}, + {0x7fffed, 23}, {0x3fffe1, 22}, {0x7fffee, 23}, {0x7fffef, 23}, + {0xfffea, 20}, {0x3fffe2, 22}, {0x3fffe3, 22}, {0x3fffe4, 22}, + {0x7ffff0, 23}, {0x3fffe5, 22}, {0x3fffe6, 22}, {0x7ffff1, 23}, + {0x3ffffe0, 26}, {0x3ffffe1, 26}, {0xfffeb, 20}, {0x7fff1, 19}, + {0x3fffe7, 22}, {0x7ffff2, 23}, {0x3fffe8, 22}, {0x1ffffec, 25}, + {0x3ffffe2, 26}, {0x3ffffe3, 26}, {0x3ffffe4, 26}, {0x7ffffde, 27}, + {0x7ffffdf, 27}, {0x3ffffe5, 26}, {0xfffff1, 24}, {0x1ffffed, 25}, + {0x7fff2, 19}, {0x1fffe3, 21}, {0x3ffffe6, 26}, {0x7ffffe0, 27}, + {0x7ffffe1, 27}, {0x3ffffe7, 26}, {0x7ffffe2, 27}, {0xfffff2, 24}, + {0x1fffe4, 21}, {0x1fffe5, 21}, {0x3ffffe8, 26}, {0x3ffffe9, 26}, + {0xffffffd, 28}, {0x7ffffe3, 27}, {0x7ffffe4, 27}, {0x7ffffe5, 27}, + {0xfffec, 20}, {0xfffff3, 24}, {0xfffed, 20}, {0x1fffe6, 21}, + {0x3fffe9, 22}, {0x1fffe7, 21}, {0x1fffe8, 21}, {0x7ffff3, 23}, + {0x3fffea, 22}, {0x3fffeb, 22}, {0x1ffffee, 25}, {0x1ffffef, 25}, + {0xfffff4, 24}, {0xfffff5, 24}, {0x3ffffea, 26}, {0x7ffff4, 23}, + {0x3ffffeb, 26}, {0x7ffffe6, 27}, {0x3ffffec, 26}, {0x3ffffed, 26}, + {0x7ffffe7, 27}, {0x7ffffe8, 27}, {0x7ffffe9, 27}, {0x7ffffea, 27}, + {0x7ffffeb, 27}, {0xffffffe, 28}, {0x7ffffec, 27}, {0x7ffffed, 27}, + {0x7ffffee, 27}, {0x7ffffef, 27}, {0x7fffff0, 27}, {0x3ffffee, 26}, + {0x3fffffff, 30}, +}; diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.c b/src/core/ext/transport/chttp2/transport/incoming_metadata.c deleted file mode 100644 index ba680a89db..0000000000 --- a/src/core/ext/transport/chttp2/transport/incoming_metadata.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" - -#include - -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include -#include - -void grpc_chttp2_incoming_metadata_buffer_init( - grpc_chttp2_incoming_metadata_buffer *buffer, gpr_arena *arena) { - buffer->arena = arena; - grpc_metadata_batch_init(&buffer->batch); - buffer->batch.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); -} - -void grpc_chttp2_incoming_metadata_buffer_destroy( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer) { - grpc_metadata_batch_destroy(exec_ctx, &buffer->batch); -} - -grpc_error *grpc_chttp2_incoming_metadata_buffer_add( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer, - grpc_mdelem elem) { - buffer->size += GRPC_MDELEM_LENGTH(elem); - return grpc_metadata_batch_add_tail( - exec_ctx, &buffer->batch, (grpc_linked_mdelem *)gpr_arena_alloc( - buffer->arena, sizeof(grpc_linked_mdelem)), - elem); -} - -grpc_error *grpc_chttp2_incoming_metadata_buffer_replace_or_add( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer, - grpc_mdelem elem) { - for (grpc_linked_mdelem *l = buffer->batch.list.head; l != NULL; - l = l->next) { - if (grpc_slice_eq(GRPC_MDKEY(l->md), GRPC_MDKEY(elem))) { - GRPC_MDELEM_UNREF(exec_ctx, l->md); - l->md = elem; - return GRPC_ERROR_NONE; - } - } - return grpc_chttp2_incoming_metadata_buffer_add(exec_ctx, buffer, elem); -} - -void grpc_chttp2_incoming_metadata_buffer_set_deadline( - grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline) { - buffer->batch.deadline = deadline; -} - -void grpc_chttp2_incoming_metadata_buffer_publish( - grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer, - grpc_metadata_batch *batch) { - *batch = buffer->batch; - grpc_metadata_batch_init(&buffer->batch); -} diff --git a/src/core/ext/transport/chttp2/transport/incoming_metadata.cc b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc new file mode 100644 index 0000000000..ba680a89db --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/incoming_metadata.cc @@ -0,0 +1,74 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" + +#include + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include +#include + +void grpc_chttp2_incoming_metadata_buffer_init( + grpc_chttp2_incoming_metadata_buffer *buffer, gpr_arena *arena) { + buffer->arena = arena; + grpc_metadata_batch_init(&buffer->batch); + buffer->batch.deadline = gpr_inf_future(GPR_CLOCK_REALTIME); +} + +void grpc_chttp2_incoming_metadata_buffer_destroy( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer) { + grpc_metadata_batch_destroy(exec_ctx, &buffer->batch); +} + +grpc_error *grpc_chttp2_incoming_metadata_buffer_add( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer, + grpc_mdelem elem) { + buffer->size += GRPC_MDELEM_LENGTH(elem); + return grpc_metadata_batch_add_tail( + exec_ctx, &buffer->batch, (grpc_linked_mdelem *)gpr_arena_alloc( + buffer->arena, sizeof(grpc_linked_mdelem)), + elem); +} + +grpc_error *grpc_chttp2_incoming_metadata_buffer_replace_or_add( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer, + grpc_mdelem elem) { + for (grpc_linked_mdelem *l = buffer->batch.list.head; l != NULL; + l = l->next) { + if (grpc_slice_eq(GRPC_MDKEY(l->md), GRPC_MDKEY(elem))) { + GRPC_MDELEM_UNREF(exec_ctx, l->md); + l->md = elem; + return GRPC_ERROR_NONE; + } + } + return grpc_chttp2_incoming_metadata_buffer_add(exec_ctx, buffer, elem); +} + +void grpc_chttp2_incoming_metadata_buffer_set_deadline( + grpc_chttp2_incoming_metadata_buffer *buffer, gpr_timespec deadline) { + buffer->batch.deadline = deadline; +} + +void grpc_chttp2_incoming_metadata_buffer_publish( + grpc_exec_ctx *exec_ctx, grpc_chttp2_incoming_metadata_buffer *buffer, + grpc_metadata_batch *batch) { + *batch = buffer->batch; + grpc_metadata_batch_init(&buffer->batch); +} diff --git a/src/core/ext/transport/chttp2/transport/parsing.c b/src/core/ext/transport/chttp2/transport/parsing.c deleted file mode 100644 index 3db1ad4123..0000000000 --- a/src/core/ext/transport/chttp2/transport/parsing.c +++ /dev/null @@ -1,766 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include - -#include -#include -#include - -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/transport/http2_errors.h" -#include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/status_conversion.h" -#include "src/core/lib/transport/timeout_encoding.h" - -static grpc_error *init_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_header_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - int is_continuation); -static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_rst_stream_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_settings_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_window_update_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_ping_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_goaway_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t); -static grpc_error *init_skip_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - int is_header); - -static grpc_error *parse_frame_slice(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, grpc_slice slice, - int is_last); - -grpc_error *grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_slice slice) { - uint8_t *beg = GRPC_SLICE_START_PTR(slice); - uint8_t *end = GRPC_SLICE_END_PTR(slice); - uint8_t *cur = beg; - grpc_error *err; - - if (cur == end) return GRPC_ERROR_NONE; - - switch (t->deframe_state) { - case GRPC_DTS_CLIENT_PREFIX_0: - case GRPC_DTS_CLIENT_PREFIX_1: - case GRPC_DTS_CLIENT_PREFIX_2: - case GRPC_DTS_CLIENT_PREFIX_3: - case GRPC_DTS_CLIENT_PREFIX_4: - case GRPC_DTS_CLIENT_PREFIX_5: - case GRPC_DTS_CLIENT_PREFIX_6: - case GRPC_DTS_CLIENT_PREFIX_7: - case GRPC_DTS_CLIENT_PREFIX_8: - case GRPC_DTS_CLIENT_PREFIX_9: - case GRPC_DTS_CLIENT_PREFIX_10: - case GRPC_DTS_CLIENT_PREFIX_11: - case GRPC_DTS_CLIENT_PREFIX_12: - case GRPC_DTS_CLIENT_PREFIX_13: - case GRPC_DTS_CLIENT_PREFIX_14: - case GRPC_DTS_CLIENT_PREFIX_15: - case GRPC_DTS_CLIENT_PREFIX_16: - case GRPC_DTS_CLIENT_PREFIX_17: - case GRPC_DTS_CLIENT_PREFIX_18: - case GRPC_DTS_CLIENT_PREFIX_19: - case GRPC_DTS_CLIENT_PREFIX_20: - case GRPC_DTS_CLIENT_PREFIX_21: - case GRPC_DTS_CLIENT_PREFIX_22: - case GRPC_DTS_CLIENT_PREFIX_23: - while (cur != end && t->deframe_state != GRPC_DTS_FH_0) { - if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state]) { - char *msg; - gpr_asprintf( - &msg, - "Connect string mismatch: expected '%c' (%d) got '%c' (%d) " - "at byte %d", - GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state], - (int)(uint8_t)GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state], - *cur, (int)*cur, t->deframe_state); - err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - ++cur; - t->deframe_state = - (grpc_chttp2_deframe_transport_state)(1 + (int)t->deframe_state); - } - if (cur == end) { - return GRPC_ERROR_NONE; - } - /* fallthrough */ - dts_fh_0: - case GRPC_DTS_FH_0: - GPR_ASSERT(cur < end); - t->incoming_frame_size = ((uint32_t)*cur) << 16; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_1; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_1: - GPR_ASSERT(cur < end); - t->incoming_frame_size |= ((uint32_t)*cur) << 8; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_2; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_2: - GPR_ASSERT(cur < end); - t->incoming_frame_size |= *cur; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_3; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_3: - GPR_ASSERT(cur < end); - t->incoming_frame_type = *cur; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_4; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_4: - GPR_ASSERT(cur < end); - t->incoming_frame_flags = *cur; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_5; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_5: - GPR_ASSERT(cur < end); - t->incoming_stream_id = (((uint32_t)*cur) & 0x7f) << 24; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_6; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_6: - GPR_ASSERT(cur < end); - t->incoming_stream_id |= ((uint32_t)*cur) << 16; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_7; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_7: - GPR_ASSERT(cur < end); - t->incoming_stream_id |= ((uint32_t)*cur) << 8; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_8; - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FH_8: - GPR_ASSERT(cur < end); - t->incoming_stream_id |= ((uint32_t)*cur); - t->deframe_state = GRPC_DTS_FRAME; - err = init_frame_parser(exec_ctx, t); - if (err != GRPC_ERROR_NONE) { - return err; - } - if (t->incoming_frame_size == 0) { - err = parse_frame_slice(exec_ctx, t, grpc_empty_slice(), 1); - if (err != GRPC_ERROR_NONE) { - return err; - } - t->incoming_stream = NULL; - if (++cur == end) { - t->deframe_state = GRPC_DTS_FH_0; - return GRPC_ERROR_NONE; - } - goto dts_fh_0; /* loop */ - } else if (t->incoming_frame_size > - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]) { - char *msg; - gpr_asprintf(&msg, "Frame size %d is larger than max frame size %d", - t->incoming_frame_size, - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]); - err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - if (++cur == end) { - return GRPC_ERROR_NONE; - } - /* fallthrough */ - case GRPC_DTS_FRAME: - GPR_ASSERT(cur < end); - if ((uint32_t)(end - cur) == t->incoming_frame_size) { - err = parse_frame_slice( - exec_ctx, t, grpc_slice_sub_no_ref(slice, (size_t)(cur - beg), - (size_t)(end - beg)), - 1); - if (err != GRPC_ERROR_NONE) { - return err; - } - t->deframe_state = GRPC_DTS_FH_0; - t->incoming_stream = NULL; - return GRPC_ERROR_NONE; - } else if ((uint32_t)(end - cur) > t->incoming_frame_size) { - size_t cur_offset = (size_t)(cur - beg); - err = parse_frame_slice( - exec_ctx, t, - grpc_slice_sub_no_ref(slice, cur_offset, - cur_offset + t->incoming_frame_size), - 1); - if (err != GRPC_ERROR_NONE) { - return err; - } - cur += t->incoming_frame_size; - t->incoming_stream = NULL; - goto dts_fh_0; /* loop */ - } else { - err = parse_frame_slice( - exec_ctx, t, grpc_slice_sub_no_ref(slice, (size_t)(cur - beg), - (size_t)(end - beg)), - 0); - if (err != GRPC_ERROR_NONE) { - return err; - } - t->incoming_frame_size -= (uint32_t)(end - cur); - return GRPC_ERROR_NONE; - } - GPR_UNREACHABLE_CODE(return 0); - } - - GPR_UNREACHABLE_CODE(return 0); -} - -static grpc_error *init_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (t->is_first_frame && - t->incoming_frame_type != GRPC_CHTTP2_FRAME_SETTINGS) { - char *msg; - gpr_asprintf( - &msg, "Expected SETTINGS frame as the first frame, got frame type %d", - t->incoming_frame_type); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - t->is_first_frame = false; - if (t->expect_continuation_stream_id != 0) { - if (t->incoming_frame_type != GRPC_CHTTP2_FRAME_CONTINUATION) { - char *msg; - gpr_asprintf(&msg, "Expected CONTINUATION frame, got frame type %02x", - t->incoming_frame_type); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - if (t->expect_continuation_stream_id != t->incoming_stream_id) { - char *msg; - gpr_asprintf( - &msg, - "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got " - "grpc_chttp2_stream %08x", - t->expect_continuation_stream_id, t->incoming_stream_id); - grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return err; - } - return init_header_frame_parser(exec_ctx, t, 1); - } - switch (t->incoming_frame_type) { - case GRPC_CHTTP2_FRAME_DATA: - return init_data_frame_parser(exec_ctx, t); - case GRPC_CHTTP2_FRAME_HEADER: - return init_header_frame_parser(exec_ctx, t, 0); - case GRPC_CHTTP2_FRAME_CONTINUATION: - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Unexpected CONTINUATION frame"); - case GRPC_CHTTP2_FRAME_RST_STREAM: - return init_rst_stream_parser(exec_ctx, t); - case GRPC_CHTTP2_FRAME_SETTINGS: - return init_settings_frame_parser(exec_ctx, t); - case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: - return init_window_update_frame_parser(exec_ctx, t); - case GRPC_CHTTP2_FRAME_PING: - return init_ping_parser(exec_ctx, t); - case GRPC_CHTTP2_FRAME_GOAWAY: - return init_goaway_parser(exec_ctx, t); - default: - if (GRPC_TRACER_ON(grpc_http_trace)) { - gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type); - } - return init_skip_frame_parser(exec_ctx, t, 0); - } -} - -static grpc_error *skip_parser(grpc_exec_ctx *exec_ctx, void *parser, - grpc_chttp2_transport *t, grpc_chttp2_stream *s, - grpc_slice slice, int is_last) { - return GRPC_ERROR_NONE; -} - -static void skip_header(grpc_exec_ctx *exec_ctx, void *tp, grpc_mdelem md) { - GRPC_MDELEM_UNREF(exec_ctx, md); -} - -static grpc_error *init_skip_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - int is_header) { - if (is_header) { - uint8_t is_eoh = t->expect_continuation_stream_id != 0; - t->parser = grpc_chttp2_header_parser_parse; - t->parser_data = &t->hpack_parser; - t->hpack_parser.on_header = skip_header; - t->hpack_parser.on_header_user_data = NULL; - t->hpack_parser.is_boundary = is_eoh; - t->hpack_parser.is_eof = (uint8_t)(is_eoh ? t->header_eof : 0); - } else { - t->parser = skip_parser; - } - return GRPC_ERROR_NONE; -} - -void grpc_chttp2_parsing_become_skip_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - init_skip_frame_parser(exec_ctx, t, - t->parser == grpc_chttp2_header_parser_parse); -} - -static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_chttp2_stream *s = - grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); - grpc_error *err = GRPC_ERROR_NONE; - err = grpc_chttp2_flowctl_recv_data(&t->flow_control, - s == NULL ? NULL : &s->flow_control, - t->incoming_frame_size); - grpc_chttp2_act_on_flowctl_action( - exec_ctx, grpc_chttp2_flowctl_get_action( - &t->flow_control, s == NULL ? NULL : &s->flow_control), - t, s); - if (err != GRPC_ERROR_NONE) { - goto error_handler; - } - if (s == NULL) { - return init_skip_frame_parser(exec_ctx, t, 0); - } - s->received_bytes += t->incoming_frame_size; - s->stats.incoming.framing_bytes += 9; - if (err == GRPC_ERROR_NONE && s->read_closed) { - return init_skip_frame_parser(exec_ctx, t, 0); - } - if (err == GRPC_ERROR_NONE) { - err = grpc_chttp2_data_parser_begin_frame( - &s->data_parser, t->incoming_frame_flags, s->id, s); - } -error_handler: - if (err == GRPC_ERROR_NONE) { - t->incoming_stream = s; - /* t->parser = grpc_chttp2_data_parser_parse;*/ - t->parser = grpc_chttp2_data_parser_parse; - t->parser_data = &s->data_parser; - t->ping_state.pings_before_data_required = - t->ping_policy.max_pings_without_data; - t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); - return GRPC_ERROR_NONE; - } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { - /* handle stream errors by closing the stream */ - if (s != NULL) { - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, err); - } - grpc_slice_buffer_add( - &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, - GRPC_HTTP2_PROTOCOL_ERROR, - &s->stats.outgoing)); - return init_skip_frame_parser(exec_ctx, t, 0); - } else { - return err; - } -} - -static void free_timeout(void *p) { gpr_free(p); } - -static void on_initial_header(grpc_exec_ctx *exec_ctx, void *tp, - grpc_mdelem md) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - grpc_chttp2_stream *s = t->incoming_stream; - - GPR_TIMER_BEGIN("on_initial_header", 0); - - GPR_ASSERT(s != NULL); - - if (GRPC_TRACER_ON(grpc_http_trace)) { - char *key = grpc_slice_to_c_string(GRPC_MDKEY(md)); - char *value = - grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", s->id, - t->is_client ? "CLI" : "SVR", key, value); - gpr_free(key); - gpr_free(value); - } - - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && - !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { - /* TODO(ctiller): check for a status like " 0" */ - s->seen_error = true; - } - - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { - gpr_timespec *cached_timeout = - (gpr_timespec *)grpc_mdelem_get_user_data(md, free_timeout); - gpr_timespec timeout; - if (cached_timeout == NULL) { - /* not already parsed: parse it now, and store the result away */ - cached_timeout = (gpr_timespec *)gpr_malloc(sizeof(gpr_timespec)); - if (!grpc_http2_decode_timeout(GRPC_MDVALUE(md), cached_timeout)) { - char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); - gpr_free(val); - *cached_timeout = gpr_inf_future(GPR_TIMESPAN); - } - timeout = *cached_timeout; - grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); - } else { - timeout = *cached_timeout; - } - grpc_chttp2_incoming_metadata_buffer_set_deadline( - &s->metadata_buffer[0], - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), timeout)); - GRPC_MDELEM_UNREF(exec_ctx, md); - } else { - const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md); - const size_t metadata_size_limit = - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; - if (new_size > metadata_size_limit) { - gpr_log(GPR_DEBUG, - "received initial metadata size exceeds limit (%" PRIuPTR - " vs. %" PRIuPTR ")", - new_size, metadata_size_limit); - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "received initial metadata size exceeds limit"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); - grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); - s->seen_error = true; - GRPC_MDELEM_UNREF(exec_ctx, md); - } else { - grpc_error *error = grpc_chttp2_incoming_metadata_buffer_add( - exec_ctx, &s->metadata_buffer[0], md); - if (error != GRPC_ERROR_NONE) { - grpc_chttp2_cancel_stream(exec_ctx, t, s, error); - grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); - s->seen_error = true; - GRPC_MDELEM_UNREF(exec_ctx, md); - } - } - } - - GPR_TIMER_END("on_initial_header", 0); -} - -static void on_trailing_header(grpc_exec_ctx *exec_ctx, void *tp, - grpc_mdelem md) { - grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; - grpc_chttp2_stream *s = t->incoming_stream; - - GPR_TIMER_BEGIN("on_trailing_header", 0); - - GPR_ASSERT(s != NULL); - - if (GRPC_TRACER_ON(grpc_http_trace)) { - char *key = grpc_slice_to_c_string(GRPC_MDKEY(md)); - char *value = - grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_INFO, "HTTP:%d:TRL:%s: %s: %s", s->id, - t->is_client ? "CLI" : "SVR", key, value); - gpr_free(key); - gpr_free(value); - } - - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && - !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { - /* TODO(ctiller): check for a status like " 0" */ - s->seen_error = true; - } - - const size_t new_size = s->metadata_buffer[1].size + GRPC_MDELEM_LENGTH(md); - const size_t metadata_size_limit = - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; - if (new_size > metadata_size_limit) { - gpr_log(GPR_DEBUG, - "received trailing metadata size exceeds limit (%" PRIuPTR - " vs. %" PRIuPTR ")", - new_size, metadata_size_limit); - grpc_chttp2_cancel_stream( - exec_ctx, t, s, - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "received trailing metadata size exceeds limit"), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_RESOURCE_EXHAUSTED)); - grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); - s->seen_error = true; - GRPC_MDELEM_UNREF(exec_ctx, md); - } else { - grpc_error *error = grpc_chttp2_incoming_metadata_buffer_add( - exec_ctx, &s->metadata_buffer[1], md); - if (error != GRPC_ERROR_NONE) { - grpc_chttp2_cancel_stream(exec_ctx, t, s, error); - grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); - s->seen_error = true; - GRPC_MDELEM_UNREF(exec_ctx, md); - } - } - - GPR_TIMER_END("on_trailing_header", 0); -} - -static grpc_error *init_header_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - int is_continuation) { - uint8_t is_eoh = - (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; - grpc_chttp2_stream *s; - - /* TODO(ctiller): when to increment header_frames_received? */ - - if (is_eoh) { - t->expect_continuation_stream_id = 0; - } else { - t->expect_continuation_stream_id = t->incoming_stream_id; - } - - if (!is_continuation) { - t->header_eof = - (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) != 0; - } - - t->ping_state.pings_before_data_required = - t->ping_policy.max_pings_without_data; - t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); - - /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */ - s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); - if (s == NULL) { - if (is_continuation) { - GRPC_CHTTP2_IF_TRACING( - gpr_log(GPR_ERROR, - "grpc_chttp2_stream disbanded before CONTINUATION received")); - return init_skip_frame_parser(exec_ctx, t, 1); - } - if (t->is_client) { - if ((t->incoming_stream_id & 1) && - t->incoming_stream_id < t->next_stream_id) { - /* this is an old (probably cancelled) grpc_chttp2_stream */ - } else { - GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client")); - } - return init_skip_frame_parser(exec_ctx, t, 1); - } else if (t->last_new_stream_id >= t->incoming_stream_id) { - GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_ERROR, - "ignoring out of order new grpc_chttp2_stream request on server; " - "last grpc_chttp2_stream " - "id=%d, new grpc_chttp2_stream id=%d", - t->last_new_stream_id, t->incoming_stream_id)); - return init_skip_frame_parser(exec_ctx, t, 1); - } else if ((t->incoming_stream_id & 1) == 0) { - GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_ERROR, - "ignoring grpc_chttp2_stream with non-client generated index %d", - t->incoming_stream_id)); - return init_skip_frame_parser(exec_ctx, t, 1); - } else if (grpc_chttp2_stream_map_size(&t->stream_map) >= - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Max stream count exceeded"); - } - t->last_new_stream_id = t->incoming_stream_id; - s = t->incoming_stream = - grpc_chttp2_parsing_accept_stream(exec_ctx, t, t->incoming_stream_id); - if (s == NULL) { - GRPC_CHTTP2_IF_TRACING( - gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted")); - return init_skip_frame_parser(exec_ctx, t, 1); - } - } else { - t->incoming_stream = s; - } - GPR_ASSERT(s != NULL); - s->stats.incoming.framing_bytes += 9; - if (s->read_closed) { - GRPC_CHTTP2_IF_TRACING(gpr_log( - GPR_ERROR, "skipping already closed grpc_chttp2_stream header")); - t->incoming_stream = NULL; - return init_skip_frame_parser(exec_ctx, t, 1); - } - t->parser = grpc_chttp2_header_parser_parse; - t->parser_data = &t->hpack_parser; - switch (s->header_frames_received) { - case 0: - if (t->is_client && t->header_eof) { - GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing Trailers-Only")); - if (s->trailing_metadata_available != NULL) { - *s->trailing_metadata_available = true; - } - t->hpack_parser.on_header = on_trailing_header; - s->received_trailing_metadata = true; - } else { - GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing initial_metadata")); - t->hpack_parser.on_header = on_initial_header; - } - break; - case 1: - GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing trailing_metadata")); - t->hpack_parser.on_header = on_trailing_header; - s->received_trailing_metadata = true; - break; - case 2: - gpr_log(GPR_ERROR, "too many header frames received"); - return init_skip_frame_parser(exec_ctx, t, 1); - } - t->hpack_parser.on_header_user_data = t; - t->hpack_parser.is_boundary = is_eoh; - t->hpack_parser.is_eof = (uint8_t)(is_eoh ? t->header_eof : 0); - if (!is_continuation && - (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY)) { - grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser); - } - return GRPC_ERROR_NONE; -} - -static grpc_error *init_window_update_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_error *err = grpc_chttp2_window_update_parser_begin_frame( - &t->simple.window_update, t->incoming_frame_size, - t->incoming_frame_flags); - if (err != GRPC_ERROR_NONE) return err; - if (t->incoming_stream_id != 0) { - grpc_chttp2_stream *s = t->incoming_stream = - grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); - if (s == NULL) { - return init_skip_frame_parser(exec_ctx, t, 0); - } - s->stats.incoming.framing_bytes += 9; - } - t->parser = grpc_chttp2_window_update_parser_parse; - t->parser_data = &t->simple.window_update; - return GRPC_ERROR_NONE; -} - -static grpc_error *init_ping_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_error *err = grpc_chttp2_ping_parser_begin_frame( - &t->simple.ping, t->incoming_frame_size, t->incoming_frame_flags); - if (err != GRPC_ERROR_NONE) return err; - t->parser = grpc_chttp2_ping_parser_parse; - t->parser_data = &t->simple.ping; - return GRPC_ERROR_NONE; -} - -static grpc_error *init_rst_stream_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_error *err = grpc_chttp2_rst_stream_parser_begin_frame( - &t->simple.rst_stream, t->incoming_frame_size, t->incoming_frame_flags); - if (err != GRPC_ERROR_NONE) return err; - grpc_chttp2_stream *s = t->incoming_stream = - grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); - if (!t->incoming_stream) { - return init_skip_frame_parser(exec_ctx, t, 0); - } - s->stats.incoming.framing_bytes += 9; - t->parser = grpc_chttp2_rst_stream_parser_parse; - t->parser_data = &t->simple.rst_stream; - return GRPC_ERROR_NONE; -} - -static grpc_error *init_goaway_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - grpc_error *err = grpc_chttp2_goaway_parser_begin_frame( - &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags); - if (err != GRPC_ERROR_NONE) return err; - t->parser = grpc_chttp2_goaway_parser_parse; - t->parser_data = &t->goaway_parser; - return GRPC_ERROR_NONE; -} - -static grpc_error *init_settings_frame_parser(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t) { - if (t->incoming_stream_id != 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Settings frame received for grpc_chttp2_stream"); - } - - grpc_error *err = grpc_chttp2_settings_parser_begin_frame( - &t->simple.settings, t->incoming_frame_size, t->incoming_frame_flags, - t->settings[GRPC_PEER_SETTINGS]); - if (err != GRPC_ERROR_NONE) { - return err; - } - if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) { - memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS], - GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); - grpc_chttp2_hptbl_set_max_bytes( - exec_ctx, &t->hpack_parser.table, - t->settings[GRPC_ACKED_SETTINGS] - [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); - t->sent_local_settings = 0; - } - t->parser = grpc_chttp2_settings_parser_parse; - t->parser_data = &t->simple.settings; - return GRPC_ERROR_NONE; -} - -static grpc_error *parse_frame_slice(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, grpc_slice slice, - int is_last) { - grpc_chttp2_stream *s = t->incoming_stream; - grpc_error *err = t->parser(exec_ctx, t->parser_data, t, s, slice, is_last); - if (err == GRPC_ERROR_NONE) { - return err; - } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { - if (GRPC_TRACER_ON(grpc_http_trace)) { - const char *msg = grpc_error_string(err); - gpr_log(GPR_ERROR, "%s", msg); - } - grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); - if (s) { - s->forced_close_error = err; - grpc_slice_buffer_add( - &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, - GRPC_HTTP2_PROTOCOL_ERROR, - &s->stats.outgoing)); - } else { - GRPC_ERROR_UNREF(err); - } - } - return err; -} diff --git a/src/core/ext/transport/chttp2/transport/parsing.cc b/src/core/ext/transport/chttp2/transport/parsing.cc new file mode 100644 index 0000000000..3db1ad4123 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/parsing.cc @@ -0,0 +1,766 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include +#include +#include + +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/transport/http2_errors.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/status_conversion.h" +#include "src/core/lib/transport/timeout_encoding.h" + +static grpc_error *init_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_header_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + int is_continuation); +static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_rst_stream_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_settings_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_window_update_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_ping_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_goaway_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t); +static grpc_error *init_skip_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + int is_header); + +static grpc_error *parse_frame_slice(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_slice slice, + int is_last); + +grpc_error *grpc_chttp2_perform_read(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_slice slice) { + uint8_t *beg = GRPC_SLICE_START_PTR(slice); + uint8_t *end = GRPC_SLICE_END_PTR(slice); + uint8_t *cur = beg; + grpc_error *err; + + if (cur == end) return GRPC_ERROR_NONE; + + switch (t->deframe_state) { + case GRPC_DTS_CLIENT_PREFIX_0: + case GRPC_DTS_CLIENT_PREFIX_1: + case GRPC_DTS_CLIENT_PREFIX_2: + case GRPC_DTS_CLIENT_PREFIX_3: + case GRPC_DTS_CLIENT_PREFIX_4: + case GRPC_DTS_CLIENT_PREFIX_5: + case GRPC_DTS_CLIENT_PREFIX_6: + case GRPC_DTS_CLIENT_PREFIX_7: + case GRPC_DTS_CLIENT_PREFIX_8: + case GRPC_DTS_CLIENT_PREFIX_9: + case GRPC_DTS_CLIENT_PREFIX_10: + case GRPC_DTS_CLIENT_PREFIX_11: + case GRPC_DTS_CLIENT_PREFIX_12: + case GRPC_DTS_CLIENT_PREFIX_13: + case GRPC_DTS_CLIENT_PREFIX_14: + case GRPC_DTS_CLIENT_PREFIX_15: + case GRPC_DTS_CLIENT_PREFIX_16: + case GRPC_DTS_CLIENT_PREFIX_17: + case GRPC_DTS_CLIENT_PREFIX_18: + case GRPC_DTS_CLIENT_PREFIX_19: + case GRPC_DTS_CLIENT_PREFIX_20: + case GRPC_DTS_CLIENT_PREFIX_21: + case GRPC_DTS_CLIENT_PREFIX_22: + case GRPC_DTS_CLIENT_PREFIX_23: + while (cur != end && t->deframe_state != GRPC_DTS_FH_0) { + if (*cur != GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state]) { + char *msg; + gpr_asprintf( + &msg, + "Connect string mismatch: expected '%c' (%d) got '%c' (%d) " + "at byte %d", + GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state], + (int)(uint8_t)GRPC_CHTTP2_CLIENT_CONNECT_STRING[t->deframe_state], + *cur, (int)*cur, t->deframe_state); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + ++cur; + t->deframe_state = + (grpc_chttp2_deframe_transport_state)(1 + (int)t->deframe_state); + } + if (cur == end) { + return GRPC_ERROR_NONE; + } + /* fallthrough */ + dts_fh_0: + case GRPC_DTS_FH_0: + GPR_ASSERT(cur < end); + t->incoming_frame_size = ((uint32_t)*cur) << 16; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_1; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_1: + GPR_ASSERT(cur < end); + t->incoming_frame_size |= ((uint32_t)*cur) << 8; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_2; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_2: + GPR_ASSERT(cur < end); + t->incoming_frame_size |= *cur; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_3; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_3: + GPR_ASSERT(cur < end); + t->incoming_frame_type = *cur; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_4; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_4: + GPR_ASSERT(cur < end); + t->incoming_frame_flags = *cur; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_5; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_5: + GPR_ASSERT(cur < end); + t->incoming_stream_id = (((uint32_t)*cur) & 0x7f) << 24; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_6; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_6: + GPR_ASSERT(cur < end); + t->incoming_stream_id |= ((uint32_t)*cur) << 16; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_7; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_7: + GPR_ASSERT(cur < end); + t->incoming_stream_id |= ((uint32_t)*cur) << 8; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_8; + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FH_8: + GPR_ASSERT(cur < end); + t->incoming_stream_id |= ((uint32_t)*cur); + t->deframe_state = GRPC_DTS_FRAME; + err = init_frame_parser(exec_ctx, t); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (t->incoming_frame_size == 0) { + err = parse_frame_slice(exec_ctx, t, grpc_empty_slice(), 1); + if (err != GRPC_ERROR_NONE) { + return err; + } + t->incoming_stream = NULL; + if (++cur == end) { + t->deframe_state = GRPC_DTS_FH_0; + return GRPC_ERROR_NONE; + } + goto dts_fh_0; /* loop */ + } else if (t->incoming_frame_size > + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]) { + char *msg; + gpr_asprintf(&msg, "Frame size %d is larger than max frame size %d", + t->incoming_frame_size, + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE]); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + if (++cur == end) { + return GRPC_ERROR_NONE; + } + /* fallthrough */ + case GRPC_DTS_FRAME: + GPR_ASSERT(cur < end); + if ((uint32_t)(end - cur) == t->incoming_frame_size) { + err = parse_frame_slice( + exec_ctx, t, grpc_slice_sub_no_ref(slice, (size_t)(cur - beg), + (size_t)(end - beg)), + 1); + if (err != GRPC_ERROR_NONE) { + return err; + } + t->deframe_state = GRPC_DTS_FH_0; + t->incoming_stream = NULL; + return GRPC_ERROR_NONE; + } else if ((uint32_t)(end - cur) > t->incoming_frame_size) { + size_t cur_offset = (size_t)(cur - beg); + err = parse_frame_slice( + exec_ctx, t, + grpc_slice_sub_no_ref(slice, cur_offset, + cur_offset + t->incoming_frame_size), + 1); + if (err != GRPC_ERROR_NONE) { + return err; + } + cur += t->incoming_frame_size; + t->incoming_stream = NULL; + goto dts_fh_0; /* loop */ + } else { + err = parse_frame_slice( + exec_ctx, t, grpc_slice_sub_no_ref(slice, (size_t)(cur - beg), + (size_t)(end - beg)), + 0); + if (err != GRPC_ERROR_NONE) { + return err; + } + t->incoming_frame_size -= (uint32_t)(end - cur); + return GRPC_ERROR_NONE; + } + GPR_UNREACHABLE_CODE(return 0); + } + + GPR_UNREACHABLE_CODE(return 0); +} + +static grpc_error *init_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (t->is_first_frame && + t->incoming_frame_type != GRPC_CHTTP2_FRAME_SETTINGS) { + char *msg; + gpr_asprintf( + &msg, "Expected SETTINGS frame as the first frame, got frame type %d", + t->incoming_frame_type); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + t->is_first_frame = false; + if (t->expect_continuation_stream_id != 0) { + if (t->incoming_frame_type != GRPC_CHTTP2_FRAME_CONTINUATION) { + char *msg; + gpr_asprintf(&msg, "Expected CONTINUATION frame, got frame type %02x", + t->incoming_frame_type); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + if (t->expect_continuation_stream_id != t->incoming_stream_id) { + char *msg; + gpr_asprintf( + &msg, + "Expected CONTINUATION frame for grpc_chttp2_stream %08x, got " + "grpc_chttp2_stream %08x", + t->expect_continuation_stream_id, t->incoming_stream_id); + grpc_error *err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return err; + } + return init_header_frame_parser(exec_ctx, t, 1); + } + switch (t->incoming_frame_type) { + case GRPC_CHTTP2_FRAME_DATA: + return init_data_frame_parser(exec_ctx, t); + case GRPC_CHTTP2_FRAME_HEADER: + return init_header_frame_parser(exec_ctx, t, 0); + case GRPC_CHTTP2_FRAME_CONTINUATION: + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unexpected CONTINUATION frame"); + case GRPC_CHTTP2_FRAME_RST_STREAM: + return init_rst_stream_parser(exec_ctx, t); + case GRPC_CHTTP2_FRAME_SETTINGS: + return init_settings_frame_parser(exec_ctx, t); + case GRPC_CHTTP2_FRAME_WINDOW_UPDATE: + return init_window_update_frame_parser(exec_ctx, t); + case GRPC_CHTTP2_FRAME_PING: + return init_ping_parser(exec_ctx, t); + case GRPC_CHTTP2_FRAME_GOAWAY: + return init_goaway_parser(exec_ctx, t); + default: + if (GRPC_TRACER_ON(grpc_http_trace)) { + gpr_log(GPR_ERROR, "Unknown frame type %02x", t->incoming_frame_type); + } + return init_skip_frame_parser(exec_ctx, t, 0); + } +} + +static grpc_error *skip_parser(grpc_exec_ctx *exec_ctx, void *parser, + grpc_chttp2_transport *t, grpc_chttp2_stream *s, + grpc_slice slice, int is_last) { + return GRPC_ERROR_NONE; +} + +static void skip_header(grpc_exec_ctx *exec_ctx, void *tp, grpc_mdelem md) { + GRPC_MDELEM_UNREF(exec_ctx, md); +} + +static grpc_error *init_skip_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + int is_header) { + if (is_header) { + uint8_t is_eoh = t->expect_continuation_stream_id != 0; + t->parser = grpc_chttp2_header_parser_parse; + t->parser_data = &t->hpack_parser; + t->hpack_parser.on_header = skip_header; + t->hpack_parser.on_header_user_data = NULL; + t->hpack_parser.is_boundary = is_eoh; + t->hpack_parser.is_eof = (uint8_t)(is_eoh ? t->header_eof : 0); + } else { + t->parser = skip_parser; + } + return GRPC_ERROR_NONE; +} + +void grpc_chttp2_parsing_become_skip_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + init_skip_frame_parser(exec_ctx, t, + t->parser == grpc_chttp2_header_parser_parse); +} + +static grpc_error *init_data_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_chttp2_stream *s = + grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + grpc_error *err = GRPC_ERROR_NONE; + err = grpc_chttp2_flowctl_recv_data(&t->flow_control, + s == NULL ? NULL : &s->flow_control, + t->incoming_frame_size); + grpc_chttp2_act_on_flowctl_action( + exec_ctx, grpc_chttp2_flowctl_get_action( + &t->flow_control, s == NULL ? NULL : &s->flow_control), + t, s); + if (err != GRPC_ERROR_NONE) { + goto error_handler; + } + if (s == NULL) { + return init_skip_frame_parser(exec_ctx, t, 0); + } + s->received_bytes += t->incoming_frame_size; + s->stats.incoming.framing_bytes += 9; + if (err == GRPC_ERROR_NONE && s->read_closed) { + return init_skip_frame_parser(exec_ctx, t, 0); + } + if (err == GRPC_ERROR_NONE) { + err = grpc_chttp2_data_parser_begin_frame( + &s->data_parser, t->incoming_frame_flags, s->id, s); + } +error_handler: + if (err == GRPC_ERROR_NONE) { + t->incoming_stream = s; + /* t->parser = grpc_chttp2_data_parser_parse;*/ + t->parser = grpc_chttp2_data_parser_parse; + t->parser_data = &s->data_parser; + t->ping_state.pings_before_data_required = + t->ping_policy.max_pings_without_data; + t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); + return GRPC_ERROR_NONE; + } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { + /* handle stream errors by closing the stream */ + if (s != NULL) { + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, true, false, err); + } + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, + GRPC_HTTP2_PROTOCOL_ERROR, + &s->stats.outgoing)); + return init_skip_frame_parser(exec_ctx, t, 0); + } else { + return err; + } +} + +static void free_timeout(void *p) { gpr_free(p); } + +static void on_initial_header(grpc_exec_ctx *exec_ctx, void *tp, + grpc_mdelem md) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + grpc_chttp2_stream *s = t->incoming_stream; + + GPR_TIMER_BEGIN("on_initial_header", 0); + + GPR_ASSERT(s != NULL); + + if (GRPC_TRACER_ON(grpc_http_trace)) { + char *key = grpc_slice_to_c_string(GRPC_MDKEY(md)); + char *value = + grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "HTTP:%d:HDR:%s: %s: %s", s->id, + t->is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } + + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + /* TODO(ctiller): check for a status like " 0" */ + s->seen_error = true; + } + + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_TIMEOUT)) { + gpr_timespec *cached_timeout = + (gpr_timespec *)grpc_mdelem_get_user_data(md, free_timeout); + gpr_timespec timeout; + if (cached_timeout == NULL) { + /* not already parsed: parse it now, and store the result away */ + cached_timeout = (gpr_timespec *)gpr_malloc(sizeof(gpr_timespec)); + if (!grpc_http2_decode_timeout(GRPC_MDVALUE(md), cached_timeout)) { + char *val = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, "Ignoring bad timeout value '%s'", val); + gpr_free(val); + *cached_timeout = gpr_inf_future(GPR_TIMESPAN); + } + timeout = *cached_timeout; + grpc_mdelem_set_user_data(md, free_timeout, cached_timeout); + } else { + timeout = *cached_timeout; + } + grpc_chttp2_incoming_metadata_buffer_set_deadline( + &s->metadata_buffer[0], + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), timeout)); + GRPC_MDELEM_UNREF(exec_ctx, md); + } else { + const size_t new_size = s->metadata_buffer[0].size + GRPC_MDELEM_LENGTH(md); + const size_t metadata_size_limit = + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (new_size > metadata_size_limit) { + gpr_log(GPR_DEBUG, + "received initial metadata size exceeds limit (%" PRIuPTR + " vs. %" PRIuPTR ")", + new_size, metadata_size_limit); + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "received initial metadata size exceeds limit"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_RESOURCE_EXHAUSTED)); + grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); + s->seen_error = true; + GRPC_MDELEM_UNREF(exec_ctx, md); + } else { + grpc_error *error = grpc_chttp2_incoming_metadata_buffer_add( + exec_ctx, &s->metadata_buffer[0], md); + if (error != GRPC_ERROR_NONE) { + grpc_chttp2_cancel_stream(exec_ctx, t, s, error); + grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); + s->seen_error = true; + GRPC_MDELEM_UNREF(exec_ctx, md); + } + } + } + + GPR_TIMER_END("on_initial_header", 0); +} + +static void on_trailing_header(grpc_exec_ctx *exec_ctx, void *tp, + grpc_mdelem md) { + grpc_chttp2_transport *t = (grpc_chttp2_transport *)tp; + grpc_chttp2_stream *s = t->incoming_stream; + + GPR_TIMER_BEGIN("on_trailing_header", 0); + + GPR_ASSERT(s != NULL); + + if (GRPC_TRACER_ON(grpc_http_trace)) { + char *key = grpc_slice_to_c_string(GRPC_MDKEY(md)); + char *value = + grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_INFO, "HTTP:%d:TRL:%s: %s: %s", s->id, + t->is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } + + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_GRPC_STATUS) && + !grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) { + /* TODO(ctiller): check for a status like " 0" */ + s->seen_error = true; + } + + const size_t new_size = s->metadata_buffer[1].size + GRPC_MDELEM_LENGTH(md); + const size_t metadata_size_limit = + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE]; + if (new_size > metadata_size_limit) { + gpr_log(GPR_DEBUG, + "received trailing metadata size exceeds limit (%" PRIuPTR + " vs. %" PRIuPTR ")", + new_size, metadata_size_limit); + grpc_chttp2_cancel_stream( + exec_ctx, t, s, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "received trailing metadata size exceeds limit"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_RESOURCE_EXHAUSTED)); + grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); + s->seen_error = true; + GRPC_MDELEM_UNREF(exec_ctx, md); + } else { + grpc_error *error = grpc_chttp2_incoming_metadata_buffer_add( + exec_ctx, &s->metadata_buffer[1], md); + if (error != GRPC_ERROR_NONE) { + grpc_chttp2_cancel_stream(exec_ctx, t, s, error); + grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); + s->seen_error = true; + GRPC_MDELEM_UNREF(exec_ctx, md); + } + } + + GPR_TIMER_END("on_trailing_header", 0); +} + +static grpc_error *init_header_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + int is_continuation) { + uint8_t is_eoh = + (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_HEADERS) != 0; + grpc_chttp2_stream *s; + + /* TODO(ctiller): when to increment header_frames_received? */ + + if (is_eoh) { + t->expect_continuation_stream_id = 0; + } else { + t->expect_continuation_stream_id = t->incoming_stream_id; + } + + if (!is_continuation) { + t->header_eof = + (t->incoming_frame_flags & GRPC_CHTTP2_DATA_FLAG_END_STREAM) != 0; + } + + t->ping_state.pings_before_data_required = + t->ping_policy.max_pings_without_data; + t->ping_state.last_ping_sent_time = gpr_inf_past(GPR_CLOCK_MONOTONIC); + + /* could be a new grpc_chttp2_stream or an existing grpc_chttp2_stream */ + s = grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + if (s == NULL) { + if (is_continuation) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_ERROR, + "grpc_chttp2_stream disbanded before CONTINUATION received")); + return init_skip_frame_parser(exec_ctx, t, 1); + } + if (t->is_client) { + if ((t->incoming_stream_id & 1) && + t->incoming_stream_id < t->next_stream_id) { + /* this is an old (probably cancelled) grpc_chttp2_stream */ + } else { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, "ignoring new grpc_chttp2_stream creation on client")); + } + return init_skip_frame_parser(exec_ctx, t, 1); + } else if (t->last_new_stream_id >= t->incoming_stream_id) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, + "ignoring out of order new grpc_chttp2_stream request on server; " + "last grpc_chttp2_stream " + "id=%d, new grpc_chttp2_stream id=%d", + t->last_new_stream_id, t->incoming_stream_id)); + return init_skip_frame_parser(exec_ctx, t, 1); + } else if ((t->incoming_stream_id & 1) == 0) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, + "ignoring grpc_chttp2_stream with non-client generated index %d", + t->incoming_stream_id)); + return init_skip_frame_parser(exec_ctx, t, 1); + } else if (grpc_chttp2_stream_map_size(&t->stream_map) >= + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS]) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Max stream count exceeded"); + } + t->last_new_stream_id = t->incoming_stream_id; + s = t->incoming_stream = + grpc_chttp2_parsing_accept_stream(exec_ctx, t, t->incoming_stream_id); + if (s == NULL) { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_ERROR, "grpc_chttp2_stream not accepted")); + return init_skip_frame_parser(exec_ctx, t, 1); + } + } else { + t->incoming_stream = s; + } + GPR_ASSERT(s != NULL); + s->stats.incoming.framing_bytes += 9; + if (s->read_closed) { + GRPC_CHTTP2_IF_TRACING(gpr_log( + GPR_ERROR, "skipping already closed grpc_chttp2_stream header")); + t->incoming_stream = NULL; + return init_skip_frame_parser(exec_ctx, t, 1); + } + t->parser = grpc_chttp2_header_parser_parse; + t->parser_data = &t->hpack_parser; + switch (s->header_frames_received) { + case 0: + if (t->is_client && t->header_eof) { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing Trailers-Only")); + if (s->trailing_metadata_available != NULL) { + *s->trailing_metadata_available = true; + } + t->hpack_parser.on_header = on_trailing_header; + s->received_trailing_metadata = true; + } else { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing initial_metadata")); + t->hpack_parser.on_header = on_initial_header; + } + break; + case 1: + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "parsing trailing_metadata")); + t->hpack_parser.on_header = on_trailing_header; + s->received_trailing_metadata = true; + break; + case 2: + gpr_log(GPR_ERROR, "too many header frames received"); + return init_skip_frame_parser(exec_ctx, t, 1); + } + t->hpack_parser.on_header_user_data = t; + t->hpack_parser.is_boundary = is_eoh; + t->hpack_parser.is_eof = (uint8_t)(is_eoh ? t->header_eof : 0); + if (!is_continuation && + (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_HAS_PRIORITY)) { + grpc_chttp2_hpack_parser_set_has_priority(&t->hpack_parser); + } + return GRPC_ERROR_NONE; +} + +static grpc_error *init_window_update_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_error *err = grpc_chttp2_window_update_parser_begin_frame( + &t->simple.window_update, t->incoming_frame_size, + t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + if (t->incoming_stream_id != 0) { + grpc_chttp2_stream *s = t->incoming_stream = + grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + if (s == NULL) { + return init_skip_frame_parser(exec_ctx, t, 0); + } + s->stats.incoming.framing_bytes += 9; + } + t->parser = grpc_chttp2_window_update_parser_parse; + t->parser_data = &t->simple.window_update; + return GRPC_ERROR_NONE; +} + +static grpc_error *init_ping_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_error *err = grpc_chttp2_ping_parser_begin_frame( + &t->simple.ping, t->incoming_frame_size, t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + t->parser = grpc_chttp2_ping_parser_parse; + t->parser_data = &t->simple.ping; + return GRPC_ERROR_NONE; +} + +static grpc_error *init_rst_stream_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_error *err = grpc_chttp2_rst_stream_parser_begin_frame( + &t->simple.rst_stream, t->incoming_frame_size, t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + grpc_chttp2_stream *s = t->incoming_stream = + grpc_chttp2_parsing_lookup_stream(t, t->incoming_stream_id); + if (!t->incoming_stream) { + return init_skip_frame_parser(exec_ctx, t, 0); + } + s->stats.incoming.framing_bytes += 9; + t->parser = grpc_chttp2_rst_stream_parser_parse; + t->parser_data = &t->simple.rst_stream; + return GRPC_ERROR_NONE; +} + +static grpc_error *init_goaway_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + grpc_error *err = grpc_chttp2_goaway_parser_begin_frame( + &t->goaway_parser, t->incoming_frame_size, t->incoming_frame_flags); + if (err != GRPC_ERROR_NONE) return err; + t->parser = grpc_chttp2_goaway_parser_parse; + t->parser_data = &t->goaway_parser; + return GRPC_ERROR_NONE; +} + +static grpc_error *init_settings_frame_parser(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t) { + if (t->incoming_stream_id != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Settings frame received for grpc_chttp2_stream"); + } + + grpc_error *err = grpc_chttp2_settings_parser_begin_frame( + &t->simple.settings, t->incoming_frame_size, t->incoming_frame_flags, + t->settings[GRPC_PEER_SETTINGS]); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (t->incoming_frame_flags & GRPC_CHTTP2_FLAG_ACK) { + memcpy(t->settings[GRPC_ACKED_SETTINGS], t->settings[GRPC_SENT_SETTINGS], + GRPC_CHTTP2_NUM_SETTINGS * sizeof(uint32_t)); + grpc_chttp2_hptbl_set_max_bytes( + exec_ctx, &t->hpack_parser.table, + t->settings[GRPC_ACKED_SETTINGS] + [GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); + t->sent_local_settings = 0; + } + t->parser = grpc_chttp2_settings_parser_parse; + t->parser_data = &t->simple.settings; + return GRPC_ERROR_NONE; +} + +static grpc_error *parse_frame_slice(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, grpc_slice slice, + int is_last) { + grpc_chttp2_stream *s = t->incoming_stream; + grpc_error *err = t->parser(exec_ctx, t->parser_data, t, s, slice, is_last); + if (err == GRPC_ERROR_NONE) { + return err; + } else if (grpc_error_get_int(err, GRPC_ERROR_INT_STREAM_ID, NULL)) { + if (GRPC_TRACER_ON(grpc_http_trace)) { + const char *msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "%s", msg); + } + grpc_chttp2_parsing_become_skip_parser(exec_ctx, t); + if (s) { + s->forced_close_error = err; + grpc_slice_buffer_add( + &t->qbuf, grpc_chttp2_rst_stream_create(t->incoming_stream_id, + GRPC_HTTP2_PROTOCOL_ERROR, + &s->stats.outgoing)); + } else { + GRPC_ERROR_UNREF(err); + } + } + return err; +} diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.c b/src/core/ext/transport/chttp2/transport/stream_lists.c deleted file mode 100644 index 34f62aef84..0000000000 --- a/src/core/ext/transport/chttp2/transport/stream_lists.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include - -static const char *stream_list_id_string(grpc_chttp2_stream_list_id id) { - switch (id) { - case GRPC_CHTTP2_LIST_WRITABLE: - return "writable"; - case GRPC_CHTTP2_LIST_WRITING: - return "writing"; - case GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT: - return "stalled_by_transport"; - case GRPC_CHTTP2_LIST_STALLED_BY_STREAM: - return "stalled_by_stream"; - case GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY: - return "waiting_for_concurrency"; - case STREAM_LIST_COUNT: - GPR_UNREACHABLE_CODE(return "unknown"); - } - GPR_UNREACHABLE_CODE(return "unknown"); -} - -grpc_tracer_flag grpc_trace_http2_stream_state = - GRPC_TRACER_INITIALIZER(false, "http2_stream_state"); - -/* core list management */ - -static bool stream_list_empty(grpc_chttp2_transport *t, - grpc_chttp2_stream_list_id id) { - return t->lists[id].head == NULL; -} - -static bool stream_list_pop(grpc_chttp2_transport *t, - grpc_chttp2_stream **stream, - grpc_chttp2_stream_list_id id) { - grpc_chttp2_stream *s = t->lists[id].head; - if (s) { - grpc_chttp2_stream *new_head = s->links[id].next; - GPR_ASSERT(s->included[id]); - if (new_head) { - t->lists[id].head = new_head; - new_head->links[id].prev = NULL; - } else { - t->lists[id].head = NULL; - t->lists[id].tail = NULL; - } - s->included[id] = 0; - } - *stream = s; - if (s && GRPC_TRACER_ON(grpc_trace_http2_stream_state)) { - gpr_log(GPR_DEBUG, "%p[%d][%s]: pop from %s", t, s->id, - t->is_client ? "cli" : "svr", stream_list_id_string(id)); - } - return s != 0; -} - -static void stream_list_remove(grpc_chttp2_transport *t, grpc_chttp2_stream *s, - grpc_chttp2_stream_list_id id) { - GPR_ASSERT(s->included[id]); - s->included[id] = 0; - if (s->links[id].prev) { - s->links[id].prev->links[id].next = s->links[id].next; - } else { - GPR_ASSERT(t->lists[id].head == s); - t->lists[id].head = s->links[id].next; - } - if (s->links[id].next) { - s->links[id].next->links[id].prev = s->links[id].prev; - } else { - t->lists[id].tail = s->links[id].prev; - } - if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) { - gpr_log(GPR_DEBUG, "%p[%d][%s]: remove from %s", t, s->id, - t->is_client ? "cli" : "svr", stream_list_id_string(id)); - } -} - -static bool stream_list_maybe_remove(grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_chttp2_stream_list_id id) { - if (s->included[id]) { - stream_list_remove(t, s, id); - return true; - } else { - return false; - } -} - -static void stream_list_add_tail(grpc_chttp2_transport *t, - grpc_chttp2_stream *s, - grpc_chttp2_stream_list_id id) { - grpc_chttp2_stream *old_tail; - GPR_ASSERT(!s->included[id]); - old_tail = t->lists[id].tail; - s->links[id].next = NULL; - s->links[id].prev = old_tail; - if (old_tail) { - old_tail->links[id].next = s; - } else { - t->lists[id].head = s; - } - t->lists[id].tail = s; - s->included[id] = 1; - if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) { - gpr_log(GPR_DEBUG, "%p[%d][%s]: add to %s", t, s->id, - t->is_client ? "cli" : "svr", stream_list_id_string(id)); - } -} - -static bool stream_list_add(grpc_chttp2_transport *t, grpc_chttp2_stream *s, - grpc_chttp2_stream_list_id id) { - if (s->included[id]) { - return false; - } - stream_list_add_tail(t, s, id); - return true; -} - -/* wrappers for specializations */ - -bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - GPR_ASSERT(s->id != 0); - return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITABLE); -} - -bool grpc_chttp2_list_pop_writable_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream **s) { - return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITABLE); -} - -bool grpc_chttp2_list_remove_writable_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WRITABLE); -} - -bool grpc_chttp2_list_add_writing_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITING); -} - -bool grpc_chttp2_list_have_writing_streams(grpc_chttp2_transport *t) { - return !stream_list_empty(t, GRPC_CHTTP2_LIST_WRITING); -} - -bool grpc_chttp2_list_pop_writing_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream **s) { - return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITING); -} - -void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - stream_list_add(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); -} - -bool grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport *t, - grpc_chttp2_stream **s) { - return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); -} - -void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); -} - -void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); -} - -bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport *t, - grpc_chttp2_stream **s) { - return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); -} - -void grpc_chttp2_list_remove_stalled_by_transport(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); -} - -void grpc_chttp2_list_add_stalled_by_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); -} - -bool grpc_chttp2_list_pop_stalled_by_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream **s) { - return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); -} - -bool grpc_chttp2_list_remove_stalled_by_stream(grpc_chttp2_transport *t, - grpc_chttp2_stream *s) { - return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); -} diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.cc b/src/core/ext/transport/chttp2/transport/stream_lists.cc new file mode 100644 index 0000000000..34f62aef84 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/stream_lists.cc @@ -0,0 +1,212 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +static const char *stream_list_id_string(grpc_chttp2_stream_list_id id) { + switch (id) { + case GRPC_CHTTP2_LIST_WRITABLE: + return "writable"; + case GRPC_CHTTP2_LIST_WRITING: + return "writing"; + case GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT: + return "stalled_by_transport"; + case GRPC_CHTTP2_LIST_STALLED_BY_STREAM: + return "stalled_by_stream"; + case GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY: + return "waiting_for_concurrency"; + case STREAM_LIST_COUNT: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +grpc_tracer_flag grpc_trace_http2_stream_state = + GRPC_TRACER_INITIALIZER(false, "http2_stream_state"); + +/* core list management */ + +static bool stream_list_empty(grpc_chttp2_transport *t, + grpc_chttp2_stream_list_id id) { + return t->lists[id].head == NULL; +} + +static bool stream_list_pop(grpc_chttp2_transport *t, + grpc_chttp2_stream **stream, + grpc_chttp2_stream_list_id id) { + grpc_chttp2_stream *s = t->lists[id].head; + if (s) { + grpc_chttp2_stream *new_head = s->links[id].next; + GPR_ASSERT(s->included[id]); + if (new_head) { + t->lists[id].head = new_head; + new_head->links[id].prev = NULL; + } else { + t->lists[id].head = NULL; + t->lists[id].tail = NULL; + } + s->included[id] = 0; + } + *stream = s; + if (s && GRPC_TRACER_ON(grpc_trace_http2_stream_state)) { + gpr_log(GPR_DEBUG, "%p[%d][%s]: pop from %s", t, s->id, + t->is_client ? "cli" : "svr", stream_list_id_string(id)); + } + return s != 0; +} + +static void stream_list_remove(grpc_chttp2_transport *t, grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + GPR_ASSERT(s->included[id]); + s->included[id] = 0; + if (s->links[id].prev) { + s->links[id].prev->links[id].next = s->links[id].next; + } else { + GPR_ASSERT(t->lists[id].head == s); + t->lists[id].head = s->links[id].next; + } + if (s->links[id].next) { + s->links[id].next->links[id].prev = s->links[id].prev; + } else { + t->lists[id].tail = s->links[id].prev; + } + if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) { + gpr_log(GPR_DEBUG, "%p[%d][%s]: remove from %s", t, s->id, + t->is_client ? "cli" : "svr", stream_list_id_string(id)); + } +} + +static bool stream_list_maybe_remove(grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + if (s->included[id]) { + stream_list_remove(t, s, id); + return true; + } else { + return false; + } +} + +static void stream_list_add_tail(grpc_chttp2_transport *t, + grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + grpc_chttp2_stream *old_tail; + GPR_ASSERT(!s->included[id]); + old_tail = t->lists[id].tail; + s->links[id].next = NULL; + s->links[id].prev = old_tail; + if (old_tail) { + old_tail->links[id].next = s; + } else { + t->lists[id].head = s; + } + t->lists[id].tail = s; + s->included[id] = 1; + if (GRPC_TRACER_ON(grpc_trace_http2_stream_state)) { + gpr_log(GPR_DEBUG, "%p[%d][%s]: add to %s", t, s->id, + t->is_client ? "cli" : "svr", stream_list_id_string(id)); + } +} + +static bool stream_list_add(grpc_chttp2_transport *t, grpc_chttp2_stream *s, + grpc_chttp2_stream_list_id id) { + if (s->included[id]) { + return false; + } + stream_list_add_tail(t, s, id); + return true; +} + +/* wrappers for specializations */ + +bool grpc_chttp2_list_add_writable_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + GPR_ASSERT(s->id != 0); + return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITABLE); +} + +bool grpc_chttp2_list_pop_writable_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream **s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITABLE); +} + +bool grpc_chttp2_list_remove_writable_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WRITABLE); +} + +bool grpc_chttp2_list_add_writing_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + return stream_list_add(t, s, GRPC_CHTTP2_LIST_WRITING); +} + +bool grpc_chttp2_list_have_writing_streams(grpc_chttp2_transport *t) { + return !stream_list_empty(t, GRPC_CHTTP2_LIST_WRITING); +} + +bool grpc_chttp2_list_pop_writing_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream **s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WRITING); +} + +void grpc_chttp2_list_add_waiting_for_concurrency(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_add(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +bool grpc_chttp2_list_pop_waiting_for_concurrency(grpc_chttp2_transport *t, + grpc_chttp2_stream **s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +void grpc_chttp2_list_remove_waiting_for_concurrency(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_WAITING_FOR_CONCURRENCY); +} + +void grpc_chttp2_list_add_stalled_by_transport(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); +} + +bool grpc_chttp2_list_pop_stalled_by_transport(grpc_chttp2_transport *t, + grpc_chttp2_stream **s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); +} + +void grpc_chttp2_list_remove_stalled_by_transport(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_TRANSPORT); +} + +void grpc_chttp2_list_add_stalled_by_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + stream_list_add(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); +} + +bool grpc_chttp2_list_pop_stalled_by_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream **s) { + return stream_list_pop(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); +} + +bool grpc_chttp2_list_remove_stalled_by_stream(grpc_chttp2_transport *t, + grpc_chttp2_stream *s) { + return stream_list_maybe_remove(t, s, GRPC_CHTTP2_LIST_STALLED_BY_STREAM); +} diff --git a/src/core/ext/transport/chttp2/transport/stream_map.c b/src/core/ext/transport/chttp2/transport/stream_map.c deleted file mode 100644 index d6079a9a33..0000000000 --- a/src/core/ext/transport/chttp2/transport/stream_map.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/stream_map.h" - -#include - -#include -#include -#include - -void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map *map, - size_t initial_capacity) { - GPR_ASSERT(initial_capacity > 1); - map->keys = (uint32_t *)gpr_malloc(sizeof(uint32_t) * initial_capacity); - map->values = (void **)gpr_malloc(sizeof(void *) * initial_capacity); - map->count = 0; - map->free = 0; - map->capacity = initial_capacity; -} - -void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map *map) { - gpr_free(map->keys); - gpr_free(map->values); -} - -static size_t compact(uint32_t *keys, void **values, size_t count) { - size_t i, out; - - for (i = 0, out = 0; i < count; i++) { - if (values[i]) { - keys[out] = keys[i]; - values[out] = values[i]; - out++; - } - } - - return out; -} - -void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map *map, uint32_t key, - void *value) { - size_t count = map->count; - size_t capacity = map->capacity; - uint32_t *keys = map->keys; - void **values = map->values; - - GPR_ASSERT(count == 0 || keys[count - 1] < key); - GPR_ASSERT(value); - GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == NULL); - - if (count == capacity) { - if (map->free > capacity / 4) { - count = compact(keys, values, count); - map->free = 0; - } else { - /* resize when less than 25% of the table is free, because compaction - won't help much */ - map->capacity = capacity = 3 * capacity / 2; - map->keys = keys = - (uint32_t *)gpr_realloc(keys, capacity * sizeof(uint32_t)); - map->values = values = - (void **)gpr_realloc(values, capacity * sizeof(void *)); - } - } - - keys[count] = key; - values[count] = value; - map->count = count + 1; -} - -static void **find(grpc_chttp2_stream_map *map, uint32_t key) { - size_t min_idx = 0; - size_t max_idx = map->count; - size_t mid_idx; - uint32_t *keys = map->keys; - void **values = map->values; - uint32_t mid_key; - - if (max_idx == 0) return NULL; - - while (min_idx < max_idx) { - /* find the midpoint, avoiding overflow */ - mid_idx = min_idx + ((max_idx - min_idx) / 2); - mid_key = keys[mid_idx]; - - if (mid_key < key) { - min_idx = mid_idx + 1; - } else if (mid_key > key) { - max_idx = mid_idx; - } else /* mid_key == key */ - { - return &values[mid_idx]; - } - } - - return NULL; -} - -void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, uint32_t key) { - void **pvalue = find(map, key); - void *out = NULL; - if (pvalue != NULL) { - out = *pvalue; - *pvalue = NULL; - map->free += (out != NULL); - /* recognize complete emptyness and ensure we can skip - * defragmentation later */ - if (map->free == map->count) { - map->free = map->count = 0; - } - GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == NULL); - } - return out; -} - -void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, uint32_t key) { - void **pvalue = find(map, key); - return pvalue != NULL ? *pvalue : NULL; -} - -size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map) { - return map->count - map->free; -} - -void *grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map *map) { - if (map->count == map->free) { - return NULL; - } - if (map->free != 0) { - map->count = compact(map->keys, map->values, map->count); - map->free = 0; - } - return map->values[((size_t)rand()) % map->count]; -} - -void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, - void (*f)(void *user_data, uint32_t key, - void *value), - void *user_data) { - size_t i; - - for (i = 0; i < map->count; i++) { - if (map->values[i]) { - f(user_data, map->keys[i], map->values[i]); - } - } -} diff --git a/src/core/ext/transport/chttp2/transport/stream_map.cc b/src/core/ext/transport/chttp2/transport/stream_map.cc new file mode 100644 index 0000000000..d6079a9a33 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/stream_map.cc @@ -0,0 +1,163 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/stream_map.h" + +#include + +#include +#include +#include + +void grpc_chttp2_stream_map_init(grpc_chttp2_stream_map *map, + size_t initial_capacity) { + GPR_ASSERT(initial_capacity > 1); + map->keys = (uint32_t *)gpr_malloc(sizeof(uint32_t) * initial_capacity); + map->values = (void **)gpr_malloc(sizeof(void *) * initial_capacity); + map->count = 0; + map->free = 0; + map->capacity = initial_capacity; +} + +void grpc_chttp2_stream_map_destroy(grpc_chttp2_stream_map *map) { + gpr_free(map->keys); + gpr_free(map->values); +} + +static size_t compact(uint32_t *keys, void **values, size_t count) { + size_t i, out; + + for (i = 0, out = 0; i < count; i++) { + if (values[i]) { + keys[out] = keys[i]; + values[out] = values[i]; + out++; + } + } + + return out; +} + +void grpc_chttp2_stream_map_add(grpc_chttp2_stream_map *map, uint32_t key, + void *value) { + size_t count = map->count; + size_t capacity = map->capacity; + uint32_t *keys = map->keys; + void **values = map->values; + + GPR_ASSERT(count == 0 || keys[count - 1] < key); + GPR_ASSERT(value); + GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == NULL); + + if (count == capacity) { + if (map->free > capacity / 4) { + count = compact(keys, values, count); + map->free = 0; + } else { + /* resize when less than 25% of the table is free, because compaction + won't help much */ + map->capacity = capacity = 3 * capacity / 2; + map->keys = keys = + (uint32_t *)gpr_realloc(keys, capacity * sizeof(uint32_t)); + map->values = values = + (void **)gpr_realloc(values, capacity * sizeof(void *)); + } + } + + keys[count] = key; + values[count] = value; + map->count = count + 1; +} + +static void **find(grpc_chttp2_stream_map *map, uint32_t key) { + size_t min_idx = 0; + size_t max_idx = map->count; + size_t mid_idx; + uint32_t *keys = map->keys; + void **values = map->values; + uint32_t mid_key; + + if (max_idx == 0) return NULL; + + while (min_idx < max_idx) { + /* find the midpoint, avoiding overflow */ + mid_idx = min_idx + ((max_idx - min_idx) / 2); + mid_key = keys[mid_idx]; + + if (mid_key < key) { + min_idx = mid_idx + 1; + } else if (mid_key > key) { + max_idx = mid_idx; + } else /* mid_key == key */ + { + return &values[mid_idx]; + } + } + + return NULL; +} + +void *grpc_chttp2_stream_map_delete(grpc_chttp2_stream_map *map, uint32_t key) { + void **pvalue = find(map, key); + void *out = NULL; + if (pvalue != NULL) { + out = *pvalue; + *pvalue = NULL; + map->free += (out != NULL); + /* recognize complete emptyness and ensure we can skip + * defragmentation later */ + if (map->free == map->count) { + map->free = map->count = 0; + } + GPR_ASSERT(grpc_chttp2_stream_map_find(map, key) == NULL); + } + return out; +} + +void *grpc_chttp2_stream_map_find(grpc_chttp2_stream_map *map, uint32_t key) { + void **pvalue = find(map, key); + return pvalue != NULL ? *pvalue : NULL; +} + +size_t grpc_chttp2_stream_map_size(grpc_chttp2_stream_map *map) { + return map->count - map->free; +} + +void *grpc_chttp2_stream_map_rand(grpc_chttp2_stream_map *map) { + if (map->count == map->free) { + return NULL; + } + if (map->free != 0) { + map->count = compact(map->keys, map->values, map->count); + map->free = 0; + } + return map->values[((size_t)rand()) % map->count]; +} + +void grpc_chttp2_stream_map_for_each(grpc_chttp2_stream_map *map, + void (*f)(void *user_data, uint32_t key, + void *value), + void *user_data) { + size_t i; + + for (i = 0; i < map->count; i++) { + if (map->values[i]) { + f(user_data, map->keys[i], map->values[i]); + } + } +} diff --git a/src/core/ext/transport/chttp2/transport/varint.c b/src/core/ext/transport/chttp2/transport/varint.c deleted file mode 100644 index 0d94ddcbc3..0000000000 --- a/src/core/ext/transport/chttp2/transport/varint.c +++ /dev/null @@ -1,54 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/varint.h" - -uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value) { - if (tail_value < (1 << 7)) { - return 2; - } else if (tail_value < (1 << 14)) { - return 3; - } else if (tail_value < (1 << 21)) { - return 4; - } else if (tail_value < (1 << 28)) { - return 5; - } else { - return 6; - } -} - -void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target, - uint32_t tail_length) { - switch (tail_length) { - case 5: - target[4] = (uint8_t)((tail_value >> 28) | 0x80); - /* fallthrough */ - case 4: - target[3] = (uint8_t)((tail_value >> 21) | 0x80); - /* fallthrough */ - case 3: - target[2] = (uint8_t)((tail_value >> 14) | 0x80); - /* fallthrough */ - case 2: - target[1] = (uint8_t)((tail_value >> 7) | 0x80); - /* fallthrough */ - case 1: - target[0] = (uint8_t)((tail_value) | 0x80); - } - target[tail_length - 1] &= 0x7f; -} diff --git a/src/core/ext/transport/chttp2/transport/varint.cc b/src/core/ext/transport/chttp2/transport/varint.cc new file mode 100644 index 0000000000..0d94ddcbc3 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/varint.cc @@ -0,0 +1,54 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/varint.h" + +uint32_t grpc_chttp2_hpack_varint_length(uint32_t tail_value) { + if (tail_value < (1 << 7)) { + return 2; + } else if (tail_value < (1 << 14)) { + return 3; + } else if (tail_value < (1 << 21)) { + return 4; + } else if (tail_value < (1 << 28)) { + return 5; + } else { + return 6; + } +} + +void grpc_chttp2_hpack_write_varint_tail(uint32_t tail_value, uint8_t* target, + uint32_t tail_length) { + switch (tail_length) { + case 5: + target[4] = (uint8_t)((tail_value >> 28) | 0x80); + /* fallthrough */ + case 4: + target[3] = (uint8_t)((tail_value >> 21) | 0x80); + /* fallthrough */ + case 3: + target[2] = (uint8_t)((tail_value >> 14) | 0x80); + /* fallthrough */ + case 2: + target[1] = (uint8_t)((tail_value >> 7) | 0x80); + /* fallthrough */ + case 1: + target[0] = (uint8_t)((tail_value) | 0x80); + } + target[tail_length - 1] &= 0x7f; +} diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c deleted file mode 100644 index be1af16019..0000000000 --- a/src/core/ext/transport/chttp2/transport/writing.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/chttp2/transport/internal.h" - -#include - -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/transport/http2_errors.h" - -static void add_to_write_list(grpc_chttp2_write_cb **list, - grpc_chttp2_write_cb *cb) { - cb->next = *list; - *list = cb; -} - -static void finish_write_cb(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, grpc_chttp2_write_cb *cb, - grpc_error *error) { - grpc_chttp2_complete_closure_step(exec_ctx, t, s, &cb->closure, error, - "finish_write_cb"); - cb->next = t->write_cb_pool; - t->write_cb_pool = cb; -} - -static void collapse_pings_from_into(grpc_chttp2_transport *t, - grpc_chttp2_ping_type ping_type, - grpc_chttp2_ping_queue *pq) { - for (size_t i = 0; i < GRPC_CHTTP2_PCL_COUNT; i++) { - grpc_closure_list_move(&t->ping_queues[ping_type].lists[i], &pq->lists[i]); - } -} - -static void maybe_initiate_ping(grpc_exec_ctx *exec_ctx, - grpc_chttp2_transport *t, - grpc_chttp2_ping_type ping_type) { - grpc_chttp2_ping_queue *pq = &t->ping_queues[ping_type]; - if (grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) { - /* no ping needed: wait */ - return; - } - if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) { - /* ping already in-flight: wait */ - if (GRPC_TRACER_ON(grpc_http_trace) || - GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "Ping delayed [%p]: already pinging", t->peer_string); - } - return; - } - if (t->ping_state.pings_before_data_required == 0 && - t->ping_policy.max_pings_without_data != 0) { - /* need to receive something of substance before sending a ping again */ - if (GRPC_TRACER_ON(grpc_http_trace) || - GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "Ping delayed [%p]: too many recent pings: %d/%d", - t->peer_string, t->ping_state.pings_before_data_required, - t->ping_policy.max_pings_without_data); - } - return; - } - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec next_allowed_ping = - gpr_time_add(t->ping_state.last_ping_sent_time, - t->ping_policy.min_sent_ping_interval_without_data); - if (t->keepalive_permit_without_calls == 0 && - grpc_chttp2_stream_map_size(&t->stream_map) == 0) { - next_allowed_ping = gpr_time_add(t->ping_recv_state.last_ping_recv_time, - gpr_time_from_seconds(7200, GPR_TIMESPAN)); - } - /* gpr_log(GPR_DEBUG, "next_allowed_ping:%d.%09d now:%d.%09d", - (int)next_allowed_ping.tv_sec, (int)next_allowed_ping.tv_nsec, - (int)now.tv_sec, (int)now.tv_nsec); */ - if (gpr_time_cmp(next_allowed_ping, now) > 0) { - /* not enough elapsed time between successive pings */ - if (GRPC_TRACER_ON(grpc_http_trace) || - GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, - "Ping delayed [%p]: not enough time elapsed since last ping", - t->peer_string); - } - if (!t->ping_state.is_delayed_ping_timer_set) { - t->ping_state.is_delayed_ping_timer_set = true; - grpc_timer_init(exec_ctx, &t->ping_state.delayed_ping_timer, - next_allowed_ping, &t->retry_initiate_ping_locked, - gpr_now(GPR_CLOCK_MONOTONIC)); - } - return; - } - /* coalesce equivalent pings into this one */ - switch (ping_type) { - case GRPC_CHTTP2_PING_BEFORE_TRANSPORT_WINDOW_UPDATE: - collapse_pings_from_into(t, GRPC_CHTTP2_PING_ON_NEXT_WRITE, pq); - break; - case GRPC_CHTTP2_PING_ON_NEXT_WRITE: - break; - case GRPC_CHTTP2_PING_TYPE_COUNT: - GPR_UNREACHABLE_CODE(break); - } - pq->inflight_id = t->ping_ctr * GRPC_CHTTP2_PING_TYPE_COUNT + ping_type; - t->ping_ctr++; - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pq->lists[GRPC_CHTTP2_PCL_INITIATE]); - grpc_closure_list_move(&pq->lists[GRPC_CHTTP2_PCL_NEXT], - &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]); - grpc_slice_buffer_add(&t->outbuf, - grpc_chttp2_ping_create(false, pq->inflight_id)); - GRPC_STATS_INC_HTTP2_PINGS_SENT(exec_ctx); - t->ping_state.last_ping_sent_time = now; - if (GRPC_TRACER_ON(grpc_http_trace) || - GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "Ping sent [%p]: %d/%d", t->peer_string, - t->ping_state.pings_before_data_required, - t->ping_policy.max_pings_without_data); - } - t->ping_state.pings_before_data_required -= - (t->ping_state.pings_before_data_required != 0); -} - -static bool update_list(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_chttp2_stream *s, int64_t send_bytes, - grpc_chttp2_write_cb **list, int64_t *ctr, - grpc_error *error) { - bool sched_any = false; - grpc_chttp2_write_cb *cb = *list; - *list = NULL; - *ctr += send_bytes; - while (cb) { - grpc_chttp2_write_cb *next = cb->next; - if (cb->call_at_byte <= *ctr) { - sched_any = true; - finish_write_cb(exec_ctx, t, s, cb, GRPC_ERROR_REF(error)); - } else { - add_to_write_list(list, cb); - } - cb = next; - } - GRPC_ERROR_UNREF(error); - return sched_any; -} - -static bool stream_ref_if_not_destroyed(gpr_refcount *r) { - gpr_atm count; - do { - count = gpr_atm_acq_load(&r->count); - if (count == 0) return false; - } while (!gpr_atm_rel_cas(&r->count, count, count + 1)); - return true; -} - -/* How many bytes would we like to put on the wire during a single syscall */ -static uint32_t target_write_size(grpc_chttp2_transport *t) { - return 1024 * 1024; -} - -// Returns true if initial_metadata contains only default headers. -static bool is_default_initial_metadata(grpc_metadata_batch *initial_metadata) { - return initial_metadata->list.default_count == initial_metadata->list.count; -} - -grpc_chttp2_begin_write_result grpc_chttp2_begin_write( - grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { - grpc_chttp2_stream *s; - - /* stats histogram counters: we increment these throughout this function, - and at the end publish to the central stats histograms */ - int flow_control_writes = 0; - int initial_metadata_writes = 0; - int trailing_metadata_writes = 0; - int message_writes = 0; - - GRPC_STATS_INC_HTTP2_WRITES_BEGUN(exec_ctx); - - GPR_TIMER_BEGIN("grpc_chttp2_begin_write", 0); - - if (t->dirtied_local_settings && !t->sent_local_settings) { - grpc_slice_buffer_add( - &t->outbuf, - grpc_chttp2_settings_create( - t->settings[GRPC_SENT_SETTINGS], t->settings[GRPC_LOCAL_SETTINGS], - t->force_send_settings, GRPC_CHTTP2_NUM_SETTINGS)); - t->force_send_settings = 0; - t->dirtied_local_settings = 0; - t->sent_local_settings = 1; - GRPC_STATS_INC_HTTP2_SETTINGS_WRITES(exec_ctx); - } - - /* simple writes are queued to qbuf, and flushed here */ - grpc_slice_buffer_move_into(&t->qbuf, &t->outbuf); - GPR_ASSERT(t->qbuf.count == 0); - - grpc_chttp2_hpack_compressor_set_max_table_size( - &t->hpack_compressor, - t->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); - - if (t->flow_control.remote_window > 0) { - while (grpc_chttp2_list_pop_stalled_by_transport(t, &s)) { - if (!t->closed && grpc_chttp2_list_add_writable_stream(t, s)) { - stream_ref_if_not_destroyed(&s->refcount->refs); - } - } - } - - grpc_chttp2_begin_write_result result = {false, false, false}; - - /* for each grpc_chttp2_stream that's become writable, frame it's data - (according to available window sizes) and add to the output buffer */ - while (true) { - if (t->outbuf.length > target_write_size(t)) { - result.partial = true; - break; - } - - if (!grpc_chttp2_list_pop_writable_stream(t, &s)) { - break; - } - - bool sent_initial_metadata = s->sent_initial_metadata; - bool now_writing = false; - - GRPC_CHTTP2_IF_TRACING( - gpr_log(GPR_DEBUG, "W:%p %s[%d] im-(sent,send)=(%d,%d) announce=%d", t, - t->is_client ? "CLIENT" : "SERVER", s->id, - sent_initial_metadata, s->send_initial_metadata != NULL, - (int)(s->flow_control.local_window_delta - - s->flow_control.announced_window_delta))); - - grpc_mdelem *extra_headers_for_trailing_metadata[2]; - size_t num_extra_headers_for_trailing_metadata = 0; - - /* send initial metadata if it's available */ - if (!sent_initial_metadata && s->send_initial_metadata != NULL) { - // We skip this on the server side if there is no custom initial - // metadata, there are no messages to send, and we are also sending - // trailing metadata. This results in a Trailers-Only response, - // which is required for retries, as per: - // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid - if (t->is_client || s->fetching_send_message != NULL || - s->flow_controlled_buffer.length != 0 || - s->send_trailing_metadata == NULL || - !is_default_initial_metadata(s->send_initial_metadata)) { - grpc_encode_header_options hopt = { - .stream_id = s->id, - .is_eof = false, - .use_true_binary_metadata = - t->settings - [GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != 0, - .max_frame_size = t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], - .stats = &s->stats.outgoing}; - grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, NULL, 0, - s->send_initial_metadata, &hopt, &t->outbuf); - now_writing = true; - if (!t->is_client) { - t->ping_recv_state.last_ping_recv_time = - gpr_inf_past(GPR_CLOCK_MONOTONIC); - t->ping_recv_state.ping_strikes = 0; - } - initial_metadata_writes++; - } else { - GRPC_CHTTP2_IF_TRACING( - gpr_log(GPR_INFO, "not sending initial_metadata (Trailers-Only)")); - // When sending Trailers-Only, we need to move the :status and - // content-type headers to the trailers. - if (s->send_initial_metadata->idx.named.status != NULL) { - extra_headers_for_trailing_metadata - [num_extra_headers_for_trailing_metadata++] = - &s->send_initial_metadata->idx.named.status->md; - } - if (s->send_initial_metadata->idx.named.content_type != NULL) { - extra_headers_for_trailing_metadata - [num_extra_headers_for_trailing_metadata++] = - &s->send_initial_metadata->idx.named.content_type->md; - } - trailing_metadata_writes++; - } - s->send_initial_metadata = NULL; - s->sent_initial_metadata = true; - sent_initial_metadata = true; - result.early_results_scheduled = true; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->send_initial_metadata_finished, GRPC_ERROR_NONE, - "send_initial_metadata_finished"); - } - /* send any window updates */ - uint32_t stream_announce = grpc_chttp2_flowctl_maybe_send_stream_update( - &t->flow_control, &s->flow_control); - if (stream_announce > 0) { - grpc_slice_buffer_add( - &t->outbuf, grpc_chttp2_window_update_create(s->id, stream_announce, - &s->stats.outgoing)); - if (!t->is_client) { - t->ping_recv_state.last_ping_recv_time = - gpr_inf_past(GPR_CLOCK_MONOTONIC); - t->ping_recv_state.ping_strikes = 0; - } - flow_control_writes++; - } - if (sent_initial_metadata) { - /* send any body bytes, if allowed by flow control */ - if (s->flow_controlled_buffer.length > 0 || - s->compressed_data_buffer.length > 0) { - uint32_t stream_remote_window = (uint32_t)GPR_MAX( - 0, - s->flow_control.remote_window_delta + - (int64_t)t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); - uint32_t max_outgoing = (uint32_t)GPR_MIN( - t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], - GPR_MIN(stream_remote_window, t->flow_control.remote_window)); - if (max_outgoing > 0) { - bool is_last_data_frame = false; - bool is_last_frame = false; - size_t sending_bytes_before = s->sending_bytes; - while ((s->flow_controlled_buffer.length > 0 || - s->compressed_data_buffer.length > 0) && - max_outgoing > 0) { - if (s->compressed_data_buffer.length > 0) { - uint32_t send_bytes = (uint32_t)GPR_MIN( - max_outgoing, s->compressed_data_buffer.length); - is_last_data_frame = - (send_bytes == s->compressed_data_buffer.length && - s->flow_controlled_buffer.length == 0 && - s->fetching_send_message == NULL); - if (is_last_data_frame && s->send_trailing_metadata != NULL && - s->stream_compression_ctx != NULL) { - if (!grpc_stream_compress( - s->stream_compression_ctx, &s->flow_controlled_buffer, - &s->compressed_data_buffer, NULL, MAX_SIZE_T, - GRPC_STREAM_COMPRESSION_FLUSH_FINISH)) { - gpr_log(GPR_ERROR, "Stream compression failed."); - } - grpc_stream_compression_context_destroy( - s->stream_compression_ctx); - s->stream_compression_ctx = NULL; - /* After finish, bytes in s->compressed_data_buffer may be - * more than max_outgoing. Start another round of the current - * while loop so that send_bytes and is_last_data_frame are - * recalculated. */ - continue; - } - is_last_frame = - is_last_data_frame && s->send_trailing_metadata != NULL && - grpc_metadata_batch_is_empty(s->send_trailing_metadata); - grpc_chttp2_encode_data(s->id, &s->compressed_data_buffer, - send_bytes, is_last_frame, - &s->stats.outgoing, &t->outbuf); - grpc_chttp2_flowctl_sent_data(&t->flow_control, &s->flow_control, - send_bytes); - max_outgoing -= send_bytes; - if (s->compressed_data_buffer.length == 0) { - s->sending_bytes += s->uncompressed_data_size; - } - } else { - if (s->stream_compression_ctx == NULL) { - s->stream_compression_ctx = - grpc_stream_compression_context_create( - s->stream_compression_method); - } - s->uncompressed_data_size = s->flow_controlled_buffer.length; - if (!grpc_stream_compress( - s->stream_compression_ctx, &s->flow_controlled_buffer, - &s->compressed_data_buffer, NULL, MAX_SIZE_T, - GRPC_STREAM_COMPRESSION_FLUSH_SYNC)) { - gpr_log(GPR_ERROR, "Stream compression failed."); - } - } - } - if (!t->is_client) { - t->ping_recv_state.last_ping_recv_time = - gpr_inf_past(GPR_CLOCK_MONOTONIC); - t->ping_recv_state.ping_strikes = 0; - } - if (is_last_frame) { - s->send_trailing_metadata = NULL; - s->sent_trailing_metadata = true; - if (!t->is_client && !s->read_closed) { - grpc_slice_buffer_add(&t->outbuf, grpc_chttp2_rst_stream_create( - s->id, GRPC_HTTP2_NO_ERROR, - &s->stats.outgoing)); - } - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, !t->is_client, 1, - GRPC_ERROR_NONE); - } - result.early_results_scheduled |= - update_list(exec_ctx, t, s, - (int64_t)(s->sending_bytes - sending_bytes_before), - &s->on_flow_controlled_cbs, - &s->flow_controlled_bytes_flowed, GRPC_ERROR_NONE); - now_writing = true; - if (s->flow_controlled_buffer.length > 0 || - s->compressed_data_buffer.length > 0) { - GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:fork"); - grpc_chttp2_list_add_writable_stream(t, s); - } - message_writes++; - } else if (t->flow_control.remote_window == 0) { - grpc_chttp2_list_add_stalled_by_transport(t, s); - now_writing = true; - } else if (stream_remote_window == 0) { - grpc_chttp2_list_add_stalled_by_stream(t, s); - now_writing = true; - } - } - if (s->send_trailing_metadata != NULL && - s->fetching_send_message == NULL && - s->flow_controlled_buffer.length == 0 && - s->compressed_data_buffer.length == 0) { - GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata")); - if (grpc_metadata_batch_is_empty(s->send_trailing_metadata)) { - grpc_chttp2_encode_data(s->id, &s->flow_controlled_buffer, 0, true, - &s->stats.outgoing, &t->outbuf); - } else { - grpc_encode_header_options hopt = { - .stream_id = s->id, - .is_eof = true, - .use_true_binary_metadata = - t->settings - [GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != - 0, - .max_frame_size = - t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], - .stats = &s->stats.outgoing}; - grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, - extra_headers_for_trailing_metadata, - num_extra_headers_for_trailing_metadata, - s->send_trailing_metadata, &hopt, - &t->outbuf); - trailing_metadata_writes++; - } - s->send_trailing_metadata = NULL; - s->sent_trailing_metadata = true; - if (!t->is_client && !s->read_closed) { - grpc_slice_buffer_add( - &t->outbuf, grpc_chttp2_rst_stream_create( - s->id, GRPC_HTTP2_NO_ERROR, &s->stats.outgoing)); - } - grpc_chttp2_mark_stream_closed(exec_ctx, t, s, !t->is_client, 1, - GRPC_ERROR_NONE); - now_writing = true; - result.early_results_scheduled = true; - grpc_chttp2_complete_closure_step( - exec_ctx, t, s, &s->send_trailing_metadata_finished, - GRPC_ERROR_NONE, "send_trailing_metadata_finished"); - } - } - - if (now_writing) { - GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE( - exec_ctx, initial_metadata_writes); - GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(exec_ctx, message_writes); - GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE( - exec_ctx, trailing_metadata_writes); - GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(exec_ctx, - flow_control_writes); - - if (!grpc_chttp2_list_add_writing_stream(t, s)) { - /* already in writing list: drop ref */ - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:already_writing"); - } - } else { - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:no_write"); - } - } - - uint32_t transport_announce = - grpc_chttp2_flowctl_maybe_send_transport_update(&t->flow_control); - if (transport_announce) { - maybe_initiate_ping(exec_ctx, t, - GRPC_CHTTP2_PING_BEFORE_TRANSPORT_WINDOW_UPDATE); - grpc_transport_one_way_stats throwaway_stats; - grpc_slice_buffer_add( - &t->outbuf, grpc_chttp2_window_update_create(0, transport_announce, - &throwaway_stats)); - if (!t->is_client) { - t->ping_recv_state.last_ping_recv_time = - gpr_inf_past(GPR_CLOCK_MONOTONIC); - t->ping_recv_state.ping_strikes = 0; - } - } - - for (size_t i = 0; i < t->ping_ack_count; i++) { - grpc_slice_buffer_add(&t->outbuf, - grpc_chttp2_ping_create(1, t->ping_acks[i])); - } - t->ping_ack_count = 0; - - maybe_initiate_ping(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE); - - GPR_TIMER_END("grpc_chttp2_begin_write", 0); - - result.writing = t->outbuf.count > 0; - return result; -} - -void grpc_chttp2_end_write(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, - grpc_error *error) { - GPR_TIMER_BEGIN("grpc_chttp2_end_write", 0); - grpc_chttp2_stream *s; - - while (grpc_chttp2_list_pop_writing_stream(t, &s)) { - if (s->sending_bytes != 0) { - update_list(exec_ctx, t, s, (int64_t)s->sending_bytes, - &s->on_write_finished_cbs, &s->flow_controlled_bytes_written, - GRPC_ERROR_REF(error)); - s->sending_bytes = 0; - } - GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:end"); - } - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &t->outbuf); - GRPC_ERROR_UNREF(error); - GPR_TIMER_END("grpc_chttp2_end_write", 0); -} diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc new file mode 100644 index 0000000000..be1af16019 --- /dev/null +++ b/src/core/ext/transport/chttp2/transport/writing.cc @@ -0,0 +1,534 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/chttp2/transport/internal.h" + +#include + +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/http2_errors.h" + +static void add_to_write_list(grpc_chttp2_write_cb **list, + grpc_chttp2_write_cb *cb) { + cb->next = *list; + *list = cb; +} + +static void finish_write_cb(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, grpc_chttp2_write_cb *cb, + grpc_error *error) { + grpc_chttp2_complete_closure_step(exec_ctx, t, s, &cb->closure, error, + "finish_write_cb"); + cb->next = t->write_cb_pool; + t->write_cb_pool = cb; +} + +static void collapse_pings_from_into(grpc_chttp2_transport *t, + grpc_chttp2_ping_type ping_type, + grpc_chttp2_ping_queue *pq) { + for (size_t i = 0; i < GRPC_CHTTP2_PCL_COUNT; i++) { + grpc_closure_list_move(&t->ping_queues[ping_type].lists[i], &pq->lists[i]); + } +} + +static void maybe_initiate_ping(grpc_exec_ctx *exec_ctx, + grpc_chttp2_transport *t, + grpc_chttp2_ping_type ping_type) { + grpc_chttp2_ping_queue *pq = &t->ping_queues[ping_type]; + if (grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_NEXT])) { + /* no ping needed: wait */ + return; + } + if (!grpc_closure_list_empty(pq->lists[GRPC_CHTTP2_PCL_INFLIGHT])) { + /* ping already in-flight: wait */ + if (GRPC_TRACER_ON(grpc_http_trace) || + GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "Ping delayed [%p]: already pinging", t->peer_string); + } + return; + } + if (t->ping_state.pings_before_data_required == 0 && + t->ping_policy.max_pings_without_data != 0) { + /* need to receive something of substance before sending a ping again */ + if (GRPC_TRACER_ON(grpc_http_trace) || + GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "Ping delayed [%p]: too many recent pings: %d/%d", + t->peer_string, t->ping_state.pings_before_data_required, + t->ping_policy.max_pings_without_data); + } + return; + } + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec next_allowed_ping = + gpr_time_add(t->ping_state.last_ping_sent_time, + t->ping_policy.min_sent_ping_interval_without_data); + if (t->keepalive_permit_without_calls == 0 && + grpc_chttp2_stream_map_size(&t->stream_map) == 0) { + next_allowed_ping = gpr_time_add(t->ping_recv_state.last_ping_recv_time, + gpr_time_from_seconds(7200, GPR_TIMESPAN)); + } + /* gpr_log(GPR_DEBUG, "next_allowed_ping:%d.%09d now:%d.%09d", + (int)next_allowed_ping.tv_sec, (int)next_allowed_ping.tv_nsec, + (int)now.tv_sec, (int)now.tv_nsec); */ + if (gpr_time_cmp(next_allowed_ping, now) > 0) { + /* not enough elapsed time between successive pings */ + if (GRPC_TRACER_ON(grpc_http_trace) || + GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, + "Ping delayed [%p]: not enough time elapsed since last ping", + t->peer_string); + } + if (!t->ping_state.is_delayed_ping_timer_set) { + t->ping_state.is_delayed_ping_timer_set = true; + grpc_timer_init(exec_ctx, &t->ping_state.delayed_ping_timer, + next_allowed_ping, &t->retry_initiate_ping_locked, + gpr_now(GPR_CLOCK_MONOTONIC)); + } + return; + } + /* coalesce equivalent pings into this one */ + switch (ping_type) { + case GRPC_CHTTP2_PING_BEFORE_TRANSPORT_WINDOW_UPDATE: + collapse_pings_from_into(t, GRPC_CHTTP2_PING_ON_NEXT_WRITE, pq); + break; + case GRPC_CHTTP2_PING_ON_NEXT_WRITE: + break; + case GRPC_CHTTP2_PING_TYPE_COUNT: + GPR_UNREACHABLE_CODE(break); + } + pq->inflight_id = t->ping_ctr * GRPC_CHTTP2_PING_TYPE_COUNT + ping_type; + t->ping_ctr++; + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pq->lists[GRPC_CHTTP2_PCL_INITIATE]); + grpc_closure_list_move(&pq->lists[GRPC_CHTTP2_PCL_NEXT], + &pq->lists[GRPC_CHTTP2_PCL_INFLIGHT]); + grpc_slice_buffer_add(&t->outbuf, + grpc_chttp2_ping_create(false, pq->inflight_id)); + GRPC_STATS_INC_HTTP2_PINGS_SENT(exec_ctx); + t->ping_state.last_ping_sent_time = now; + if (GRPC_TRACER_ON(grpc_http_trace) || + GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "Ping sent [%p]: %d/%d", t->peer_string, + t->ping_state.pings_before_data_required, + t->ping_policy.max_pings_without_data); + } + t->ping_state.pings_before_data_required -= + (t->ping_state.pings_before_data_required != 0); +} + +static bool update_list(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_chttp2_stream *s, int64_t send_bytes, + grpc_chttp2_write_cb **list, int64_t *ctr, + grpc_error *error) { + bool sched_any = false; + grpc_chttp2_write_cb *cb = *list; + *list = NULL; + *ctr += send_bytes; + while (cb) { + grpc_chttp2_write_cb *next = cb->next; + if (cb->call_at_byte <= *ctr) { + sched_any = true; + finish_write_cb(exec_ctx, t, s, cb, GRPC_ERROR_REF(error)); + } else { + add_to_write_list(list, cb); + } + cb = next; + } + GRPC_ERROR_UNREF(error); + return sched_any; +} + +static bool stream_ref_if_not_destroyed(gpr_refcount *r) { + gpr_atm count; + do { + count = gpr_atm_acq_load(&r->count); + if (count == 0) return false; + } while (!gpr_atm_rel_cas(&r->count, count, count + 1)); + return true; +} + +/* How many bytes would we like to put on the wire during a single syscall */ +static uint32_t target_write_size(grpc_chttp2_transport *t) { + return 1024 * 1024; +} + +// Returns true if initial_metadata contains only default headers. +static bool is_default_initial_metadata(grpc_metadata_batch *initial_metadata) { + return initial_metadata->list.default_count == initial_metadata->list.count; +} + +grpc_chttp2_begin_write_result grpc_chttp2_begin_write( + grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t) { + grpc_chttp2_stream *s; + + /* stats histogram counters: we increment these throughout this function, + and at the end publish to the central stats histograms */ + int flow_control_writes = 0; + int initial_metadata_writes = 0; + int trailing_metadata_writes = 0; + int message_writes = 0; + + GRPC_STATS_INC_HTTP2_WRITES_BEGUN(exec_ctx); + + GPR_TIMER_BEGIN("grpc_chttp2_begin_write", 0); + + if (t->dirtied_local_settings && !t->sent_local_settings) { + grpc_slice_buffer_add( + &t->outbuf, + grpc_chttp2_settings_create( + t->settings[GRPC_SENT_SETTINGS], t->settings[GRPC_LOCAL_SETTINGS], + t->force_send_settings, GRPC_CHTTP2_NUM_SETTINGS)); + t->force_send_settings = 0; + t->dirtied_local_settings = 0; + t->sent_local_settings = 1; + GRPC_STATS_INC_HTTP2_SETTINGS_WRITES(exec_ctx); + } + + /* simple writes are queued to qbuf, and flushed here */ + grpc_slice_buffer_move_into(&t->qbuf, &t->outbuf); + GPR_ASSERT(t->qbuf.count == 0); + + grpc_chttp2_hpack_compressor_set_max_table_size( + &t->hpack_compressor, + t->settings[GRPC_PEER_SETTINGS][GRPC_CHTTP2_SETTINGS_HEADER_TABLE_SIZE]); + + if (t->flow_control.remote_window > 0) { + while (grpc_chttp2_list_pop_stalled_by_transport(t, &s)) { + if (!t->closed && grpc_chttp2_list_add_writable_stream(t, s)) { + stream_ref_if_not_destroyed(&s->refcount->refs); + } + } + } + + grpc_chttp2_begin_write_result result = {false, false, false}; + + /* for each grpc_chttp2_stream that's become writable, frame it's data + (according to available window sizes) and add to the output buffer */ + while (true) { + if (t->outbuf.length > target_write_size(t)) { + result.partial = true; + break; + } + + if (!grpc_chttp2_list_pop_writable_stream(t, &s)) { + break; + } + + bool sent_initial_metadata = s->sent_initial_metadata; + bool now_writing = false; + + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_DEBUG, "W:%p %s[%d] im-(sent,send)=(%d,%d) announce=%d", t, + t->is_client ? "CLIENT" : "SERVER", s->id, + sent_initial_metadata, s->send_initial_metadata != NULL, + (int)(s->flow_control.local_window_delta - + s->flow_control.announced_window_delta))); + + grpc_mdelem *extra_headers_for_trailing_metadata[2]; + size_t num_extra_headers_for_trailing_metadata = 0; + + /* send initial metadata if it's available */ + if (!sent_initial_metadata && s->send_initial_metadata != NULL) { + // We skip this on the server side if there is no custom initial + // metadata, there are no messages to send, and we are also sending + // trailing metadata. This results in a Trailers-Only response, + // which is required for retries, as per: + // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#when-retries-are-valid + if (t->is_client || s->fetching_send_message != NULL || + s->flow_controlled_buffer.length != 0 || + s->send_trailing_metadata == NULL || + !is_default_initial_metadata(s->send_initial_metadata)) { + grpc_encode_header_options hopt = { + .stream_id = s->id, + .is_eof = false, + .use_true_binary_metadata = + t->settings + [GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != 0, + .max_frame_size = t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + .stats = &s->stats.outgoing}; + grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, NULL, 0, + s->send_initial_metadata, &hopt, &t->outbuf); + now_writing = true; + if (!t->is_client) { + t->ping_recv_state.last_ping_recv_time = + gpr_inf_past(GPR_CLOCK_MONOTONIC); + t->ping_recv_state.ping_strikes = 0; + } + initial_metadata_writes++; + } else { + GRPC_CHTTP2_IF_TRACING( + gpr_log(GPR_INFO, "not sending initial_metadata (Trailers-Only)")); + // When sending Trailers-Only, we need to move the :status and + // content-type headers to the trailers. + if (s->send_initial_metadata->idx.named.status != NULL) { + extra_headers_for_trailing_metadata + [num_extra_headers_for_trailing_metadata++] = + &s->send_initial_metadata->idx.named.status->md; + } + if (s->send_initial_metadata->idx.named.content_type != NULL) { + extra_headers_for_trailing_metadata + [num_extra_headers_for_trailing_metadata++] = + &s->send_initial_metadata->idx.named.content_type->md; + } + trailing_metadata_writes++; + } + s->send_initial_metadata = NULL; + s->sent_initial_metadata = true; + sent_initial_metadata = true; + result.early_results_scheduled = true; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->send_initial_metadata_finished, GRPC_ERROR_NONE, + "send_initial_metadata_finished"); + } + /* send any window updates */ + uint32_t stream_announce = grpc_chttp2_flowctl_maybe_send_stream_update( + &t->flow_control, &s->flow_control); + if (stream_announce > 0) { + grpc_slice_buffer_add( + &t->outbuf, grpc_chttp2_window_update_create(s->id, stream_announce, + &s->stats.outgoing)); + if (!t->is_client) { + t->ping_recv_state.last_ping_recv_time = + gpr_inf_past(GPR_CLOCK_MONOTONIC); + t->ping_recv_state.ping_strikes = 0; + } + flow_control_writes++; + } + if (sent_initial_metadata) { + /* send any body bytes, if allowed by flow control */ + if (s->flow_controlled_buffer.length > 0 || + s->compressed_data_buffer.length > 0) { + uint32_t stream_remote_window = (uint32_t)GPR_MAX( + 0, + s->flow_control.remote_window_delta + + (int64_t)t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_INITIAL_WINDOW_SIZE]); + uint32_t max_outgoing = (uint32_t)GPR_MIN( + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + GPR_MIN(stream_remote_window, t->flow_control.remote_window)); + if (max_outgoing > 0) { + bool is_last_data_frame = false; + bool is_last_frame = false; + size_t sending_bytes_before = s->sending_bytes; + while ((s->flow_controlled_buffer.length > 0 || + s->compressed_data_buffer.length > 0) && + max_outgoing > 0) { + if (s->compressed_data_buffer.length > 0) { + uint32_t send_bytes = (uint32_t)GPR_MIN( + max_outgoing, s->compressed_data_buffer.length); + is_last_data_frame = + (send_bytes == s->compressed_data_buffer.length && + s->flow_controlled_buffer.length == 0 && + s->fetching_send_message == NULL); + if (is_last_data_frame && s->send_trailing_metadata != NULL && + s->stream_compression_ctx != NULL) { + if (!grpc_stream_compress( + s->stream_compression_ctx, &s->flow_controlled_buffer, + &s->compressed_data_buffer, NULL, MAX_SIZE_T, + GRPC_STREAM_COMPRESSION_FLUSH_FINISH)) { + gpr_log(GPR_ERROR, "Stream compression failed."); + } + grpc_stream_compression_context_destroy( + s->stream_compression_ctx); + s->stream_compression_ctx = NULL; + /* After finish, bytes in s->compressed_data_buffer may be + * more than max_outgoing. Start another round of the current + * while loop so that send_bytes and is_last_data_frame are + * recalculated. */ + continue; + } + is_last_frame = + is_last_data_frame && s->send_trailing_metadata != NULL && + grpc_metadata_batch_is_empty(s->send_trailing_metadata); + grpc_chttp2_encode_data(s->id, &s->compressed_data_buffer, + send_bytes, is_last_frame, + &s->stats.outgoing, &t->outbuf); + grpc_chttp2_flowctl_sent_data(&t->flow_control, &s->flow_control, + send_bytes); + max_outgoing -= send_bytes; + if (s->compressed_data_buffer.length == 0) { + s->sending_bytes += s->uncompressed_data_size; + } + } else { + if (s->stream_compression_ctx == NULL) { + s->stream_compression_ctx = + grpc_stream_compression_context_create( + s->stream_compression_method); + } + s->uncompressed_data_size = s->flow_controlled_buffer.length; + if (!grpc_stream_compress( + s->stream_compression_ctx, &s->flow_controlled_buffer, + &s->compressed_data_buffer, NULL, MAX_SIZE_T, + GRPC_STREAM_COMPRESSION_FLUSH_SYNC)) { + gpr_log(GPR_ERROR, "Stream compression failed."); + } + } + } + if (!t->is_client) { + t->ping_recv_state.last_ping_recv_time = + gpr_inf_past(GPR_CLOCK_MONOTONIC); + t->ping_recv_state.ping_strikes = 0; + } + if (is_last_frame) { + s->send_trailing_metadata = NULL; + s->sent_trailing_metadata = true; + if (!t->is_client && !s->read_closed) { + grpc_slice_buffer_add(&t->outbuf, grpc_chttp2_rst_stream_create( + s->id, GRPC_HTTP2_NO_ERROR, + &s->stats.outgoing)); + } + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, !t->is_client, 1, + GRPC_ERROR_NONE); + } + result.early_results_scheduled |= + update_list(exec_ctx, t, s, + (int64_t)(s->sending_bytes - sending_bytes_before), + &s->on_flow_controlled_cbs, + &s->flow_controlled_bytes_flowed, GRPC_ERROR_NONE); + now_writing = true; + if (s->flow_controlled_buffer.length > 0 || + s->compressed_data_buffer.length > 0) { + GRPC_CHTTP2_STREAM_REF(s, "chttp2_writing:fork"); + grpc_chttp2_list_add_writable_stream(t, s); + } + message_writes++; + } else if (t->flow_control.remote_window == 0) { + grpc_chttp2_list_add_stalled_by_transport(t, s); + now_writing = true; + } else if (stream_remote_window == 0) { + grpc_chttp2_list_add_stalled_by_stream(t, s); + now_writing = true; + } + } + if (s->send_trailing_metadata != NULL && + s->fetching_send_message == NULL && + s->flow_controlled_buffer.length == 0 && + s->compressed_data_buffer.length == 0) { + GRPC_CHTTP2_IF_TRACING(gpr_log(GPR_INFO, "sending trailing_metadata")); + if (grpc_metadata_batch_is_empty(s->send_trailing_metadata)) { + grpc_chttp2_encode_data(s->id, &s->flow_controlled_buffer, 0, true, + &s->stats.outgoing, &t->outbuf); + } else { + grpc_encode_header_options hopt = { + .stream_id = s->id, + .is_eof = true, + .use_true_binary_metadata = + t->settings + [GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != + 0, + .max_frame_size = + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + .stats = &s->stats.outgoing}; + grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, + extra_headers_for_trailing_metadata, + num_extra_headers_for_trailing_metadata, + s->send_trailing_metadata, &hopt, + &t->outbuf); + trailing_metadata_writes++; + } + s->send_trailing_metadata = NULL; + s->sent_trailing_metadata = true; + if (!t->is_client && !s->read_closed) { + grpc_slice_buffer_add( + &t->outbuf, grpc_chttp2_rst_stream_create( + s->id, GRPC_HTTP2_NO_ERROR, &s->stats.outgoing)); + } + grpc_chttp2_mark_stream_closed(exec_ctx, t, s, !t->is_client, 1, + GRPC_ERROR_NONE); + now_writing = true; + result.early_results_scheduled = true; + grpc_chttp2_complete_closure_step( + exec_ctx, t, s, &s->send_trailing_metadata_finished, + GRPC_ERROR_NONE, "send_trailing_metadata_finished"); + } + } + + if (now_writing) { + GRPC_STATS_INC_HTTP2_SEND_INITIAL_METADATA_PER_WRITE( + exec_ctx, initial_metadata_writes); + GRPC_STATS_INC_HTTP2_SEND_MESSAGE_PER_WRITE(exec_ctx, message_writes); + GRPC_STATS_INC_HTTP2_SEND_TRAILING_METADATA_PER_WRITE( + exec_ctx, trailing_metadata_writes); + GRPC_STATS_INC_HTTP2_SEND_FLOWCTL_PER_WRITE(exec_ctx, + flow_control_writes); + + if (!grpc_chttp2_list_add_writing_stream(t, s)) { + /* already in writing list: drop ref */ + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:already_writing"); + } + } else { + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:no_write"); + } + } + + uint32_t transport_announce = + grpc_chttp2_flowctl_maybe_send_transport_update(&t->flow_control); + if (transport_announce) { + maybe_initiate_ping(exec_ctx, t, + GRPC_CHTTP2_PING_BEFORE_TRANSPORT_WINDOW_UPDATE); + grpc_transport_one_way_stats throwaway_stats; + grpc_slice_buffer_add( + &t->outbuf, grpc_chttp2_window_update_create(0, transport_announce, + &throwaway_stats)); + if (!t->is_client) { + t->ping_recv_state.last_ping_recv_time = + gpr_inf_past(GPR_CLOCK_MONOTONIC); + t->ping_recv_state.ping_strikes = 0; + } + } + + for (size_t i = 0; i < t->ping_ack_count; i++) { + grpc_slice_buffer_add(&t->outbuf, + grpc_chttp2_ping_create(1, t->ping_acks[i])); + } + t->ping_ack_count = 0; + + maybe_initiate_ping(exec_ctx, t, GRPC_CHTTP2_PING_ON_NEXT_WRITE); + + GPR_TIMER_END("grpc_chttp2_begin_write", 0); + + result.writing = t->outbuf.count > 0; + return result; +} + +void grpc_chttp2_end_write(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, + grpc_error *error) { + GPR_TIMER_BEGIN("grpc_chttp2_end_write", 0); + grpc_chttp2_stream *s; + + while (grpc_chttp2_list_pop_writing_stream(t, &s)) { + if (s->sending_bytes != 0) { + update_list(exec_ctx, t, s, (int64_t)s->sending_bytes, + &s->on_write_finished_cbs, &s->flow_controlled_bytes_written, + GRPC_ERROR_REF(error)); + s->sending_bytes = 0; + } + GRPC_CHTTP2_STREAM_UNREF(exec_ctx, s, "chttp2_writing:end"); + } + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &t->outbuf); + GRPC_ERROR_UNREF(error); + GPR_TIMER_END("grpc_chttp2_end_write", 0); +} diff --git a/src/core/ext/transport/cronet/client/secure/cronet_channel_create.c b/src/core/ext/transport/cronet/client/secure/cronet_channel_create.c deleted file mode 100644 index 83365192e3..0000000000 --- a/src/core/ext/transport/cronet/client/secure/cronet_channel_create.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include -#include - -#include "src/core/ext/transport/cronet/transport/cronet_transport.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/transport/transport_impl.h" - -// Cronet transport object -typedef struct cronet_transport { - grpc_transport base; // must be first element in this structure - void *engine; - char *host; -} cronet_transport; - -extern grpc_transport_vtable grpc_cronet_vtable; - -GRPCAPI grpc_channel *grpc_cronet_secure_channel_create( - void *engine, const char *target, const grpc_channel_args *args, - void *reserved) { - gpr_log(GPR_DEBUG, - "grpc_create_cronet_transport: stream_engine = %p, target=%s", engine, - target); - - grpc_transport *ct = - grpc_create_cronet_transport(engine, target, args, reserved); - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - return grpc_channel_create(&exec_ctx, target, args, - GRPC_CLIENT_DIRECT_CHANNEL, ct); -} diff --git a/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc b/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc new file mode 100644 index 0000000000..83365192e3 --- /dev/null +++ b/src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc @@ -0,0 +1,53 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include + +#include "src/core/ext/transport/cronet/transport/cronet_transport.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/transport_impl.h" + +// Cronet transport object +typedef struct cronet_transport { + grpc_transport base; // must be first element in this structure + void *engine; + char *host; +} cronet_transport; + +extern grpc_transport_vtable grpc_cronet_vtable; + +GRPCAPI grpc_channel *grpc_cronet_secure_channel_create( + void *engine, const char *target, const grpc_channel_args *args, + void *reserved) { + gpr_log(GPR_DEBUG, + "grpc_create_cronet_transport: stream_engine = %p, target=%s", engine, + target); + + grpc_transport *ct = + grpc_create_cronet_transport(engine, target, args, reserved); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + return grpc_channel_create(&exec_ctx, target, args, + GRPC_CLIENT_DIRECT_CHANNEL, ct); +} diff --git a/src/core/ext/transport/cronet/transport/cronet_api_dummy.c b/src/core/ext/transport/cronet/transport/cronet_api_dummy.c deleted file mode 100644 index 4f248ad919..0000000000 --- a/src/core/ext/transport/cronet/transport/cronet_api_dummy.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* This file has empty implementation of all the functions exposed by the cronet -library, so we can build it in all environments */ - -#include - -#include - -#include "third_party/objective_c/Cronet/bidirectional_stream_c.h" - -#ifdef GRPC_COMPILE_WITH_CRONET -/* link with the real CRONET library in the build system */ -#else -/* Dummy implementation of cronet API just to test for build-ability */ -bidirectional_stream* bidirectional_stream_create( - stream_engine* engine, void* annotation, - bidirectional_stream_callback* callback) { - GPR_ASSERT(0); - return NULL; -} - -int bidirectional_stream_destroy(bidirectional_stream* stream) { - GPR_ASSERT(0); - return 0; -} - -int bidirectional_stream_start(bidirectional_stream* stream, const char* url, - int priority, const char* method, - const bidirectional_stream_header_array* headers, - bool end_of_stream) { - GPR_ASSERT(0); - return 0; -} - -int bidirectional_stream_read(bidirectional_stream* stream, char* buffer, - int capacity) { - GPR_ASSERT(0); - return 0; -} - -int bidirectional_stream_write(bidirectional_stream* stream, const char* buffer, - int count, bool end_of_stream) { - GPR_ASSERT(0); - return 0; -} - -void bidirectional_stream_cancel(bidirectional_stream* stream) { - GPR_ASSERT(0); -} - -void bidirectional_stream_disable_auto_flush(bidirectional_stream* stream, - bool disable_auto_flush) { - GPR_ASSERT(0); -} - -void bidirectional_stream_delay_request_headers_until_flush( - bidirectional_stream* stream, bool delay_headers_until_flush) { - GPR_ASSERT(0); -} - -void bidirectional_stream_flush(bidirectional_stream* stream) { GPR_ASSERT(0); } - -#endif /* GRPC_COMPILE_WITH_CRONET */ diff --git a/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc b/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc new file mode 100644 index 0000000000..4f248ad919 --- /dev/null +++ b/src/core/ext/transport/cronet/transport/cronet_api_dummy.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* This file has empty implementation of all the functions exposed by the cronet +library, so we can build it in all environments */ + +#include + +#include + +#include "third_party/objective_c/Cronet/bidirectional_stream_c.h" + +#ifdef GRPC_COMPILE_WITH_CRONET +/* link with the real CRONET library in the build system */ +#else +/* Dummy implementation of cronet API just to test for build-ability */ +bidirectional_stream* bidirectional_stream_create( + stream_engine* engine, void* annotation, + bidirectional_stream_callback* callback) { + GPR_ASSERT(0); + return NULL; +} + +int bidirectional_stream_destroy(bidirectional_stream* stream) { + GPR_ASSERT(0); + return 0; +} + +int bidirectional_stream_start(bidirectional_stream* stream, const char* url, + int priority, const char* method, + const bidirectional_stream_header_array* headers, + bool end_of_stream) { + GPR_ASSERT(0); + return 0; +} + +int bidirectional_stream_read(bidirectional_stream* stream, char* buffer, + int capacity) { + GPR_ASSERT(0); + return 0; +} + +int bidirectional_stream_write(bidirectional_stream* stream, const char* buffer, + int count, bool end_of_stream) { + GPR_ASSERT(0); + return 0; +} + +void bidirectional_stream_cancel(bidirectional_stream* stream) { + GPR_ASSERT(0); +} + +void bidirectional_stream_disable_auto_flush(bidirectional_stream* stream, + bool disable_auto_flush) { + GPR_ASSERT(0); +} + +void bidirectional_stream_delay_request_headers_until_flush( + bidirectional_stream* stream, bool delay_headers_until_flush) { + GPR_ASSERT(0); +} + +void bidirectional_stream_flush(bidirectional_stream* stream) { GPR_ASSERT(0); } + +#endif /* GRPC_COMPILE_WITH_CRONET */ diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c deleted file mode 100644 index 90f81a8c90..0000000000 --- a/src/core/ext/transport/cronet/transport/cronet_transport.c +++ /dev/null @@ -1,1495 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" -#include "src/core/lib/iomgr/endpoint.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/transport/metadata_batch.h" -#include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/transport_impl.h" -#include "third_party/objective_c/Cronet/bidirectional_stream_c.h" - -#define GRPC_HEADER_SIZE_IN_BYTES 5 -#define GRPC_FLUSH_READ_SIZE 4096 - -#define CRONET_LOG(...) \ - do { \ - if (grpc_cronet_trace) gpr_log(__VA_ARGS__); \ - } while (0) - -/* TODO (makdharma): Hook up into the wider tracing mechanism */ -int grpc_cronet_trace = 0; - -enum e_op_result { - ACTION_TAKEN_WITH_CALLBACK, - ACTION_TAKEN_NO_CALLBACK, - NO_ACTION_POSSIBLE -}; - -enum e_op_id { - OP_SEND_INITIAL_METADATA = 0, - OP_SEND_MESSAGE, - OP_SEND_TRAILING_METADATA, - OP_RECV_MESSAGE, - OP_RECV_INITIAL_METADATA, - OP_RECV_TRAILING_METADATA, - OP_CANCEL_ERROR, - OP_ON_COMPLETE, - OP_FAILED, - OP_SUCCEEDED, - OP_CANCELED, - OP_RECV_MESSAGE_AND_ON_COMPLETE, - OP_READ_REQ_MADE, - OP_NUM_OPS -}; - -/* Cronet callbacks. See cronet_c_for_grpc.h for documentation for each. */ - -static void on_stream_ready(bidirectional_stream *); -static void on_response_headers_received( - bidirectional_stream *, const bidirectional_stream_header_array *, - const char *); -static void on_write_completed(bidirectional_stream *, const char *); -static void on_read_completed(bidirectional_stream *, char *, int); -static void on_response_trailers_received( - bidirectional_stream *, const bidirectional_stream_header_array *); -static void on_succeeded(bidirectional_stream *); -static void on_failed(bidirectional_stream *, int); -static void on_canceled(bidirectional_stream *); -static bidirectional_stream_callback cronet_callbacks = { - on_stream_ready, - on_response_headers_received, - on_read_completed, - on_write_completed, - on_response_trailers_received, - on_succeeded, - on_failed, - on_canceled}; - -/* Cronet transport object */ -struct grpc_cronet_transport { - grpc_transport base; /* must be first element in this structure */ - stream_engine *engine; - char *host; - bool use_packet_coalescing; -}; -typedef struct grpc_cronet_transport grpc_cronet_transport; - -/* TODO (makdharma): reorder structure for memory efficiency per - http://www.catb.org/esr/structure-packing/#_structure_reordering: */ -struct read_state { - /* vars to store data coming from server */ - char *read_buffer; - bool length_field_received; - int received_bytes; - int remaining_bytes; - int length_field; - bool compressed; - char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES]; - char *payload_field; - bool read_stream_closed; - - /* vars for holding data destined for the application */ - struct grpc_slice_buffer_stream sbs; - grpc_slice_buffer read_slice_buffer; - - /* vars for trailing metadata */ - grpc_chttp2_incoming_metadata_buffer trailing_metadata; - bool trailing_metadata_valid; - - /* vars for initial metadata */ - grpc_chttp2_incoming_metadata_buffer initial_metadata; -}; - -struct write_state { - char *write_buffer; -}; - -/* track state of one stream op */ -struct op_state { - bool state_op_done[OP_NUM_OPS]; - bool state_callback_received[OP_NUM_OPS]; - /* A non-zero gRPC status code has been seen */ - bool fail_state; - /* Transport is discarding all buffered messages */ - bool flush_read; - bool flush_cronet_when_ready; - bool pending_write_for_trailer; - bool pending_send_message; - /* User requested RECV_TRAILING_METADATA */ - bool pending_recv_trailing_metadata; - /* Cronet has not issued a callback of a bidirectional read */ - bool pending_read_from_cronet; - grpc_error *cancel_error; - /* data structure for storing data coming from server */ - struct read_state rs; - /* data structure for storing data going to the server */ - struct write_state ws; -}; - -struct op_and_state { - grpc_transport_stream_op_batch op; - struct op_state state; - bool done; - struct stream_obj *s; /* Pointer back to the stream object */ - struct op_and_state *next; /* next op_and_state in the linked list */ -}; - -struct op_storage { - int num_pending_ops; - struct op_and_state *head; -}; - -struct stream_obj { - gpr_arena *arena; - struct op_and_state *oas; - grpc_transport_stream_op_batch *curr_op; - grpc_cronet_transport *curr_ct; - grpc_stream *curr_gs; - bidirectional_stream *cbs; - bidirectional_stream_header_array header_array; - - /* Stream level state. Some state will be tracked both at stream and stream_op - * level */ - struct op_state state; - - /* OP storage */ - struct op_storage storage; - - /* Mutex to protect storage */ - gpr_mu mu; - - /* Refcount object of the stream */ - grpc_stream_refcount *refcount; -}; -typedef struct stream_obj stream_obj; - -#ifndef NDEBUG -#define GRPC_CRONET_STREAM_REF(stream, reason) \ - grpc_cronet_stream_ref((stream), (reason)) -#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \ - grpc_cronet_stream_unref((exec_ctx), (stream), (reason)) -void grpc_cronet_stream_ref(stream_obj *s, const char *reason) { - grpc_stream_ref(s->refcount, reason); -} -void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s, - const char *reason) { - grpc_stream_unref(exec_ctx, s->refcount, reason); -} -#else -#define GRPC_CRONET_STREAM_REF(stream, reason) grpc_cronet_stream_ref((stream)) -#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \ - grpc_cronet_stream_unref((exec_ctx), (stream)) -void grpc_cronet_stream_ref(stream_obj *s) { grpc_stream_ref(s->refcount); } -void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s) { - grpc_stream_unref(exec_ctx, s->refcount); -} -#endif - -static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx, - struct op_and_state *oas); - -/* - Utility function to translate enum into string for printing -*/ -static const char *op_result_string(enum e_op_result i) { - switch (i) { - case ACTION_TAKEN_WITH_CALLBACK: - return "ACTION_TAKEN_WITH_CALLBACK"; - case ACTION_TAKEN_NO_CALLBACK: - return "ACTION_TAKEN_NO_CALLBACK"; - case NO_ACTION_POSSIBLE: - return "NO_ACTION_POSSIBLE"; - } - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -static const char *op_id_string(enum e_op_id i) { - switch (i) { - case OP_SEND_INITIAL_METADATA: - return "OP_SEND_INITIAL_METADATA"; - case OP_SEND_MESSAGE: - return "OP_SEND_MESSAGE"; - case OP_SEND_TRAILING_METADATA: - return "OP_SEND_TRAILING_METADATA"; - case OP_RECV_MESSAGE: - return "OP_RECV_MESSAGE"; - case OP_RECV_INITIAL_METADATA: - return "OP_RECV_INITIAL_METADATA"; - case OP_RECV_TRAILING_METADATA: - return "OP_RECV_TRAILING_METADATA"; - case OP_CANCEL_ERROR: - return "OP_CANCEL_ERROR"; - case OP_ON_COMPLETE: - return "OP_ON_COMPLETE"; - case OP_FAILED: - return "OP_FAILED"; - case OP_SUCCEEDED: - return "OP_SUCCEEDED"; - case OP_CANCELED: - return "OP_CANCELED"; - case OP_RECV_MESSAGE_AND_ON_COMPLETE: - return "OP_RECV_MESSAGE_AND_ON_COMPLETE"; - case OP_READ_REQ_MADE: - return "OP_READ_REQ_MADE"; - case OP_NUM_OPS: - return "OP_NUM_OPS"; - } - return "UNKNOWN"; -} - -static void null_and_maybe_free_read_buffer(stream_obj *s) { - if (s->state.rs.read_buffer && - s->state.rs.read_buffer != s->state.rs.grpc_header_bytes) { - gpr_free(s->state.rs.read_buffer); - } - s->state.rs.read_buffer = NULL; -} - -static void maybe_flush_read(stream_obj *s) { - /* To enter flush read state (discarding all the buffered messages in - * transport layer), two conditions must be satisfied: 1) non-zero grpc status - * has been received, and 2) an op requesting the status code - * (RECV_TRAILING_METADATA) is issued by the user. (See - * doc/status_ordering.md) */ - /* Whenever the evaluation of any of the two condition is changed, we check - * whether we should enter the flush read state. */ - if (s->state.pending_recv_trailing_metadata && s->state.fail_state) { - if (!s->state.flush_read && !s->state.rs.read_stream_closed) { - CRONET_LOG(GPR_DEBUG, "%p: Flush read", s); - s->state.flush_read = true; - null_and_maybe_free_read_buffer(s); - s->state.rs.read_buffer = (char *)gpr_malloc(GRPC_FLUSH_READ_SIZE); - if (!s->state.pending_read_from_cronet) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, - GRPC_FLUSH_READ_SIZE); - s->state.pending_read_from_cronet = true; - } - } - } -} - -static grpc_error *make_error_with_desc(int error_code, const char *desc) { - grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); - error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, error_code); - return error; -} - -/* - Add a new stream op to op storage. -*/ -static void add_to_storage(struct stream_obj *s, - grpc_transport_stream_op_batch *op) { - struct op_storage *storage = &s->storage; - /* add new op at the beginning of the linked list. The memory is freed - in remove_from_storage */ - struct op_and_state *new_op = - (struct op_and_state *)gpr_malloc(sizeof(struct op_and_state)); - memcpy(&new_op->op, op, sizeof(grpc_transport_stream_op_batch)); - memset(&new_op->state, 0, sizeof(new_op->state)); - new_op->s = s; - new_op->done = false; - gpr_mu_lock(&s->mu); - new_op->next = storage->head; - storage->head = new_op; - storage->num_pending_ops++; - if (op->send_message) { - s->state.pending_send_message = true; - } - if (op->recv_trailing_metadata) { - s->state.pending_recv_trailing_metadata = true; - maybe_flush_read(s); - } - CRONET_LOG(GPR_DEBUG, "adding new op %p. %d in the queue.", new_op, - storage->num_pending_ops); - gpr_mu_unlock(&s->mu); -} - -/* - Traverse the linked list and delete op and free memory -*/ -static void remove_from_storage(struct stream_obj *s, - struct op_and_state *oas) { - struct op_and_state *curr; - if (s->storage.head == NULL || oas == NULL) { - return; - } - if (s->storage.head == oas) { - s->storage.head = oas->next; - gpr_free(oas); - s->storage.num_pending_ops--; - CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas, - s->storage.num_pending_ops); - } else { - for (curr = s->storage.head; curr != NULL; curr = curr->next) { - if (curr->next == oas) { - curr->next = oas->next; - s->storage.num_pending_ops--; - CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas, - s->storage.num_pending_ops); - gpr_free(oas); - break; - } else if (curr->next == NULL) { - CRONET_LOG(GPR_ERROR, "Reached end of LL and did not find op to free"); - } - } - } -} - -/* - Cycle through ops and try to take next action. Break when either - an action with callback is taken, or no action is possible. - This can get executed from the Cronet network thread via cronet callback - or on the application supplied thread via the perform_stream_op function. -*/ -static void execute_from_storage(grpc_exec_ctx *exec_ctx, stream_obj *s) { - gpr_mu_lock(&s->mu); - for (struct op_and_state *curr = s->storage.head; curr != NULL;) { - CRONET_LOG(GPR_DEBUG, "calling op at %p. done = %d", curr, curr->done); - GPR_ASSERT(curr->done == 0); - enum e_op_result result = execute_stream_op(exec_ctx, curr); - CRONET_LOG(GPR_DEBUG, "execute_stream_op[%p] returns %s", curr, - op_result_string(result)); - /* if this op is done, then remove it and free memory */ - if (curr->done) { - struct op_and_state *next = curr->next; - remove_from_storage(s, curr); - curr = next; - } - /* continue processing the same op if ACTION_TAKEN_WITHOUT_CALLBACK */ - if (result == NO_ACTION_POSSIBLE) { - curr = curr->next; - } else if (result == ACTION_TAKEN_WITH_CALLBACK) { - break; - } - } - gpr_mu_unlock(&s->mu); -} - -/* - Cronet callback -*/ -static void on_failed(bidirectional_stream *stream, int net_error) { - CRONET_LOG(GPR_DEBUG, "on_failed(%p, %d)", stream, net_error); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - stream_obj *s = (stream_obj *)stream->annotation; - gpr_mu_lock(&s->mu); - bidirectional_stream_destroy(s->cbs); - s->state.state_callback_received[OP_FAILED] = true; - s->cbs = NULL; - if (s->header_array.headers) { - gpr_free(s->header_array.headers); - s->header_array.headers = NULL; - } - if (s->state.ws.write_buffer) { - gpr_free(s->state.ws.write_buffer); - s->state.ws.write_buffer = NULL; - } - null_and_maybe_free_read_buffer(s); - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_canceled(bidirectional_stream *stream) { - CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - stream_obj *s = (stream_obj *)stream->annotation; - gpr_mu_lock(&s->mu); - bidirectional_stream_destroy(s->cbs); - s->state.state_callback_received[OP_CANCELED] = true; - s->cbs = NULL; - if (s->header_array.headers) { - gpr_free(s->header_array.headers); - s->header_array.headers = NULL; - } - if (s->state.ws.write_buffer) { - gpr_free(s->state.ws.write_buffer); - s->state.ws.write_buffer = NULL; - } - null_and_maybe_free_read_buffer(s); - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_succeeded(bidirectional_stream *stream) { - CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - stream_obj *s = (stream_obj *)stream->annotation; - gpr_mu_lock(&s->mu); - bidirectional_stream_destroy(s->cbs); - s->state.state_callback_received[OP_SUCCEEDED] = true; - s->cbs = NULL; - null_and_maybe_free_read_buffer(s); - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_stream_ready(bidirectional_stream *stream) { - CRONET_LOG(GPR_DEBUG, "W: on_stream_ready(%p)", stream); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - stream_obj *s = (stream_obj *)stream->annotation; - grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; - gpr_mu_lock(&s->mu); - s->state.state_op_done[OP_SEND_INITIAL_METADATA] = true; - s->state.state_callback_received[OP_SEND_INITIAL_METADATA] = true; - /* Free the memory allocated for headers */ - if (s->header_array.headers) { - gpr_free(s->header_array.headers); - s->header_array.headers = NULL; - } - /* Send the initial metadata on wire if there is no SEND_MESSAGE or - * SEND_TRAILING_METADATA ops pending */ - if (t->use_packet_coalescing) { - if (s->state.flush_cronet_when_ready) { - CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_flush (%p)", s->cbs); - bidirectional_stream_flush(stream); - } - } - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_response_headers_received( - bidirectional_stream *stream, - const bidirectional_stream_header_array *headers, - const char *negotiated_protocol) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - CRONET_LOG(GPR_DEBUG, "R: on_response_headers_received(%p, %p, %s)", stream, - headers, negotiated_protocol); - stream_obj *s = (stream_obj *)stream->annotation; - - /* Identify if this is a header or a trailer (in a trailer-only response case) - */ - for (size_t i = 0; i < headers->count; i++) { - if (0 == strcmp("grpc-status", headers->headers[i].key)) { - on_response_trailers_received(stream, headers); - return; - } - } - - gpr_mu_lock(&s->mu); - memset(&s->state.rs.initial_metadata, 0, - sizeof(s->state.rs.initial_metadata)); - grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.initial_metadata, - s->arena); - for (size_t i = 0; i < headers->count; i++) { - GRPC_LOG_IF_ERROR( - "on_response_headers_received", - grpc_chttp2_incoming_metadata_buffer_add( - &exec_ctx, &s->state.rs.initial_metadata, - grpc_mdelem_from_slices( - &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string( - headers->headers[i].key)), - grpc_slice_intern(grpc_slice_from_static_string( - headers->headers[i].value))))); - } - s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true; - if (!(s->state.state_op_done[OP_CANCEL_ERROR] || - s->state.state_callback_received[OP_FAILED])) { - /* Do an extra read to trigger on_succeeded() callback in case connection - is closed */ - GPR_ASSERT(s->state.rs.length_field_received == false); - s->state.rs.read_buffer = s->state.rs.grpc_header_bytes; - s->state.rs.compressed = false; - s->state.rs.received_bytes = 0; - s->state.rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, - s->state.rs.remaining_bytes); - s->state.pending_read_from_cronet = true; - } - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_write_completed(bidirectional_stream *stream, const char *data) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - stream_obj *s = (stream_obj *)stream->annotation; - CRONET_LOG(GPR_DEBUG, "W: on_write_completed(%p, %s)", stream, data); - gpr_mu_lock(&s->mu); - if (s->state.ws.write_buffer) { - gpr_free(s->state.ws.write_buffer); - s->state.ws.write_buffer = NULL; - } - s->state.state_callback_received[OP_SEND_MESSAGE] = true; - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_read_completed(bidirectional_stream *stream, char *data, - int count) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - stream_obj *s = (stream_obj *)stream->annotation; - CRONET_LOG(GPR_DEBUG, "R: on_read_completed(%p, %p, %d)", stream, data, - count); - gpr_mu_lock(&s->mu); - s->state.pending_read_from_cronet = false; - s->state.state_callback_received[OP_RECV_MESSAGE] = true; - if (count > 0 && s->state.flush_read) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, - GRPC_FLUSH_READ_SIZE); - s->state.pending_read_from_cronet = true; - gpr_mu_unlock(&s->mu); - } else if (count > 0) { - s->state.rs.received_bytes += count; - s->state.rs.remaining_bytes -= count; - if (s->state.rs.remaining_bytes > 0) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - s->state.state_op_done[OP_READ_REQ_MADE] = true; - bidirectional_stream_read( - s->cbs, s->state.rs.read_buffer + s->state.rs.received_bytes, - s->state.rs.remaining_bytes); - s->state.pending_read_from_cronet = true; - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - } - } else { - null_and_maybe_free_read_buffer(s); - s->state.rs.read_stream_closed = true; - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - } - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Cronet callback -*/ -static void on_response_trailers_received( - bidirectional_stream *stream, - const bidirectional_stream_header_array *trailers) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - CRONET_LOG(GPR_DEBUG, "R: on_response_trailers_received(%p,%p)", stream, - trailers); - stream_obj *s = (stream_obj *)stream->annotation; - grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; - gpr_mu_lock(&s->mu); - memset(&s->state.rs.trailing_metadata, 0, - sizeof(s->state.rs.trailing_metadata)); - s->state.rs.trailing_metadata_valid = false; - grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.trailing_metadata, - s->arena); - for (size_t i = 0; i < trailers->count; i++) { - CRONET_LOG(GPR_DEBUG, "trailer key=%s, value=%s", trailers->headers[i].key, - trailers->headers[i].value); - GRPC_LOG_IF_ERROR( - "on_response_trailers_received", - grpc_chttp2_incoming_metadata_buffer_add( - &exec_ctx, &s->state.rs.trailing_metadata, - grpc_mdelem_from_slices( - &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string( - trailers->headers[i].key)), - grpc_slice_intern(grpc_slice_from_static_string( - trailers->headers[i].value))))); - s->state.rs.trailing_metadata_valid = true; - if (0 == strcmp(trailers->headers[i].key, "grpc-status") && - 0 != strcmp(trailers->headers[i].value, "0")) { - s->state.fail_state = true; - maybe_flush_read(s); - } - } - s->state.state_callback_received[OP_RECV_TRAILING_METADATA] = true; - /* Send a EOS when server terminates the stream (testServerFinishesRequest) to - * trigger on_succeeded */ - if (!s->state.state_op_done[OP_SEND_TRAILING_METADATA] && - !(s->state.state_op_done[OP_CANCEL_ERROR] || - s->state.state_callback_received[OP_FAILED])) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs); - s->state.state_callback_received[OP_SEND_MESSAGE] = false; - bidirectional_stream_write(s->cbs, "", 0, true); - if (t->use_packet_coalescing) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); - bidirectional_stream_flush(s->cbs); - } - s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true; - - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - execute_from_storage(&exec_ctx, s); - } - grpc_exec_ctx_finish(&exec_ctx); -} - -/* - Utility function that takes the data from s->write_slice_buffer and assembles - into a contiguous byte stream with 5 byte gRPC header prepended. -*/ -static void create_grpc_frame(grpc_exec_ctx *exec_ctx, - grpc_slice_buffer *write_slice_buffer, - char **pp_write_buffer, - size_t *p_write_buffer_size, uint32_t flags) { - grpc_slice slice = grpc_slice_buffer_take_first(write_slice_buffer); - size_t length = GRPC_SLICE_LENGTH(slice); - *p_write_buffer_size = length + GRPC_HEADER_SIZE_IN_BYTES; - /* This is freed in the on_write_completed callback */ - char *write_buffer = (char *)gpr_malloc(length + GRPC_HEADER_SIZE_IN_BYTES); - *pp_write_buffer = write_buffer; - uint8_t *p = (uint8_t *)write_buffer; - /* Append 5 byte header */ - /* Compressed flag */ - *p++ = (flags & GRPC_WRITE_INTERNAL_COMPRESS) ? 1 : 0; - /* Message length */ - *p++ = (uint8_t)(length >> 24); - *p++ = (uint8_t)(length >> 16); - *p++ = (uint8_t)(length >> 8); - *p++ = (uint8_t)(length); - /* append actual data */ - memcpy(p, GRPC_SLICE_START_PTR(slice), length); - grpc_slice_unref_internal(exec_ctx, slice); -} - -/* - Convert metadata in a format that Cronet can consume -*/ -static void convert_metadata_to_cronet_headers( - grpc_linked_mdelem *head, const char *host, char **pp_url, - bidirectional_stream_header **pp_headers, size_t *p_num_headers, - const char **method) { - grpc_linked_mdelem *curr = head; - /* Walk the linked list and get number of header fields */ - size_t num_headers_available = 0; - while (curr != NULL) { - curr = curr->next; - num_headers_available++; - } - /* Allocate enough memory. It is freed in the on_stream_ready callback - */ - bidirectional_stream_header *headers = - (bidirectional_stream_header *)gpr_malloc( - sizeof(bidirectional_stream_header) * num_headers_available); - *pp_headers = headers; - - /* Walk the linked list again, this time copying the header fields. - s->num_headers can be less than num_headers_available, as some headers - are not used for cronet. - TODO (makdharma): Eliminate need to traverse the LL second time for perf. - */ - curr = head; - size_t num_headers = 0; - while (num_headers < num_headers_available) { - grpc_mdelem mdelem = curr->md; - curr = curr->next; - char *key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem)); - char *value = grpc_slice_to_c_string(GRPC_MDVALUE(mdelem)); - if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_SCHEME) || - grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_AUTHORITY)) { - /* Cronet populates these fields on its own */ - gpr_free(key); - gpr_free(value); - continue; - } - if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_METHOD)) { - if (grpc_slice_eq(GRPC_MDVALUE(mdelem), GRPC_MDSTR_PUT)) { - *method = "PUT"; - } else { - /* POST method in default*/ - *method = "POST"; - } - gpr_free(key); - gpr_free(value); - continue; - } - if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_PATH)) { - /* Create URL by appending :path value to the hostname */ - gpr_asprintf(pp_url, "https://%s%s", host, value); - gpr_free(key); - gpr_free(value); - continue; - } - CRONET_LOG(GPR_DEBUG, "header %s = %s", key, value); - headers[num_headers].key = key; - headers[num_headers].value = value; - num_headers++; - if (curr == NULL) { - break; - } - } - *p_num_headers = (size_t)num_headers; -} - -static void parse_grpc_header(const uint8_t *data, int *length, - bool *compressed) { - const uint8_t c = *data; - const uint8_t *p = data + 1; - *compressed = ((c & 0x01) == 0x01); - *length = 0; - *length |= ((uint8_t)*p++) << 24; - *length |= ((uint8_t)*p++) << 16; - *length |= ((uint8_t)*p++) << 8; - *length |= ((uint8_t)*p++); -} - -static bool header_has_authority(grpc_linked_mdelem *head) { - while (head != NULL) { - if (grpc_slice_eq(GRPC_MDKEY(head->md), GRPC_MDSTR_AUTHORITY)) { - return true; - } - head = head->next; - } - return false; -} - -/* - Op Execution: Decide if one of the actions contained in the stream op can be - executed. This is the heart of the state machine. -*/ -static bool op_can_be_run(grpc_transport_stream_op_batch *curr_op, - struct stream_obj *s, struct op_state *op_state, - enum e_op_id op_id) { - struct op_state *stream_state = &s->state; - grpc_cronet_transport *t = s->curr_ct; - bool result = true; - /* When call is canceled, every op can be run, except under following - conditions - */ - bool is_canceled_or_failed = stream_state->state_op_done[OP_CANCEL_ERROR] || - stream_state->state_callback_received[OP_FAILED]; - if (is_canceled_or_failed) { - if (op_id == OP_SEND_INITIAL_METADATA) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - if (op_id == OP_SEND_MESSAGE) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - if (op_id == OP_SEND_TRAILING_METADATA) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - if (op_id == OP_CANCEL_ERROR) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - /* already executed */ - if (op_id == OP_RECV_INITIAL_METADATA && - stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - if (op_id == OP_RECV_MESSAGE && op_state->state_op_done[OP_RECV_MESSAGE]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - if (op_id == OP_RECV_TRAILING_METADATA && - stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - /* ON_COMPLETE can be processed if one of the following conditions is met: - * 1. the stream failed - * 2. the stream is cancelled, and the callback is received - * 3. the stream succeeded before cancel is effective - * 4. the stream is cancelled, and the stream is never started */ - if (op_id == OP_ON_COMPLETE && - !(stream_state->state_callback_received[OP_FAILED] || - stream_state->state_callback_received[OP_CANCELED] || - stream_state->state_callback_received[OP_SUCCEEDED] || - !stream_state->state_op_done[OP_SEND_INITIAL_METADATA])) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - } else if (op_id == OP_SEND_INITIAL_METADATA) { - /* already executed */ - if (stream_state->state_op_done[OP_SEND_INITIAL_METADATA]) result = false; - } else if (op_id == OP_RECV_INITIAL_METADATA) { - /* already executed */ - if (stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) result = false; - /* we haven't sent headers yet. */ - else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) - result = false; - /* we haven't received headers yet. */ - else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA] && - !stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) - result = false; - } else if (op_id == OP_SEND_MESSAGE) { - /* already executed (note we're checking op specific state, not stream - state) */ - if (op_state->state_op_done[OP_SEND_MESSAGE]) result = false; - /* we haven't sent headers yet. */ - else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) - result = false; - } else if (op_id == OP_RECV_MESSAGE) { - /* already executed */ - if (op_state->state_op_done[OP_RECV_MESSAGE]) result = false; - /* we haven't received headers yet. */ - else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA] && - !stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) - result = false; - } else if (op_id == OP_RECV_TRAILING_METADATA) { - /* already executed */ - if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) result = false; - /* we have asked for but haven't received message yet. */ - else if (stream_state->state_op_done[OP_READ_REQ_MADE] && - !stream_state->state_op_done[OP_RECV_MESSAGE]) - result = false; - /* we haven't received trailers yet. */ - else if (!stream_state->state_callback_received[OP_RECV_TRAILING_METADATA]) - result = false; - /* we haven't received on_succeeded yet. */ - else if (!stream_state->state_callback_received[OP_SUCCEEDED]) - result = false; - } else if (op_id == OP_SEND_TRAILING_METADATA) { - /* already executed */ - if (stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) result = false; - /* we haven't sent initial metadata yet */ - else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) - result = false; - /* we haven't sent message yet */ - else if (stream_state->pending_send_message && - !stream_state->state_op_done[OP_SEND_MESSAGE]) - result = false; - /* we haven't got on_write_completed for the send yet */ - else if (stream_state->state_op_done[OP_SEND_MESSAGE] && - !stream_state->state_callback_received[OP_SEND_MESSAGE] && - !(t->use_packet_coalescing && - stream_state->pending_write_for_trailer)) - result = false; - } else if (op_id == OP_CANCEL_ERROR) { - /* already executed */ - if (stream_state->state_op_done[OP_CANCEL_ERROR]) result = false; - } else if (op_id == OP_ON_COMPLETE) { - /* already executed (note we're checking op specific state, not stream - state) */ - if (op_state->state_op_done[OP_ON_COMPLETE]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - /* Check if every op that was asked for is done. */ - else if (curr_op->send_initial_metadata && - !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->send_message && - !op_state->state_op_done[OP_SEND_MESSAGE]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->send_message && - !stream_state->state_callback_received[OP_SEND_MESSAGE]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->send_trailing_metadata && - !stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->recv_initial_metadata && - !stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->recv_message && - !op_state->state_op_done[OP_RECV_MESSAGE]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->cancel_stream && - !stream_state->state_callback_received[OP_CANCELED]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } else if (curr_op->recv_trailing_metadata) { - /* We aren't done with trailing metadata yet */ - if (!stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - /* We've asked for actual message in an earlier op, and it hasn't been - delivered yet. */ - else if (stream_state->state_op_done[OP_READ_REQ_MADE]) { - /* If this op is not the one asking for read, (which means some earlier - op has asked), and the read hasn't been delivered. */ - if (!curr_op->recv_message && - !stream_state->state_callback_received[OP_SUCCEEDED]) { - CRONET_LOG(GPR_DEBUG, "Because"); - result = false; - } - } - } - /* We should see at least one on_write_completed for the trailers that we - sent */ - else if (curr_op->send_trailing_metadata && - !stream_state->state_callback_received[OP_SEND_MESSAGE]) - result = false; - } - CRONET_LOG(GPR_DEBUG, "op_can_be_run %s : %s", op_id_string(op_id), - result ? "YES" : "NO"); - return result; -} - -/* - TODO (makdharma): Break down this function in smaller chunks for readability. -*/ -static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx, - struct op_and_state *oas) { - grpc_transport_stream_op_batch *stream_op = &oas->op; - struct stream_obj *s = oas->s; - grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; - struct op_state *stream_state = &s->state; - enum e_op_result result = NO_ACTION_POSSIBLE; - if (stream_op->send_initial_metadata && - op_can_be_run(stream_op, s, &oas->state, OP_SEND_INITIAL_METADATA)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_INITIAL_METADATA", oas); - /* Start new cronet stream. It is destroyed in on_succeeded, on_canceled, - * on_failed */ - GPR_ASSERT(s->cbs == NULL); - GPR_ASSERT(!stream_state->state_op_done[OP_SEND_INITIAL_METADATA]); - s->cbs = - bidirectional_stream_create(t->engine, s->curr_gs, &cronet_callbacks); - CRONET_LOG(GPR_DEBUG, "%p = bidirectional_stream_create()", s->cbs); - if (t->use_packet_coalescing) { - bidirectional_stream_disable_auto_flush(s->cbs, true); - bidirectional_stream_delay_request_headers_until_flush(s->cbs, true); - } - char *url = NULL; - const char *method = "POST"; - s->header_array.headers = NULL; - convert_metadata_to_cronet_headers(stream_op->payload->send_initial_metadata - .send_initial_metadata->list.head, - t->host, &url, &s->header_array.headers, - &s->header_array.count, &method); - s->header_array.capacity = s->header_array.count; - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_start(%p, %s)", s->cbs, url); - bidirectional_stream_start(s->cbs, url, 0, method, &s->header_array, false); - if (url) { - gpr_free(url); - } - unsigned int header_index; - for (header_index = 0; header_index < s->header_array.count; - header_index++) { - gpr_free((void *)s->header_array.headers[header_index].key); - gpr_free((void *)s->header_array.headers[header_index].value); - } - stream_state->state_op_done[OP_SEND_INITIAL_METADATA] = true; - if (t->use_packet_coalescing) { - if (!stream_op->send_message && !stream_op->send_trailing_metadata) { - s->state.flush_cronet_when_ready = true; - } - } - result = ACTION_TAKEN_WITH_CALLBACK; - } else if (stream_op->send_message && - op_can_be_run(stream_op, s, &oas->state, OP_SEND_MESSAGE)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_MESSAGE", oas); - stream_state->pending_send_message = false; - if (stream_state->state_callback_received[OP_FAILED]) { - result = NO_ACTION_POSSIBLE; - CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed."); - } else { - grpc_slice_buffer write_slice_buffer; - grpc_slice slice; - grpc_slice_buffer_init(&write_slice_buffer); - if (1 != grpc_byte_stream_next( - exec_ctx, stream_op->payload->send_message.send_message, - stream_op->payload->send_message.send_message->length, - NULL)) { - /* Should never reach here */ - GPR_ASSERT(false); - } - if (GRPC_ERROR_NONE != - grpc_byte_stream_pull(exec_ctx, - stream_op->payload->send_message.send_message, - &slice)) { - /* Should never reach here */ - GPR_ASSERT(false); - } - grpc_slice_buffer_add(&write_slice_buffer, slice); - if (write_slice_buffer.count != 1) { - /* Empty request not handled yet */ - gpr_log(GPR_ERROR, "Empty request is not supported"); - GPR_ASSERT(write_slice_buffer.count == 1); - } - if (write_slice_buffer.count > 0) { - size_t write_buffer_size; - create_grpc_frame(exec_ctx, &write_slice_buffer, - &stream_state->ws.write_buffer, &write_buffer_size, - stream_op->payload->send_message.send_message->flags); - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs, - stream_state->ws.write_buffer); - stream_state->state_callback_received[OP_SEND_MESSAGE] = false; - bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer, - (int)write_buffer_size, false); - grpc_slice_buffer_destroy_internal(exec_ctx, &write_slice_buffer); - if (t->use_packet_coalescing) { - if (!stream_op->send_trailing_metadata) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); - bidirectional_stream_flush(s->cbs); - result = ACTION_TAKEN_WITH_CALLBACK; - } else { - stream_state->pending_write_for_trailer = true; - result = ACTION_TAKEN_NO_CALLBACK; - } - } else { - result = ACTION_TAKEN_WITH_CALLBACK; - } - } else { - result = NO_ACTION_POSSIBLE; - } - } - stream_state->state_op_done[OP_SEND_MESSAGE] = true; - oas->state.state_op_done[OP_SEND_MESSAGE] = true; - } else if (stream_op->send_trailing_metadata && - op_can_be_run(stream_op, s, &oas->state, - OP_SEND_TRAILING_METADATA)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_TRAILING_METADATA", oas); - if (stream_state->state_callback_received[OP_FAILED]) { - result = NO_ACTION_POSSIBLE; - CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed."); - } else { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs); - stream_state->state_callback_received[OP_SEND_MESSAGE] = false; - bidirectional_stream_write(s->cbs, "", 0, true); - if (t->use_packet_coalescing) { - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); - bidirectional_stream_flush(s->cbs); - } - result = ACTION_TAKEN_WITH_CALLBACK; - } - stream_state->state_op_done[OP_SEND_TRAILING_METADATA] = true; - } else if (stream_op->recv_initial_metadata && - op_can_be_run(stream_op, s, &oas->state, - OP_RECV_INITIAL_METADATA)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_INITIAL_METADATA", oas); - if (stream_state->state_op_done[OP_CANCEL_ERROR]) { - GRPC_CLOSURE_SCHED( - exec_ctx, - stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_NONE); - } else if (stream_state->state_callback_received[OP_FAILED]) { - GRPC_CLOSURE_SCHED( - exec_ctx, - stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_NONE); - } else if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { - GRPC_CLOSURE_SCHED( - exec_ctx, - stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_NONE); - } else { - grpc_chttp2_incoming_metadata_buffer_publish( - exec_ctx, &oas->s->state.rs.initial_metadata, - stream_op->payload->recv_initial_metadata.recv_initial_metadata); - GRPC_CLOSURE_SCHED( - exec_ctx, - stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_NONE); - } - stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true; - result = ACTION_TAKEN_NO_CALLBACK; - } else if (stream_op->recv_message && - op_can_be_run(stream_op, s, &oas->state, OP_RECV_MESSAGE)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_MESSAGE", oas); - if (stream_state->state_op_done[OP_CANCEL_ERROR]) { - CRONET_LOG(GPR_DEBUG, "Stream is cancelled."); - GRPC_CLOSURE_SCHED(exec_ctx, - stream_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - stream_state->state_op_done[OP_RECV_MESSAGE] = true; - oas->state.state_op_done[OP_RECV_MESSAGE] = true; - result = ACTION_TAKEN_NO_CALLBACK; - } else if (stream_state->state_callback_received[OP_FAILED]) { - CRONET_LOG(GPR_DEBUG, "Stream failed."); - GRPC_CLOSURE_SCHED(exec_ctx, - stream_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - stream_state->state_op_done[OP_RECV_MESSAGE] = true; - oas->state.state_op_done[OP_RECV_MESSAGE] = true; - result = ACTION_TAKEN_NO_CALLBACK; - } else if (stream_state->rs.read_stream_closed == true) { - /* No more data will be received */ - CRONET_LOG(GPR_DEBUG, "read stream closed"); - GRPC_CLOSURE_SCHED(exec_ctx, - stream_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - stream_state->state_op_done[OP_RECV_MESSAGE] = true; - oas->state.state_op_done[OP_RECV_MESSAGE] = true; - result = ACTION_TAKEN_NO_CALLBACK; - } else if (stream_state->flush_read) { - CRONET_LOG(GPR_DEBUG, "flush read"); - GRPC_CLOSURE_SCHED(exec_ctx, - stream_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - stream_state->state_op_done[OP_RECV_MESSAGE] = true; - oas->state.state_op_done[OP_RECV_MESSAGE] = true; - result = ACTION_TAKEN_NO_CALLBACK; - } else if (stream_state->rs.length_field_received == false) { - if (stream_state->rs.received_bytes == GRPC_HEADER_SIZE_IN_BYTES && - stream_state->rs.remaining_bytes == 0) { - /* Start a read operation for data */ - stream_state->rs.length_field_received = true; - parse_grpc_header((const uint8_t *)stream_state->rs.read_buffer, - &stream_state->rs.length_field, - &stream_state->rs.compressed); - CRONET_LOG(GPR_DEBUG, "length field = %d", - stream_state->rs.length_field); - if (stream_state->rs.length_field > 0) { - stream_state->rs.read_buffer = - (char *)gpr_malloc((size_t)stream_state->rs.length_field); - GPR_ASSERT(stream_state->rs.read_buffer); - stream_state->rs.remaining_bytes = stream_state->rs.length_field; - stream_state->rs.received_bytes = 0; - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - stream_state->state_op_done[OP_READ_REQ_MADE] = - true; /* Indicates that at least one read request has been made */ - bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, - stream_state->rs.remaining_bytes); - stream_state->pending_read_from_cronet = true; - result = ACTION_TAKEN_WITH_CALLBACK; - } else { - stream_state->rs.remaining_bytes = 0; - CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response."); - /* Clean up read_slice_buffer in case there is unread data. */ - grpc_slice_buffer_destroy_internal( - exec_ctx, &stream_state->rs.read_slice_buffer); - grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); - grpc_slice_buffer_stream_init(&stream_state->rs.sbs, - &stream_state->rs.read_slice_buffer, 0); - if (stream_state->rs.compressed) { - stream_state->rs.sbs.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; - } - *((grpc_byte_buffer **) - stream_op->payload->recv_message.recv_message) = - (grpc_byte_buffer *)&stream_state->rs.sbs; - GRPC_CLOSURE_SCHED( - exec_ctx, stream_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - stream_state->state_op_done[OP_RECV_MESSAGE] = true; - oas->state.state_op_done[OP_RECV_MESSAGE] = true; - - /* Extra read to trigger on_succeed */ - stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; - stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; - stream_state->rs.received_bytes = 0; - stream_state->rs.compressed = false; - stream_state->rs.length_field_received = false; - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - stream_state->state_op_done[OP_READ_REQ_MADE] = - true; /* Indicates that at least one read request has been made */ - bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, - stream_state->rs.remaining_bytes); - stream_state->pending_read_from_cronet = true; - result = ACTION_TAKEN_NO_CALLBACK; - } - } else if (stream_state->rs.remaining_bytes == 0) { - /* Start a read operation for first 5 bytes (GRPC header) */ - stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; - stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; - stream_state->rs.received_bytes = 0; - stream_state->rs.compressed = false; - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - stream_state->state_op_done[OP_READ_REQ_MADE] = - true; /* Indicates that at least one read request has been made */ - bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, - stream_state->rs.remaining_bytes); - stream_state->pending_read_from_cronet = true; - result = ACTION_TAKEN_WITH_CALLBACK; - } else { - result = NO_ACTION_POSSIBLE; - } - } else if (stream_state->rs.remaining_bytes == 0) { - CRONET_LOG(GPR_DEBUG, "read operation complete"); - grpc_slice read_data_slice = - GRPC_SLICE_MALLOC((uint32_t)stream_state->rs.length_field); - uint8_t *dst_p = GRPC_SLICE_START_PTR(read_data_slice); - memcpy(dst_p, stream_state->rs.read_buffer, - (size_t)stream_state->rs.length_field); - null_and_maybe_free_read_buffer(s); - /* Clean up read_slice_buffer in case there is unread data. */ - grpc_slice_buffer_destroy_internal(exec_ctx, - &stream_state->rs.read_slice_buffer); - grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); - grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer, - read_data_slice); - grpc_slice_buffer_stream_init(&stream_state->rs.sbs, - &stream_state->rs.read_slice_buffer, 0); - if (stream_state->rs.compressed) { - stream_state->rs.sbs.base.flags = GRPC_WRITE_INTERNAL_COMPRESS; - } - *((grpc_byte_buffer **)stream_op->payload->recv_message.recv_message) = - (grpc_byte_buffer *)&stream_state->rs.sbs; - GRPC_CLOSURE_SCHED(exec_ctx, - stream_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - stream_state->state_op_done[OP_RECV_MESSAGE] = true; - oas->state.state_op_done[OP_RECV_MESSAGE] = true; - /* Do an extra read to trigger on_succeeded() callback in case connection - is closed */ - stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; - stream_state->rs.compressed = false; - stream_state->rs.received_bytes = 0; - stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; - stream_state->rs.length_field_received = false; - CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); - bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, - stream_state->rs.remaining_bytes); - stream_state->pending_read_from_cronet = true; - result = ACTION_TAKEN_NO_CALLBACK; - } - } else if (stream_op->recv_trailing_metadata && - op_can_be_run(stream_op, s, &oas->state, - OP_RECV_TRAILING_METADATA)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_TRAILING_METADATA", oas); - if (oas->s->state.rs.trailing_metadata_valid) { - grpc_chttp2_incoming_metadata_buffer_publish( - exec_ctx, &oas->s->state.rs.trailing_metadata, - stream_op->payload->recv_trailing_metadata.recv_trailing_metadata); - stream_state->rs.trailing_metadata_valid = false; - } - stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true; - result = ACTION_TAKEN_NO_CALLBACK; - } else if (stream_op->cancel_stream && - op_can_be_run(stream_op, s, &oas->state, OP_CANCEL_ERROR)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_CANCEL_ERROR", oas); - if (s->cbs) { - CRONET_LOG(GPR_DEBUG, "W: bidirectional_stream_cancel(%p)", s->cbs); - bidirectional_stream_cancel(s->cbs); - result = ACTION_TAKEN_WITH_CALLBACK; - } else { - result = ACTION_TAKEN_NO_CALLBACK; - } - stream_state->state_op_done[OP_CANCEL_ERROR] = true; - if (!stream_state->cancel_error) { - stream_state->cancel_error = - GRPC_ERROR_REF(stream_op->payload->cancel_stream.cancel_error); - } - } else if (stream_op->on_complete && - op_can_be_run(stream_op, s, &oas->state, OP_ON_COMPLETE)) { - CRONET_LOG(GPR_DEBUG, "running: %p OP_ON_COMPLETE", oas); - if (stream_state->state_op_done[OP_CANCEL_ERROR]) { - GRPC_CLOSURE_SCHED(exec_ctx, stream_op->on_complete, - GRPC_ERROR_REF(stream_state->cancel_error)); - } else if (stream_state->state_callback_received[OP_FAILED]) { - GRPC_CLOSURE_SCHED( - exec_ctx, stream_op->on_complete, - make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable.")); - } else { - /* All actions in this stream_op are complete. Call the on_complete - * callback - */ - GRPC_CLOSURE_SCHED(exec_ctx, stream_op->on_complete, GRPC_ERROR_NONE); - } - oas->state.state_op_done[OP_ON_COMPLETE] = true; - oas->done = true; - /* reset any send message state, only if this ON_COMPLETE is about a send. - */ - if (stream_op->send_message) { - stream_state->state_callback_received[OP_SEND_MESSAGE] = false; - stream_state->state_op_done[OP_SEND_MESSAGE] = false; - } - result = ACTION_TAKEN_NO_CALLBACK; - /* If this is the on_complete callback being called for a received message - - make a note */ - if (stream_op->recv_message) - stream_state->state_op_done[OP_RECV_MESSAGE_AND_ON_COMPLETE] = true; - } else { - result = NO_ACTION_POSSIBLE; - } - return result; -} - -/* - Functions used by upper layers to access transport functionality. -*/ - -static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_stream_refcount *refcount, - const void *server_data, gpr_arena *arena) { - stream_obj *s = (stream_obj *)gs; - - s->refcount = refcount; - GRPC_CRONET_STREAM_REF(s, "cronet transport"); - memset(&s->storage, 0, sizeof(s->storage)); - s->storage.head = NULL; - memset(&s->state, 0, sizeof(s->state)); - s->curr_op = NULL; - s->cbs = NULL; - memset(&s->header_array, 0, sizeof(s->header_array)); - memset(&s->state.rs, 0, sizeof(s->state.rs)); - memset(&s->state.ws, 0, sizeof(s->state.ws)); - memset(s->state.state_op_done, 0, sizeof(s->state.state_op_done)); - memset(s->state.state_callback_received, 0, - sizeof(s->state.state_callback_received)); - s->state.fail_state = s->state.flush_read = false; - s->state.cancel_error = NULL; - s->state.flush_cronet_when_ready = s->state.pending_write_for_trailer = false; - s->state.pending_send_message = false; - s->state.pending_recv_trailing_metadata = false; - s->state.pending_read_from_cronet = false; - - s->curr_gs = gs; - s->curr_ct = (grpc_cronet_transport *)gt; - s->arena = arena; - - gpr_mu_init(&s->mu); - return 0; -} - -static void set_pollset_do_nothing(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_pollset *pollset) {} - -static void set_pollset_set_do_nothing(grpc_exec_ctx *exec_ctx, - grpc_transport *gt, grpc_stream *gs, - grpc_pollset_set *pollset_set) {} - -static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, - grpc_transport_stream_op_batch *op) { - CRONET_LOG(GPR_DEBUG, "perform_stream_op"); - if (op->send_initial_metadata && - header_has_authority(op->payload->send_initial_metadata - .send_initial_metadata->list.head)) { - /* Cronet does not support :authority header field. We cancel the call when - this field is present in metadata */ - if (op->recv_initial_metadata) { - GRPC_CLOSURE_SCHED( - exec_ctx, - op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_CANCELLED); - } - if (op->recv_message) { - GRPC_CLOSURE_SCHED(exec_ctx, op->payload->recv_message.recv_message_ready, - GRPC_ERROR_CANCELLED); - } - GRPC_CLOSURE_SCHED(exec_ctx, op->on_complete, GRPC_ERROR_CANCELLED); - return; - } - stream_obj *s = (stream_obj *)gs; - add_to_storage(s, op); - execute_from_storage(exec_ctx, s); -} - -static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, - grpc_closure *then_schedule_closure) { - stream_obj *s = (stream_obj *)gs; - null_and_maybe_free_read_buffer(s); - /* Clean up read_slice_buffer in case there is unread data. */ - grpc_slice_buffer_destroy_internal(exec_ctx, &s->state.rs.read_slice_buffer); - GRPC_ERROR_UNREF(s->state.cancel_error); - GRPC_CLOSURE_SCHED(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE); -} - -static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {} - -static grpc_endpoint *get_endpoint(grpc_exec_ctx *exec_ctx, - grpc_transport *gt) { - return NULL; -} - -static void perform_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_transport_op *op) {} - -static const grpc_transport_vtable grpc_cronet_vtable = { - sizeof(stream_obj), - "cronet_http", - init_stream, - set_pollset_do_nothing, - set_pollset_set_do_nothing, - perform_stream_op, - perform_op, - destroy_stream, - destroy_transport, - get_endpoint}; - -grpc_transport *grpc_create_cronet_transport(void *engine, const char *target, - const grpc_channel_args *args, - void *reserved) { - grpc_cronet_transport *ct = - (grpc_cronet_transport *)gpr_malloc(sizeof(grpc_cronet_transport)); - if (!ct) { - goto error; - } - ct->base.vtable = &grpc_cronet_vtable; - ct->engine = (stream_engine *)engine; - ct->host = (char *)gpr_malloc(strlen(target) + 1); - if (!ct->host) { - goto error; - } - strcpy(ct->host, target); - - ct->use_packet_coalescing = true; - if (args) { - for (size_t i = 0; i < args->num_args; i++) { - if (0 == - strcmp(args->args[i].key, GRPC_ARG_USE_CRONET_PACKET_COALESCING)) { - if (args->args[i].type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s ignored: it must be an integer", - GRPC_ARG_USE_CRONET_PACKET_COALESCING); - } else { - ct->use_packet_coalescing = (args->args[i].value.integer != 0); - } - } - } - } - - return &ct->base; - -error: - if (ct) { - if (ct->host) { - gpr_free(ct->host); - } - gpr_free(ct); - } - - return NULL; -} diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.cc b/src/core/ext/transport/cronet/transport/cronet_transport.cc new file mode 100644 index 0000000000..90f81a8c90 --- /dev/null +++ b/src/core/ext/transport/cronet/transport/cronet_transport.cc @@ -0,0 +1,1495 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/transport/incoming_metadata.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/transport/metadata_batch.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/transport_impl.h" +#include "third_party/objective_c/Cronet/bidirectional_stream_c.h" + +#define GRPC_HEADER_SIZE_IN_BYTES 5 +#define GRPC_FLUSH_READ_SIZE 4096 + +#define CRONET_LOG(...) \ + do { \ + if (grpc_cronet_trace) gpr_log(__VA_ARGS__); \ + } while (0) + +/* TODO (makdharma): Hook up into the wider tracing mechanism */ +int grpc_cronet_trace = 0; + +enum e_op_result { + ACTION_TAKEN_WITH_CALLBACK, + ACTION_TAKEN_NO_CALLBACK, + NO_ACTION_POSSIBLE +}; + +enum e_op_id { + OP_SEND_INITIAL_METADATA = 0, + OP_SEND_MESSAGE, + OP_SEND_TRAILING_METADATA, + OP_RECV_MESSAGE, + OP_RECV_INITIAL_METADATA, + OP_RECV_TRAILING_METADATA, + OP_CANCEL_ERROR, + OP_ON_COMPLETE, + OP_FAILED, + OP_SUCCEEDED, + OP_CANCELED, + OP_RECV_MESSAGE_AND_ON_COMPLETE, + OP_READ_REQ_MADE, + OP_NUM_OPS +}; + +/* Cronet callbacks. See cronet_c_for_grpc.h for documentation for each. */ + +static void on_stream_ready(bidirectional_stream *); +static void on_response_headers_received( + bidirectional_stream *, const bidirectional_stream_header_array *, + const char *); +static void on_write_completed(bidirectional_stream *, const char *); +static void on_read_completed(bidirectional_stream *, char *, int); +static void on_response_trailers_received( + bidirectional_stream *, const bidirectional_stream_header_array *); +static void on_succeeded(bidirectional_stream *); +static void on_failed(bidirectional_stream *, int); +static void on_canceled(bidirectional_stream *); +static bidirectional_stream_callback cronet_callbacks = { + on_stream_ready, + on_response_headers_received, + on_read_completed, + on_write_completed, + on_response_trailers_received, + on_succeeded, + on_failed, + on_canceled}; + +/* Cronet transport object */ +struct grpc_cronet_transport { + grpc_transport base; /* must be first element in this structure */ + stream_engine *engine; + char *host; + bool use_packet_coalescing; +}; +typedef struct grpc_cronet_transport grpc_cronet_transport; + +/* TODO (makdharma): reorder structure for memory efficiency per + http://www.catb.org/esr/structure-packing/#_structure_reordering: */ +struct read_state { + /* vars to store data coming from server */ + char *read_buffer; + bool length_field_received; + int received_bytes; + int remaining_bytes; + int length_field; + bool compressed; + char grpc_header_bytes[GRPC_HEADER_SIZE_IN_BYTES]; + char *payload_field; + bool read_stream_closed; + + /* vars for holding data destined for the application */ + struct grpc_slice_buffer_stream sbs; + grpc_slice_buffer read_slice_buffer; + + /* vars for trailing metadata */ + grpc_chttp2_incoming_metadata_buffer trailing_metadata; + bool trailing_metadata_valid; + + /* vars for initial metadata */ + grpc_chttp2_incoming_metadata_buffer initial_metadata; +}; + +struct write_state { + char *write_buffer; +}; + +/* track state of one stream op */ +struct op_state { + bool state_op_done[OP_NUM_OPS]; + bool state_callback_received[OP_NUM_OPS]; + /* A non-zero gRPC status code has been seen */ + bool fail_state; + /* Transport is discarding all buffered messages */ + bool flush_read; + bool flush_cronet_when_ready; + bool pending_write_for_trailer; + bool pending_send_message; + /* User requested RECV_TRAILING_METADATA */ + bool pending_recv_trailing_metadata; + /* Cronet has not issued a callback of a bidirectional read */ + bool pending_read_from_cronet; + grpc_error *cancel_error; + /* data structure for storing data coming from server */ + struct read_state rs; + /* data structure for storing data going to the server */ + struct write_state ws; +}; + +struct op_and_state { + grpc_transport_stream_op_batch op; + struct op_state state; + bool done; + struct stream_obj *s; /* Pointer back to the stream object */ + struct op_and_state *next; /* next op_and_state in the linked list */ +}; + +struct op_storage { + int num_pending_ops; + struct op_and_state *head; +}; + +struct stream_obj { + gpr_arena *arena; + struct op_and_state *oas; + grpc_transport_stream_op_batch *curr_op; + grpc_cronet_transport *curr_ct; + grpc_stream *curr_gs; + bidirectional_stream *cbs; + bidirectional_stream_header_array header_array; + + /* Stream level state. Some state will be tracked both at stream and stream_op + * level */ + struct op_state state; + + /* OP storage */ + struct op_storage storage; + + /* Mutex to protect storage */ + gpr_mu mu; + + /* Refcount object of the stream */ + grpc_stream_refcount *refcount; +}; +typedef struct stream_obj stream_obj; + +#ifndef NDEBUG +#define GRPC_CRONET_STREAM_REF(stream, reason) \ + grpc_cronet_stream_ref((stream), (reason)) +#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \ + grpc_cronet_stream_unref((exec_ctx), (stream), (reason)) +void grpc_cronet_stream_ref(stream_obj *s, const char *reason) { + grpc_stream_ref(s->refcount, reason); +} +void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s, + const char *reason) { + grpc_stream_unref(exec_ctx, s->refcount, reason); +} +#else +#define GRPC_CRONET_STREAM_REF(stream, reason) grpc_cronet_stream_ref((stream)) +#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \ + grpc_cronet_stream_unref((exec_ctx), (stream)) +void grpc_cronet_stream_ref(stream_obj *s) { grpc_stream_ref(s->refcount); } +void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s) { + grpc_stream_unref(exec_ctx, s->refcount); +} +#endif + +static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx, + struct op_and_state *oas); + +/* + Utility function to translate enum into string for printing +*/ +static const char *op_result_string(enum e_op_result i) { + switch (i) { + case ACTION_TAKEN_WITH_CALLBACK: + return "ACTION_TAKEN_WITH_CALLBACK"; + case ACTION_TAKEN_NO_CALLBACK: + return "ACTION_TAKEN_NO_CALLBACK"; + case NO_ACTION_POSSIBLE: + return "NO_ACTION_POSSIBLE"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +static const char *op_id_string(enum e_op_id i) { + switch (i) { + case OP_SEND_INITIAL_METADATA: + return "OP_SEND_INITIAL_METADATA"; + case OP_SEND_MESSAGE: + return "OP_SEND_MESSAGE"; + case OP_SEND_TRAILING_METADATA: + return "OP_SEND_TRAILING_METADATA"; + case OP_RECV_MESSAGE: + return "OP_RECV_MESSAGE"; + case OP_RECV_INITIAL_METADATA: + return "OP_RECV_INITIAL_METADATA"; + case OP_RECV_TRAILING_METADATA: + return "OP_RECV_TRAILING_METADATA"; + case OP_CANCEL_ERROR: + return "OP_CANCEL_ERROR"; + case OP_ON_COMPLETE: + return "OP_ON_COMPLETE"; + case OP_FAILED: + return "OP_FAILED"; + case OP_SUCCEEDED: + return "OP_SUCCEEDED"; + case OP_CANCELED: + return "OP_CANCELED"; + case OP_RECV_MESSAGE_AND_ON_COMPLETE: + return "OP_RECV_MESSAGE_AND_ON_COMPLETE"; + case OP_READ_REQ_MADE: + return "OP_READ_REQ_MADE"; + case OP_NUM_OPS: + return "OP_NUM_OPS"; + } + return "UNKNOWN"; +} + +static void null_and_maybe_free_read_buffer(stream_obj *s) { + if (s->state.rs.read_buffer && + s->state.rs.read_buffer != s->state.rs.grpc_header_bytes) { + gpr_free(s->state.rs.read_buffer); + } + s->state.rs.read_buffer = NULL; +} + +static void maybe_flush_read(stream_obj *s) { + /* To enter flush read state (discarding all the buffered messages in + * transport layer), two conditions must be satisfied: 1) non-zero grpc status + * has been received, and 2) an op requesting the status code + * (RECV_TRAILING_METADATA) is issued by the user. (See + * doc/status_ordering.md) */ + /* Whenever the evaluation of any of the two condition is changed, we check + * whether we should enter the flush read state. */ + if (s->state.pending_recv_trailing_metadata && s->state.fail_state) { + if (!s->state.flush_read && !s->state.rs.read_stream_closed) { + CRONET_LOG(GPR_DEBUG, "%p: Flush read", s); + s->state.flush_read = true; + null_and_maybe_free_read_buffer(s); + s->state.rs.read_buffer = (char *)gpr_malloc(GRPC_FLUSH_READ_SIZE); + if (!s->state.pending_read_from_cronet) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, + GRPC_FLUSH_READ_SIZE); + s->state.pending_read_from_cronet = true; + } + } + } +} + +static grpc_error *make_error_with_desc(int error_code, const char *desc) { + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, error_code); + return error; +} + +/* + Add a new stream op to op storage. +*/ +static void add_to_storage(struct stream_obj *s, + grpc_transport_stream_op_batch *op) { + struct op_storage *storage = &s->storage; + /* add new op at the beginning of the linked list. The memory is freed + in remove_from_storage */ + struct op_and_state *new_op = + (struct op_and_state *)gpr_malloc(sizeof(struct op_and_state)); + memcpy(&new_op->op, op, sizeof(grpc_transport_stream_op_batch)); + memset(&new_op->state, 0, sizeof(new_op->state)); + new_op->s = s; + new_op->done = false; + gpr_mu_lock(&s->mu); + new_op->next = storage->head; + storage->head = new_op; + storage->num_pending_ops++; + if (op->send_message) { + s->state.pending_send_message = true; + } + if (op->recv_trailing_metadata) { + s->state.pending_recv_trailing_metadata = true; + maybe_flush_read(s); + } + CRONET_LOG(GPR_DEBUG, "adding new op %p. %d in the queue.", new_op, + storage->num_pending_ops); + gpr_mu_unlock(&s->mu); +} + +/* + Traverse the linked list and delete op and free memory +*/ +static void remove_from_storage(struct stream_obj *s, + struct op_and_state *oas) { + struct op_and_state *curr; + if (s->storage.head == NULL || oas == NULL) { + return; + } + if (s->storage.head == oas) { + s->storage.head = oas->next; + gpr_free(oas); + s->storage.num_pending_ops--; + CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas, + s->storage.num_pending_ops); + } else { + for (curr = s->storage.head; curr != NULL; curr = curr->next) { + if (curr->next == oas) { + curr->next = oas->next; + s->storage.num_pending_ops--; + CRONET_LOG(GPR_DEBUG, "Freed %p. Now %d in the queue", oas, + s->storage.num_pending_ops); + gpr_free(oas); + break; + } else if (curr->next == NULL) { + CRONET_LOG(GPR_ERROR, "Reached end of LL and did not find op to free"); + } + } + } +} + +/* + Cycle through ops and try to take next action. Break when either + an action with callback is taken, or no action is possible. + This can get executed from the Cronet network thread via cronet callback + or on the application supplied thread via the perform_stream_op function. +*/ +static void execute_from_storage(grpc_exec_ctx *exec_ctx, stream_obj *s) { + gpr_mu_lock(&s->mu); + for (struct op_and_state *curr = s->storage.head; curr != NULL;) { + CRONET_LOG(GPR_DEBUG, "calling op at %p. done = %d", curr, curr->done); + GPR_ASSERT(curr->done == 0); + enum e_op_result result = execute_stream_op(exec_ctx, curr); + CRONET_LOG(GPR_DEBUG, "execute_stream_op[%p] returns %s", curr, + op_result_string(result)); + /* if this op is done, then remove it and free memory */ + if (curr->done) { + struct op_and_state *next = curr->next; + remove_from_storage(s, curr); + curr = next; + } + /* continue processing the same op if ACTION_TAKEN_WITHOUT_CALLBACK */ + if (result == NO_ACTION_POSSIBLE) { + curr = curr->next; + } else if (result == ACTION_TAKEN_WITH_CALLBACK) { + break; + } + } + gpr_mu_unlock(&s->mu); +} + +/* + Cronet callback +*/ +static void on_failed(bidirectional_stream *stream, int net_error) { + CRONET_LOG(GPR_DEBUG, "on_failed(%p, %d)", stream, net_error); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + stream_obj *s = (stream_obj *)stream->annotation; + gpr_mu_lock(&s->mu); + bidirectional_stream_destroy(s->cbs); + s->state.state_callback_received[OP_FAILED] = true; + s->cbs = NULL; + if (s->header_array.headers) { + gpr_free(s->header_array.headers); + s->header_array.headers = NULL; + } + if (s->state.ws.write_buffer) { + gpr_free(s->state.ws.write_buffer); + s->state.ws.write_buffer = NULL; + } + null_and_maybe_free_read_buffer(s); + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_canceled(bidirectional_stream *stream) { + CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + stream_obj *s = (stream_obj *)stream->annotation; + gpr_mu_lock(&s->mu); + bidirectional_stream_destroy(s->cbs); + s->state.state_callback_received[OP_CANCELED] = true; + s->cbs = NULL; + if (s->header_array.headers) { + gpr_free(s->header_array.headers); + s->header_array.headers = NULL; + } + if (s->state.ws.write_buffer) { + gpr_free(s->state.ws.write_buffer); + s->state.ws.write_buffer = NULL; + } + null_and_maybe_free_read_buffer(s); + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_succeeded(bidirectional_stream *stream) { + CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + stream_obj *s = (stream_obj *)stream->annotation; + gpr_mu_lock(&s->mu); + bidirectional_stream_destroy(s->cbs); + s->state.state_callback_received[OP_SUCCEEDED] = true; + s->cbs = NULL; + null_and_maybe_free_read_buffer(s); + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport"); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_stream_ready(bidirectional_stream *stream) { + CRONET_LOG(GPR_DEBUG, "W: on_stream_ready(%p)", stream); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + stream_obj *s = (stream_obj *)stream->annotation; + grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; + gpr_mu_lock(&s->mu); + s->state.state_op_done[OP_SEND_INITIAL_METADATA] = true; + s->state.state_callback_received[OP_SEND_INITIAL_METADATA] = true; + /* Free the memory allocated for headers */ + if (s->header_array.headers) { + gpr_free(s->header_array.headers); + s->header_array.headers = NULL; + } + /* Send the initial metadata on wire if there is no SEND_MESSAGE or + * SEND_TRAILING_METADATA ops pending */ + if (t->use_packet_coalescing) { + if (s->state.flush_cronet_when_ready) { + CRONET_LOG(GPR_DEBUG, "cronet_bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(stream); + } + } + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_response_headers_received( + bidirectional_stream *stream, + const bidirectional_stream_header_array *headers, + const char *negotiated_protocol) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + CRONET_LOG(GPR_DEBUG, "R: on_response_headers_received(%p, %p, %s)", stream, + headers, negotiated_protocol); + stream_obj *s = (stream_obj *)stream->annotation; + + /* Identify if this is a header or a trailer (in a trailer-only response case) + */ + for (size_t i = 0; i < headers->count; i++) { + if (0 == strcmp("grpc-status", headers->headers[i].key)) { + on_response_trailers_received(stream, headers); + return; + } + } + + gpr_mu_lock(&s->mu); + memset(&s->state.rs.initial_metadata, 0, + sizeof(s->state.rs.initial_metadata)); + grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.initial_metadata, + s->arena); + for (size_t i = 0; i < headers->count; i++) { + GRPC_LOG_IF_ERROR( + "on_response_headers_received", + grpc_chttp2_incoming_metadata_buffer_add( + &exec_ctx, &s->state.rs.initial_metadata, + grpc_mdelem_from_slices( + &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string( + headers->headers[i].key)), + grpc_slice_intern(grpc_slice_from_static_string( + headers->headers[i].value))))); + } + s->state.state_callback_received[OP_RECV_INITIAL_METADATA] = true; + if (!(s->state.state_op_done[OP_CANCEL_ERROR] || + s->state.state_callback_received[OP_FAILED])) { + /* Do an extra read to trigger on_succeeded() callback in case connection + is closed */ + GPR_ASSERT(s->state.rs.length_field_received == false); + s->state.rs.read_buffer = s->state.rs.grpc_header_bytes; + s->state.rs.compressed = false; + s->state.rs.received_bytes = 0; + s->state.rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, + s->state.rs.remaining_bytes); + s->state.pending_read_from_cronet = true; + } + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_write_completed(bidirectional_stream *stream, const char *data) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + stream_obj *s = (stream_obj *)stream->annotation; + CRONET_LOG(GPR_DEBUG, "W: on_write_completed(%p, %s)", stream, data); + gpr_mu_lock(&s->mu); + if (s->state.ws.write_buffer) { + gpr_free(s->state.ws.write_buffer); + s->state.ws.write_buffer = NULL; + } + s->state.state_callback_received[OP_SEND_MESSAGE] = true; + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_read_completed(bidirectional_stream *stream, char *data, + int count) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + stream_obj *s = (stream_obj *)stream->annotation; + CRONET_LOG(GPR_DEBUG, "R: on_read_completed(%p, %p, %d)", stream, data, + count); + gpr_mu_lock(&s->mu); + s->state.pending_read_from_cronet = false; + s->state.state_callback_received[OP_RECV_MESSAGE] = true; + if (count > 0 && s->state.flush_read) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, s->state.rs.read_buffer, + GRPC_FLUSH_READ_SIZE); + s->state.pending_read_from_cronet = true; + gpr_mu_unlock(&s->mu); + } else if (count > 0) { + s->state.rs.received_bytes += count; + s->state.rs.remaining_bytes -= count; + if (s->state.rs.remaining_bytes > 0) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + s->state.state_op_done[OP_READ_REQ_MADE] = true; + bidirectional_stream_read( + s->cbs, s->state.rs.read_buffer + s->state.rs.received_bytes, + s->state.rs.remaining_bytes); + s->state.pending_read_from_cronet = true; + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + } + } else { + null_and_maybe_free_read_buffer(s); + s->state.rs.read_stream_closed = true; + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Cronet callback +*/ +static void on_response_trailers_received( + bidirectional_stream *stream, + const bidirectional_stream_header_array *trailers) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + CRONET_LOG(GPR_DEBUG, "R: on_response_trailers_received(%p,%p)", stream, + trailers); + stream_obj *s = (stream_obj *)stream->annotation; + grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; + gpr_mu_lock(&s->mu); + memset(&s->state.rs.trailing_metadata, 0, + sizeof(s->state.rs.trailing_metadata)); + s->state.rs.trailing_metadata_valid = false; + grpc_chttp2_incoming_metadata_buffer_init(&s->state.rs.trailing_metadata, + s->arena); + for (size_t i = 0; i < trailers->count; i++) { + CRONET_LOG(GPR_DEBUG, "trailer key=%s, value=%s", trailers->headers[i].key, + trailers->headers[i].value); + GRPC_LOG_IF_ERROR( + "on_response_trailers_received", + grpc_chttp2_incoming_metadata_buffer_add( + &exec_ctx, &s->state.rs.trailing_metadata, + grpc_mdelem_from_slices( + &exec_ctx, grpc_slice_intern(grpc_slice_from_static_string( + trailers->headers[i].key)), + grpc_slice_intern(grpc_slice_from_static_string( + trailers->headers[i].value))))); + s->state.rs.trailing_metadata_valid = true; + if (0 == strcmp(trailers->headers[i].key, "grpc-status") && + 0 != strcmp(trailers->headers[i].value, "0")) { + s->state.fail_state = true; + maybe_flush_read(s); + } + } + s->state.state_callback_received[OP_RECV_TRAILING_METADATA] = true; + /* Send a EOS when server terminates the stream (testServerFinishesRequest) to + * trigger on_succeeded */ + if (!s->state.state_op_done[OP_SEND_TRAILING_METADATA] && + !(s->state.state_op_done[OP_CANCEL_ERROR] || + s->state.state_callback_received[OP_FAILED])) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs); + s->state.state_callback_received[OP_SEND_MESSAGE] = false; + bidirectional_stream_write(s->cbs, "", 0, true); + if (t->use_packet_coalescing) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(s->cbs); + } + s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true; + + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + execute_from_storage(&exec_ctx, s); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +/* + Utility function that takes the data from s->write_slice_buffer and assembles + into a contiguous byte stream with 5 byte gRPC header prepended. +*/ +static void create_grpc_frame(grpc_exec_ctx *exec_ctx, + grpc_slice_buffer *write_slice_buffer, + char **pp_write_buffer, + size_t *p_write_buffer_size, uint32_t flags) { + grpc_slice slice = grpc_slice_buffer_take_first(write_slice_buffer); + size_t length = GRPC_SLICE_LENGTH(slice); + *p_write_buffer_size = length + GRPC_HEADER_SIZE_IN_BYTES; + /* This is freed in the on_write_completed callback */ + char *write_buffer = (char *)gpr_malloc(length + GRPC_HEADER_SIZE_IN_BYTES); + *pp_write_buffer = write_buffer; + uint8_t *p = (uint8_t *)write_buffer; + /* Append 5 byte header */ + /* Compressed flag */ + *p++ = (flags & GRPC_WRITE_INTERNAL_COMPRESS) ? 1 : 0; + /* Message length */ + *p++ = (uint8_t)(length >> 24); + *p++ = (uint8_t)(length >> 16); + *p++ = (uint8_t)(length >> 8); + *p++ = (uint8_t)(length); + /* append actual data */ + memcpy(p, GRPC_SLICE_START_PTR(slice), length); + grpc_slice_unref_internal(exec_ctx, slice); +} + +/* + Convert metadata in a format that Cronet can consume +*/ +static void convert_metadata_to_cronet_headers( + grpc_linked_mdelem *head, const char *host, char **pp_url, + bidirectional_stream_header **pp_headers, size_t *p_num_headers, + const char **method) { + grpc_linked_mdelem *curr = head; + /* Walk the linked list and get number of header fields */ + size_t num_headers_available = 0; + while (curr != NULL) { + curr = curr->next; + num_headers_available++; + } + /* Allocate enough memory. It is freed in the on_stream_ready callback + */ + bidirectional_stream_header *headers = + (bidirectional_stream_header *)gpr_malloc( + sizeof(bidirectional_stream_header) * num_headers_available); + *pp_headers = headers; + + /* Walk the linked list again, this time copying the header fields. + s->num_headers can be less than num_headers_available, as some headers + are not used for cronet. + TODO (makdharma): Eliminate need to traverse the LL second time for perf. + */ + curr = head; + size_t num_headers = 0; + while (num_headers < num_headers_available) { + grpc_mdelem mdelem = curr->md; + curr = curr->next; + char *key = grpc_slice_to_c_string(GRPC_MDKEY(mdelem)); + char *value = grpc_slice_to_c_string(GRPC_MDVALUE(mdelem)); + if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_SCHEME) || + grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_AUTHORITY)) { + /* Cronet populates these fields on its own */ + gpr_free(key); + gpr_free(value); + continue; + } + if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_METHOD)) { + if (grpc_slice_eq(GRPC_MDVALUE(mdelem), GRPC_MDSTR_PUT)) { + *method = "PUT"; + } else { + /* POST method in default*/ + *method = "POST"; + } + gpr_free(key); + gpr_free(value); + continue; + } + if (grpc_slice_eq(GRPC_MDKEY(mdelem), GRPC_MDSTR_PATH)) { + /* Create URL by appending :path value to the hostname */ + gpr_asprintf(pp_url, "https://%s%s", host, value); + gpr_free(key); + gpr_free(value); + continue; + } + CRONET_LOG(GPR_DEBUG, "header %s = %s", key, value); + headers[num_headers].key = key; + headers[num_headers].value = value; + num_headers++; + if (curr == NULL) { + break; + } + } + *p_num_headers = (size_t)num_headers; +} + +static void parse_grpc_header(const uint8_t *data, int *length, + bool *compressed) { + const uint8_t c = *data; + const uint8_t *p = data + 1; + *compressed = ((c & 0x01) == 0x01); + *length = 0; + *length |= ((uint8_t)*p++) << 24; + *length |= ((uint8_t)*p++) << 16; + *length |= ((uint8_t)*p++) << 8; + *length |= ((uint8_t)*p++); +} + +static bool header_has_authority(grpc_linked_mdelem *head) { + while (head != NULL) { + if (grpc_slice_eq(GRPC_MDKEY(head->md), GRPC_MDSTR_AUTHORITY)) { + return true; + } + head = head->next; + } + return false; +} + +/* + Op Execution: Decide if one of the actions contained in the stream op can be + executed. This is the heart of the state machine. +*/ +static bool op_can_be_run(grpc_transport_stream_op_batch *curr_op, + struct stream_obj *s, struct op_state *op_state, + enum e_op_id op_id) { + struct op_state *stream_state = &s->state; + grpc_cronet_transport *t = s->curr_ct; + bool result = true; + /* When call is canceled, every op can be run, except under following + conditions + */ + bool is_canceled_or_failed = stream_state->state_op_done[OP_CANCEL_ERROR] || + stream_state->state_callback_received[OP_FAILED]; + if (is_canceled_or_failed) { + if (op_id == OP_SEND_INITIAL_METADATA) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_SEND_MESSAGE) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_SEND_TRAILING_METADATA) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_CANCEL_ERROR) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* already executed */ + if (op_id == OP_RECV_INITIAL_METADATA && + stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_RECV_MESSAGE && op_state->state_op_done[OP_RECV_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + if (op_id == OP_RECV_TRAILING_METADATA && + stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* ON_COMPLETE can be processed if one of the following conditions is met: + * 1. the stream failed + * 2. the stream is cancelled, and the callback is received + * 3. the stream succeeded before cancel is effective + * 4. the stream is cancelled, and the stream is never started */ + if (op_id == OP_ON_COMPLETE && + !(stream_state->state_callback_received[OP_FAILED] || + stream_state->state_callback_received[OP_CANCELED] || + stream_state->state_callback_received[OP_SUCCEEDED] || + !stream_state->state_op_done[OP_SEND_INITIAL_METADATA])) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + } else if (op_id == OP_SEND_INITIAL_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_SEND_INITIAL_METADATA]) result = false; + } else if (op_id == OP_RECV_INITIAL_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) result = false; + /* we haven't sent headers yet. */ + else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) + result = false; + /* we haven't received headers yet. */ + else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA] && + !stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) + result = false; + } else if (op_id == OP_SEND_MESSAGE) { + /* already executed (note we're checking op specific state, not stream + state) */ + if (op_state->state_op_done[OP_SEND_MESSAGE]) result = false; + /* we haven't sent headers yet. */ + else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) + result = false; + } else if (op_id == OP_RECV_MESSAGE) { + /* already executed */ + if (op_state->state_op_done[OP_RECV_MESSAGE]) result = false; + /* we haven't received headers yet. */ + else if (!stream_state->state_callback_received[OP_RECV_INITIAL_METADATA] && + !stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) + result = false; + } else if (op_id == OP_RECV_TRAILING_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) result = false; + /* we have asked for but haven't received message yet. */ + else if (stream_state->state_op_done[OP_READ_REQ_MADE] && + !stream_state->state_op_done[OP_RECV_MESSAGE]) + result = false; + /* we haven't received trailers yet. */ + else if (!stream_state->state_callback_received[OP_RECV_TRAILING_METADATA]) + result = false; + /* we haven't received on_succeeded yet. */ + else if (!stream_state->state_callback_received[OP_SUCCEEDED]) + result = false; + } else if (op_id == OP_SEND_TRAILING_METADATA) { + /* already executed */ + if (stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) result = false; + /* we haven't sent initial metadata yet */ + else if (!stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) + result = false; + /* we haven't sent message yet */ + else if (stream_state->pending_send_message && + !stream_state->state_op_done[OP_SEND_MESSAGE]) + result = false; + /* we haven't got on_write_completed for the send yet */ + else if (stream_state->state_op_done[OP_SEND_MESSAGE] && + !stream_state->state_callback_received[OP_SEND_MESSAGE] && + !(t->use_packet_coalescing && + stream_state->pending_write_for_trailer)) + result = false; + } else if (op_id == OP_CANCEL_ERROR) { + /* already executed */ + if (stream_state->state_op_done[OP_CANCEL_ERROR]) result = false; + } else if (op_id == OP_ON_COMPLETE) { + /* already executed (note we're checking op specific state, not stream + state) */ + if (op_state->state_op_done[OP_ON_COMPLETE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* Check if every op that was asked for is done. */ + else if (curr_op->send_initial_metadata && + !stream_state->state_callback_received[OP_SEND_INITIAL_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->send_message && + !op_state->state_op_done[OP_SEND_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->send_message && + !stream_state->state_callback_received[OP_SEND_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->send_trailing_metadata && + !stream_state->state_op_done[OP_SEND_TRAILING_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->recv_initial_metadata && + !stream_state->state_op_done[OP_RECV_INITIAL_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->recv_message && + !op_state->state_op_done[OP_RECV_MESSAGE]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->cancel_stream && + !stream_state->state_callback_received[OP_CANCELED]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } else if (curr_op->recv_trailing_metadata) { + /* We aren't done with trailing metadata yet */ + if (!stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + /* We've asked for actual message in an earlier op, and it hasn't been + delivered yet. */ + else if (stream_state->state_op_done[OP_READ_REQ_MADE]) { + /* If this op is not the one asking for read, (which means some earlier + op has asked), and the read hasn't been delivered. */ + if (!curr_op->recv_message && + !stream_state->state_callback_received[OP_SUCCEEDED]) { + CRONET_LOG(GPR_DEBUG, "Because"); + result = false; + } + } + } + /* We should see at least one on_write_completed for the trailers that we + sent */ + else if (curr_op->send_trailing_metadata && + !stream_state->state_callback_received[OP_SEND_MESSAGE]) + result = false; + } + CRONET_LOG(GPR_DEBUG, "op_can_be_run %s : %s", op_id_string(op_id), + result ? "YES" : "NO"); + return result; +} + +/* + TODO (makdharma): Break down this function in smaller chunks for readability. +*/ +static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx, + struct op_and_state *oas) { + grpc_transport_stream_op_batch *stream_op = &oas->op; + struct stream_obj *s = oas->s; + grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct; + struct op_state *stream_state = &s->state; + enum e_op_result result = NO_ACTION_POSSIBLE; + if (stream_op->send_initial_metadata && + op_can_be_run(stream_op, s, &oas->state, OP_SEND_INITIAL_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_INITIAL_METADATA", oas); + /* Start new cronet stream. It is destroyed in on_succeeded, on_canceled, + * on_failed */ + GPR_ASSERT(s->cbs == NULL); + GPR_ASSERT(!stream_state->state_op_done[OP_SEND_INITIAL_METADATA]); + s->cbs = + bidirectional_stream_create(t->engine, s->curr_gs, &cronet_callbacks); + CRONET_LOG(GPR_DEBUG, "%p = bidirectional_stream_create()", s->cbs); + if (t->use_packet_coalescing) { + bidirectional_stream_disable_auto_flush(s->cbs, true); + bidirectional_stream_delay_request_headers_until_flush(s->cbs, true); + } + char *url = NULL; + const char *method = "POST"; + s->header_array.headers = NULL; + convert_metadata_to_cronet_headers(stream_op->payload->send_initial_metadata + .send_initial_metadata->list.head, + t->host, &url, &s->header_array.headers, + &s->header_array.count, &method); + s->header_array.capacity = s->header_array.count; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_start(%p, %s)", s->cbs, url); + bidirectional_stream_start(s->cbs, url, 0, method, &s->header_array, false); + if (url) { + gpr_free(url); + } + unsigned int header_index; + for (header_index = 0; header_index < s->header_array.count; + header_index++) { + gpr_free((void *)s->header_array.headers[header_index].key); + gpr_free((void *)s->header_array.headers[header_index].value); + } + stream_state->state_op_done[OP_SEND_INITIAL_METADATA] = true; + if (t->use_packet_coalescing) { + if (!stream_op->send_message && !stream_op->send_trailing_metadata) { + s->state.flush_cronet_when_ready = true; + } + } + result = ACTION_TAKEN_WITH_CALLBACK; + } else if (stream_op->send_message && + op_can_be_run(stream_op, s, &oas->state, OP_SEND_MESSAGE)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_MESSAGE", oas); + stream_state->pending_send_message = false; + if (stream_state->state_callback_received[OP_FAILED]) { + result = NO_ACTION_POSSIBLE; + CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed."); + } else { + grpc_slice_buffer write_slice_buffer; + grpc_slice slice; + grpc_slice_buffer_init(&write_slice_buffer); + if (1 != grpc_byte_stream_next( + exec_ctx, stream_op->payload->send_message.send_message, + stream_op->payload->send_message.send_message->length, + NULL)) { + /* Should never reach here */ + GPR_ASSERT(false); + } + if (GRPC_ERROR_NONE != + grpc_byte_stream_pull(exec_ctx, + stream_op->payload->send_message.send_message, + &slice)) { + /* Should never reach here */ + GPR_ASSERT(false); + } + grpc_slice_buffer_add(&write_slice_buffer, slice); + if (write_slice_buffer.count != 1) { + /* Empty request not handled yet */ + gpr_log(GPR_ERROR, "Empty request is not supported"); + GPR_ASSERT(write_slice_buffer.count == 1); + } + if (write_slice_buffer.count > 0) { + size_t write_buffer_size; + create_grpc_frame(exec_ctx, &write_slice_buffer, + &stream_state->ws.write_buffer, &write_buffer_size, + stream_op->payload->send_message.send_message->flags); + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, %p)", s->cbs, + stream_state->ws.write_buffer); + stream_state->state_callback_received[OP_SEND_MESSAGE] = false; + bidirectional_stream_write(s->cbs, stream_state->ws.write_buffer, + (int)write_buffer_size, false); + grpc_slice_buffer_destroy_internal(exec_ctx, &write_slice_buffer); + if (t->use_packet_coalescing) { + if (!stream_op->send_trailing_metadata) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(s->cbs); + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + stream_state->pending_write_for_trailer = true; + result = ACTION_TAKEN_NO_CALLBACK; + } + } else { + result = ACTION_TAKEN_WITH_CALLBACK; + } + } else { + result = NO_ACTION_POSSIBLE; + } + } + stream_state->state_op_done[OP_SEND_MESSAGE] = true; + oas->state.state_op_done[OP_SEND_MESSAGE] = true; + } else if (stream_op->send_trailing_metadata && + op_can_be_run(stream_op, s, &oas->state, + OP_SEND_TRAILING_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_SEND_TRAILING_METADATA", oas); + if (stream_state->state_callback_received[OP_FAILED]) { + result = NO_ACTION_POSSIBLE; + CRONET_LOG(GPR_DEBUG, "Stream is either cancelled or failed."); + } else { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_write (%p, 0)", s->cbs); + stream_state->state_callback_received[OP_SEND_MESSAGE] = false; + bidirectional_stream_write(s->cbs, "", 0, true); + if (t->use_packet_coalescing) { + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_flush (%p)", s->cbs); + bidirectional_stream_flush(s->cbs); + } + result = ACTION_TAKEN_WITH_CALLBACK; + } + stream_state->state_op_done[OP_SEND_TRAILING_METADATA] = true; + } else if (stream_op->recv_initial_metadata && + op_can_be_run(stream_op, s, &oas->state, + OP_RECV_INITIAL_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_INITIAL_METADATA", oas); + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + GRPC_CLOSURE_SCHED( + exec_ctx, + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } else if (stream_state->state_callback_received[OP_FAILED]) { + GRPC_CLOSURE_SCHED( + exec_ctx, + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } else if (stream_state->state_op_done[OP_RECV_TRAILING_METADATA]) { + GRPC_CLOSURE_SCHED( + exec_ctx, + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } else { + grpc_chttp2_incoming_metadata_buffer_publish( + exec_ctx, &oas->s->state.rs.initial_metadata, + stream_op->payload->recv_initial_metadata.recv_initial_metadata); + GRPC_CLOSURE_SCHED( + exec_ctx, + stream_op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_NONE); + } + stream_state->state_op_done[OP_RECV_INITIAL_METADATA] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_op->recv_message && + op_can_be_run(stream_op, s, &oas->state, OP_RECV_MESSAGE)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_MESSAGE", oas); + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + CRONET_LOG(GPR_DEBUG, "Stream is cancelled."); + GRPC_CLOSURE_SCHED(exec_ctx, + stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->state_callback_received[OP_FAILED]) { + CRONET_LOG(GPR_DEBUG, "Stream failed."); + GRPC_CLOSURE_SCHED(exec_ctx, + stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->rs.read_stream_closed == true) { + /* No more data will be received */ + CRONET_LOG(GPR_DEBUG, "read stream closed"); + GRPC_CLOSURE_SCHED(exec_ctx, + stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->flush_read) { + CRONET_LOG(GPR_DEBUG, "flush read"); + GRPC_CLOSURE_SCHED(exec_ctx, + stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_state->rs.length_field_received == false) { + if (stream_state->rs.received_bytes == GRPC_HEADER_SIZE_IN_BYTES && + stream_state->rs.remaining_bytes == 0) { + /* Start a read operation for data */ + stream_state->rs.length_field_received = true; + parse_grpc_header((const uint8_t *)stream_state->rs.read_buffer, + &stream_state->rs.length_field, + &stream_state->rs.compressed); + CRONET_LOG(GPR_DEBUG, "length field = %d", + stream_state->rs.length_field); + if (stream_state->rs.length_field > 0) { + stream_state->rs.read_buffer = + (char *)gpr_malloc((size_t)stream_state->rs.length_field); + GPR_ASSERT(stream_state->rs.read_buffer); + stream_state->rs.remaining_bytes = stream_state->rs.length_field; + stream_state->rs.received_bytes = 0; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + stream_state->state_op_done[OP_READ_REQ_MADE] = + true; /* Indicates that at least one read request has been made */ + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + stream_state->rs.remaining_bytes = 0; + CRONET_LOG(GPR_DEBUG, "read operation complete. Empty response."); + /* Clean up read_slice_buffer in case there is unread data. */ + grpc_slice_buffer_destroy_internal( + exec_ctx, &stream_state->rs.read_slice_buffer); + grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); + grpc_slice_buffer_stream_init(&stream_state->rs.sbs, + &stream_state->rs.read_slice_buffer, 0); + if (stream_state->rs.compressed) { + stream_state->rs.sbs.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + *((grpc_byte_buffer **) + stream_op->payload->recv_message.recv_message) = + (grpc_byte_buffer *)&stream_state->rs.sbs; + GRPC_CLOSURE_SCHED( + exec_ctx, stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + + /* Extra read to trigger on_succeed */ + stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; + stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + stream_state->rs.received_bytes = 0; + stream_state->rs.compressed = false; + stream_state->rs.length_field_received = false; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + stream_state->state_op_done[OP_READ_REQ_MADE] = + true; /* Indicates that at least one read request has been made */ + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_NO_CALLBACK; + } + } else if (stream_state->rs.remaining_bytes == 0) { + /* Start a read operation for first 5 bytes (GRPC header) */ + stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; + stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + stream_state->rs.received_bytes = 0; + stream_state->rs.compressed = false; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + stream_state->state_op_done[OP_READ_REQ_MADE] = + true; /* Indicates that at least one read request has been made */ + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + result = NO_ACTION_POSSIBLE; + } + } else if (stream_state->rs.remaining_bytes == 0) { + CRONET_LOG(GPR_DEBUG, "read operation complete"); + grpc_slice read_data_slice = + GRPC_SLICE_MALLOC((uint32_t)stream_state->rs.length_field); + uint8_t *dst_p = GRPC_SLICE_START_PTR(read_data_slice); + memcpy(dst_p, stream_state->rs.read_buffer, + (size_t)stream_state->rs.length_field); + null_and_maybe_free_read_buffer(s); + /* Clean up read_slice_buffer in case there is unread data. */ + grpc_slice_buffer_destroy_internal(exec_ctx, + &stream_state->rs.read_slice_buffer); + grpc_slice_buffer_init(&stream_state->rs.read_slice_buffer); + grpc_slice_buffer_add(&stream_state->rs.read_slice_buffer, + read_data_slice); + grpc_slice_buffer_stream_init(&stream_state->rs.sbs, + &stream_state->rs.read_slice_buffer, 0); + if (stream_state->rs.compressed) { + stream_state->rs.sbs.base.flags = GRPC_WRITE_INTERNAL_COMPRESS; + } + *((grpc_byte_buffer **)stream_op->payload->recv_message.recv_message) = + (grpc_byte_buffer *)&stream_state->rs.sbs; + GRPC_CLOSURE_SCHED(exec_ctx, + stream_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + stream_state->state_op_done[OP_RECV_MESSAGE] = true; + oas->state.state_op_done[OP_RECV_MESSAGE] = true; + /* Do an extra read to trigger on_succeeded() callback in case connection + is closed */ + stream_state->rs.read_buffer = stream_state->rs.grpc_header_bytes; + stream_state->rs.compressed = false; + stream_state->rs.received_bytes = 0; + stream_state->rs.remaining_bytes = GRPC_HEADER_SIZE_IN_BYTES; + stream_state->rs.length_field_received = false; + CRONET_LOG(GPR_DEBUG, "bidirectional_stream_read(%p)", s->cbs); + bidirectional_stream_read(s->cbs, stream_state->rs.read_buffer, + stream_state->rs.remaining_bytes); + stream_state->pending_read_from_cronet = true; + result = ACTION_TAKEN_NO_CALLBACK; + } + } else if (stream_op->recv_trailing_metadata && + op_can_be_run(stream_op, s, &oas->state, + OP_RECV_TRAILING_METADATA)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_RECV_TRAILING_METADATA", oas); + if (oas->s->state.rs.trailing_metadata_valid) { + grpc_chttp2_incoming_metadata_buffer_publish( + exec_ctx, &oas->s->state.rs.trailing_metadata, + stream_op->payload->recv_trailing_metadata.recv_trailing_metadata); + stream_state->rs.trailing_metadata_valid = false; + } + stream_state->state_op_done[OP_RECV_TRAILING_METADATA] = true; + result = ACTION_TAKEN_NO_CALLBACK; + } else if (stream_op->cancel_stream && + op_can_be_run(stream_op, s, &oas->state, OP_CANCEL_ERROR)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_CANCEL_ERROR", oas); + if (s->cbs) { + CRONET_LOG(GPR_DEBUG, "W: bidirectional_stream_cancel(%p)", s->cbs); + bidirectional_stream_cancel(s->cbs); + result = ACTION_TAKEN_WITH_CALLBACK; + } else { + result = ACTION_TAKEN_NO_CALLBACK; + } + stream_state->state_op_done[OP_CANCEL_ERROR] = true; + if (!stream_state->cancel_error) { + stream_state->cancel_error = + GRPC_ERROR_REF(stream_op->payload->cancel_stream.cancel_error); + } + } else if (stream_op->on_complete && + op_can_be_run(stream_op, s, &oas->state, OP_ON_COMPLETE)) { + CRONET_LOG(GPR_DEBUG, "running: %p OP_ON_COMPLETE", oas); + if (stream_state->state_op_done[OP_CANCEL_ERROR]) { + GRPC_CLOSURE_SCHED(exec_ctx, stream_op->on_complete, + GRPC_ERROR_REF(stream_state->cancel_error)); + } else if (stream_state->state_callback_received[OP_FAILED]) { + GRPC_CLOSURE_SCHED( + exec_ctx, stream_op->on_complete, + make_error_with_desc(GRPC_STATUS_UNAVAILABLE, "Unavailable.")); + } else { + /* All actions in this stream_op are complete. Call the on_complete + * callback + */ + GRPC_CLOSURE_SCHED(exec_ctx, stream_op->on_complete, GRPC_ERROR_NONE); + } + oas->state.state_op_done[OP_ON_COMPLETE] = true; + oas->done = true; + /* reset any send message state, only if this ON_COMPLETE is about a send. + */ + if (stream_op->send_message) { + stream_state->state_callback_received[OP_SEND_MESSAGE] = false; + stream_state->state_op_done[OP_SEND_MESSAGE] = false; + } + result = ACTION_TAKEN_NO_CALLBACK; + /* If this is the on_complete callback being called for a received message - + make a note */ + if (stream_op->recv_message) + stream_state->state_op_done[OP_RECV_MESSAGE_AND_ON_COMPLETE] = true; + } else { + result = NO_ACTION_POSSIBLE; + } + return result; +} + +/* + Functions used by upper layers to access transport functionality. +*/ + +static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_stream_refcount *refcount, + const void *server_data, gpr_arena *arena) { + stream_obj *s = (stream_obj *)gs; + + s->refcount = refcount; + GRPC_CRONET_STREAM_REF(s, "cronet transport"); + memset(&s->storage, 0, sizeof(s->storage)); + s->storage.head = NULL; + memset(&s->state, 0, sizeof(s->state)); + s->curr_op = NULL; + s->cbs = NULL; + memset(&s->header_array, 0, sizeof(s->header_array)); + memset(&s->state.rs, 0, sizeof(s->state.rs)); + memset(&s->state.ws, 0, sizeof(s->state.ws)); + memset(s->state.state_op_done, 0, sizeof(s->state.state_op_done)); + memset(s->state.state_callback_received, 0, + sizeof(s->state.state_callback_received)); + s->state.fail_state = s->state.flush_read = false; + s->state.cancel_error = NULL; + s->state.flush_cronet_when_ready = s->state.pending_write_for_trailer = false; + s->state.pending_send_message = false; + s->state.pending_recv_trailing_metadata = false; + s->state.pending_read_from_cronet = false; + + s->curr_gs = gs; + s->curr_ct = (grpc_cronet_transport *)gt; + s->arena = arena; + + gpr_mu_init(&s->mu); + return 0; +} + +static void set_pollset_do_nothing(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_pollset *pollset) {} + +static void set_pollset_set_do_nothing(grpc_exec_ctx *exec_ctx, + grpc_transport *gt, grpc_stream *gs, + grpc_pollset_set *pollset_set) {} + +static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, + grpc_transport_stream_op_batch *op) { + CRONET_LOG(GPR_DEBUG, "perform_stream_op"); + if (op->send_initial_metadata && + header_has_authority(op->payload->send_initial_metadata + .send_initial_metadata->list.head)) { + /* Cronet does not support :authority header field. We cancel the call when + this field is present in metadata */ + if (op->recv_initial_metadata) { + GRPC_CLOSURE_SCHED( + exec_ctx, + op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_CANCELLED); + } + if (op->recv_message) { + GRPC_CLOSURE_SCHED(exec_ctx, op->payload->recv_message.recv_message_ready, + GRPC_ERROR_CANCELLED); + } + GRPC_CLOSURE_SCHED(exec_ctx, op->on_complete, GRPC_ERROR_CANCELLED); + return; + } + stream_obj *s = (stream_obj *)gs; + add_to_storage(s, op); + execute_from_storage(exec_ctx, s); +} + +static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, + grpc_closure *then_schedule_closure) { + stream_obj *s = (stream_obj *)gs; + null_and_maybe_free_read_buffer(s); + /* Clean up read_slice_buffer in case there is unread data. */ + grpc_slice_buffer_destroy_internal(exec_ctx, &s->state.rs.read_slice_buffer); + GRPC_ERROR_UNREF(s->state.cancel_error); + GRPC_CLOSURE_SCHED(exec_ctx, then_schedule_closure, GRPC_ERROR_NONE); +} + +static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) {} + +static grpc_endpoint *get_endpoint(grpc_exec_ctx *exec_ctx, + grpc_transport *gt) { + return NULL; +} + +static void perform_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_transport_op *op) {} + +static const grpc_transport_vtable grpc_cronet_vtable = { + sizeof(stream_obj), + "cronet_http", + init_stream, + set_pollset_do_nothing, + set_pollset_set_do_nothing, + perform_stream_op, + perform_op, + destroy_stream, + destroy_transport, + get_endpoint}; + +grpc_transport *grpc_create_cronet_transport(void *engine, const char *target, + const grpc_channel_args *args, + void *reserved) { + grpc_cronet_transport *ct = + (grpc_cronet_transport *)gpr_malloc(sizeof(grpc_cronet_transport)); + if (!ct) { + goto error; + } + ct->base.vtable = &grpc_cronet_vtable; + ct->engine = (stream_engine *)engine; + ct->host = (char *)gpr_malloc(strlen(target) + 1); + if (!ct->host) { + goto error; + } + strcpy(ct->host, target); + + ct->use_packet_coalescing = true; + if (args) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == + strcmp(args->args[i].key, GRPC_ARG_USE_CRONET_PACKET_COALESCING)) { + if (args->args[i].type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", + GRPC_ARG_USE_CRONET_PACKET_COALESCING); + } else { + ct->use_packet_coalescing = (args->args[i].value.integer != 0); + } + } + } + } + + return &ct->base; + +error: + if (ct) { + if (ct->host) { + gpr_free(ct->host); + } + gpr_free(ct); + } + + return NULL; +} diff --git a/src/core/ext/transport/inproc/inproc_plugin.c b/src/core/ext/transport/inproc/inproc_plugin.c deleted file mode 100644 index 6a796a0b19..0000000000 --- a/src/core/ext/transport/inproc/inproc_plugin.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/inproc/inproc_transport.h" -#include "src/core/lib/debug/trace.h" - -grpc_tracer_flag grpc_inproc_trace = GRPC_TRACER_INITIALIZER(false, "inproc"); - -void grpc_inproc_plugin_init(void) { - grpc_register_tracer(&grpc_inproc_trace); - grpc_inproc_transport_init(); -} - -void grpc_inproc_plugin_shutdown(void) { grpc_inproc_transport_shutdown(); } diff --git a/src/core/ext/transport/inproc/inproc_plugin.cc b/src/core/ext/transport/inproc/inproc_plugin.cc new file mode 100644 index 0000000000..5d8a1c74ab --- /dev/null +++ b/src/core/ext/transport/inproc/inproc_plugin.cc @@ -0,0 +1,31 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/inproc/inproc_transport.h" +#include "src/core/lib/debug/trace.h" + +grpc_tracer_flag grpc_inproc_trace = GRPC_TRACER_INITIALIZER(false, "inproc"); + +extern "C" void grpc_inproc_plugin_init(void) { + grpc_register_tracer(&grpc_inproc_trace); + grpc_inproc_transport_init(); +} + +extern "C" void grpc_inproc_plugin_shutdown(void) { + grpc_inproc_transport_shutdown(); +} diff --git a/src/core/ext/transport/inproc/inproc_transport.c b/src/core/ext/transport/inproc/inproc_transport.c deleted file mode 100644 index 31739d07dd..0000000000 --- a/src/core/ext/transport/inproc/inproc_transport.c +++ /dev/null @@ -1,1299 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/ext/transport/inproc/inproc_transport.h" -#include -#include -#include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/surface/channel_stack_type.h" -#include "src/core/lib/surface/server.h" -#include "src/core/lib/transport/connectivity_state.h" -#include "src/core/lib/transport/error_utils.h" -#include "src/core/lib/transport/transport_impl.h" - -#define INPROC_LOG(...) \ - do { \ - if (GRPC_TRACER_ON(grpc_inproc_trace)) gpr_log(__VA_ARGS__); \ - } while (0) - -static grpc_slice g_empty_slice; -static grpc_slice g_fake_path_key; -static grpc_slice g_fake_path_value; -static grpc_slice g_fake_auth_key; -static grpc_slice g_fake_auth_value; - -typedef struct { - gpr_mu mu; - gpr_refcount refs; -} shared_mu; - -typedef struct inproc_transport { - grpc_transport base; - shared_mu *mu; - gpr_refcount refs; - bool is_client; - grpc_connectivity_state_tracker connectivity; - void (*accept_stream_cb)(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_transport *transport, const void *server_data); - void *accept_stream_data; - bool is_closed; - struct inproc_transport *other_side; - struct inproc_stream *stream_list; -} inproc_transport; - -typedef struct sb_list_entry { - grpc_slice_buffer sb; - struct sb_list_entry *next; -} sb_list_entry; - -// Specialize grpc_byte_stream for our use case -typedef struct { - grpc_byte_stream base; - sb_list_entry *le; - grpc_error *shutdown_error; -} inproc_slice_byte_stream; - -typedef struct { - // TODO (vjpai): Add some inlined elements to avoid alloc in simple cases - sb_list_entry *head; - sb_list_entry *tail; -} slice_buffer_list; - -static void slice_buffer_list_init(slice_buffer_list *l) { - l->head = NULL; - l->tail = NULL; -} - -static void sb_list_entry_destroy(grpc_exec_ctx *exec_ctx, sb_list_entry *le) { - grpc_slice_buffer_destroy_internal(exec_ctx, &le->sb); - gpr_free(le); -} - -static void slice_buffer_list_destroy(grpc_exec_ctx *exec_ctx, - slice_buffer_list *l) { - sb_list_entry *curr = l->head; - while (curr != NULL) { - sb_list_entry *le = curr; - curr = curr->next; - sb_list_entry_destroy(exec_ctx, le); - } - l->head = NULL; - l->tail = NULL; -} - -static bool slice_buffer_list_empty(slice_buffer_list *l) { - return l->head == NULL; -} - -static void slice_buffer_list_append_entry(slice_buffer_list *l, - sb_list_entry *next) { - next->next = NULL; - if (l->tail) { - l->tail->next = next; - l->tail = next; - } else { - l->head = next; - l->tail = next; - } -} - -static grpc_slice_buffer *slice_buffer_list_append(slice_buffer_list *l) { - sb_list_entry *next = (sb_list_entry *)gpr_malloc(sizeof(*next)); - grpc_slice_buffer_init(&next->sb); - slice_buffer_list_append_entry(l, next); - return &next->sb; -} - -static sb_list_entry *slice_buffer_list_pophead(slice_buffer_list *l) { - sb_list_entry *ret = l->head; - l->head = l->head->next; - if (l->head == NULL) { - l->tail = NULL; - } - return ret; -} - -typedef struct inproc_stream { - inproc_transport *t; - grpc_metadata_batch to_read_initial_md; - uint32_t to_read_initial_md_flags; - bool to_read_initial_md_filled; - slice_buffer_list to_read_message; - grpc_metadata_batch to_read_trailing_md; - bool to_read_trailing_md_filled; - bool reads_needed; - bool read_closure_scheduled; - grpc_closure read_closure; - // Write buffer used only during gap at init time when client-side - // stream is set up but server side stream is not yet set up - grpc_metadata_batch write_buffer_initial_md; - bool write_buffer_initial_md_filled; - uint32_t write_buffer_initial_md_flags; - gpr_timespec write_buffer_deadline; - slice_buffer_list write_buffer_message; - grpc_metadata_batch write_buffer_trailing_md; - bool write_buffer_trailing_md_filled; - grpc_error *write_buffer_cancel_error; - - struct inproc_stream *other_side; - bool other_side_closed; // won't talk anymore - bool write_buffer_other_side_closed; // on hold - grpc_stream_refcount *refs; - grpc_closure *closure_at_destroy; - - gpr_arena *arena; - - grpc_transport_stream_op_batch *recv_initial_md_op; - grpc_transport_stream_op_batch *recv_message_op; - grpc_transport_stream_op_batch *recv_trailing_md_op; - - inproc_slice_byte_stream recv_message_stream; - - bool initial_md_sent; - bool trailing_md_sent; - bool initial_md_recvd; - bool trailing_md_recvd; - - bool closed; - - grpc_error *cancel_self_error; - grpc_error *cancel_other_error; - - gpr_timespec deadline; - - bool listed; - struct inproc_stream *stream_list_prev; - struct inproc_stream *stream_list_next; -} inproc_stream; - -static bool inproc_slice_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *bs, size_t max, - grpc_closure *on_complete) { - // Because inproc transport always provides the entire message atomically, - // the byte stream always has data available when this function is called. - // Thus, this function always returns true (unlike other transports) and - // there is never any need to schedule a closure - return true; -} - -static grpc_error *inproc_slice_byte_stream_pull(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *bs, - grpc_slice *slice) { - inproc_slice_byte_stream *stream = (inproc_slice_byte_stream *)bs; - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); - } - *slice = grpc_slice_buffer_take_first(&stream->le->sb); - return GRPC_ERROR_NONE; -} - -static void inproc_slice_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *bs, - grpc_error *error) { - inproc_slice_byte_stream *stream = (inproc_slice_byte_stream *)bs; - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = error; -} - -static void inproc_slice_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *bs) { - inproc_slice_byte_stream *stream = (inproc_slice_byte_stream *)bs; - sb_list_entry_destroy(exec_ctx, stream->le); - GRPC_ERROR_UNREF(stream->shutdown_error); -} - -static const grpc_byte_stream_vtable inproc_slice_byte_stream_vtable = { - inproc_slice_byte_stream_next, inproc_slice_byte_stream_pull, - inproc_slice_byte_stream_shutdown, inproc_slice_byte_stream_destroy}; - -void inproc_slice_byte_stream_init(inproc_slice_byte_stream *s, - sb_list_entry *le) { - s->base.length = (uint32_t)le->sb.length; - s->base.flags = 0; - s->base.vtable = &inproc_slice_byte_stream_vtable; - s->le = le; - s->shutdown_error = GRPC_ERROR_NONE; -} - -static void ref_transport(inproc_transport *t) { - INPROC_LOG(GPR_DEBUG, "ref_transport %p", t); - gpr_ref(&t->refs); -} - -static void really_destroy_transport(grpc_exec_ctx *exec_ctx, - inproc_transport *t) { - INPROC_LOG(GPR_DEBUG, "really_destroy_transport %p", t); - grpc_connectivity_state_destroy(exec_ctx, &t->connectivity); - if (gpr_unref(&t->mu->refs)) { - gpr_free(t->mu); - } - gpr_free(t); -} - -static void unref_transport(grpc_exec_ctx *exec_ctx, inproc_transport *t) { - INPROC_LOG(GPR_DEBUG, "unref_transport %p", t); - if (gpr_unref(&t->refs)) { - really_destroy_transport(exec_ctx, t); - } -} - -#ifndef NDEBUG -#define STREAM_REF(refs, reason) grpc_stream_ref(refs, reason) -#define STREAM_UNREF(e, refs, reason) grpc_stream_unref(e, refs, reason) -#else -#define STREAM_REF(refs, reason) grpc_stream_ref(refs) -#define STREAM_UNREF(e, refs, reason) grpc_stream_unref(e, refs) -#endif - -static void ref_stream(inproc_stream *s, const char *reason) { - INPROC_LOG(GPR_DEBUG, "ref_stream %p %s", s, reason); - STREAM_REF(s->refs, reason); -} - -static void unref_stream(grpc_exec_ctx *exec_ctx, inproc_stream *s, - const char *reason) { - INPROC_LOG(GPR_DEBUG, "unref_stream %p %s", s, reason); - STREAM_UNREF(exec_ctx, s->refs, reason); -} - -static void really_destroy_stream(grpc_exec_ctx *exec_ctx, inproc_stream *s) { - INPROC_LOG(GPR_DEBUG, "really_destroy_stream %p", s); - - slice_buffer_list_destroy(exec_ctx, &s->to_read_message); - slice_buffer_list_destroy(exec_ctx, &s->write_buffer_message); - GRPC_ERROR_UNREF(s->write_buffer_cancel_error); - GRPC_ERROR_UNREF(s->cancel_self_error); - GRPC_ERROR_UNREF(s->cancel_other_error); - - unref_transport(exec_ctx, s->t); - - if (s->closure_at_destroy) { - GRPC_CLOSURE_SCHED(exec_ctx, s->closure_at_destroy, GRPC_ERROR_NONE); - } -} - -static void read_state_machine(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); - -static void log_metadata(const grpc_metadata_batch *md_batch, bool is_client, - bool is_initial) { - for (grpc_linked_mdelem *md = md_batch->list.head; md != NULL; - md = md->next) { - char *key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); - char *value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); - gpr_log(GPR_INFO, "INPROC:%s:%s: %s: %s", is_initial ? "HDR" : "TRL", - is_client ? "CLI" : "SVR", key, value); - gpr_free(key); - gpr_free(value); - } -} - -static grpc_error *fill_in_metadata(grpc_exec_ctx *exec_ctx, inproc_stream *s, - const grpc_metadata_batch *metadata, - uint32_t flags, grpc_metadata_batch *out_md, - uint32_t *outflags, bool *markfilled) { - if (GRPC_TRACER_ON(grpc_inproc_trace)) { - log_metadata(metadata, s->t->is_client, outflags != NULL); - } - - if (outflags != NULL) { - *outflags = flags; - } - if (markfilled != NULL) { - *markfilled = true; - } - grpc_error *error = GRPC_ERROR_NONE; - for (grpc_linked_mdelem *elem = metadata->list.head; - (elem != NULL) && (error == GRPC_ERROR_NONE); elem = elem->next) { - grpc_linked_mdelem *nelem = - (grpc_linked_mdelem *)gpr_arena_alloc(s->arena, sizeof(*nelem)); - nelem->md = grpc_mdelem_from_slices( - exec_ctx, grpc_slice_intern(GRPC_MDKEY(elem->md)), - grpc_slice_intern(GRPC_MDVALUE(elem->md))); - - error = grpc_metadata_batch_link_tail(exec_ctx, out_md, nelem); - } - return error; -} - -static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_stream_refcount *refcount, - const void *server_data, gpr_arena *arena) { - INPROC_LOG(GPR_DEBUG, "init_stream %p %p %p", gt, gs, server_data); - inproc_transport *t = (inproc_transport *)gt; - inproc_stream *s = (inproc_stream *)gs; - s->arena = arena; - - s->refs = refcount; - // Ref this stream right now - ref_stream(s, "inproc_init_stream:init"); - - grpc_metadata_batch_init(&s->to_read_initial_md); - s->to_read_initial_md_flags = 0; - s->to_read_initial_md_filled = false; - grpc_metadata_batch_init(&s->to_read_trailing_md); - s->to_read_trailing_md_filled = false; - grpc_metadata_batch_init(&s->write_buffer_initial_md); - s->write_buffer_initial_md_flags = 0; - s->write_buffer_initial_md_filled = false; - grpc_metadata_batch_init(&s->write_buffer_trailing_md); - s->write_buffer_trailing_md_filled = false; - slice_buffer_list_init(&s->to_read_message); - slice_buffer_list_init(&s->write_buffer_message); - s->reads_needed = false; - s->read_closure_scheduled = false; - GRPC_CLOSURE_INIT(&s->read_closure, read_state_machine, s, - grpc_schedule_on_exec_ctx); - s->t = t; - s->closure_at_destroy = NULL; - s->other_side_closed = false; - - s->initial_md_sent = s->trailing_md_sent = s->initial_md_recvd = - s->trailing_md_recvd = false; - - s->closed = false; - - s->cancel_self_error = GRPC_ERROR_NONE; - s->cancel_other_error = GRPC_ERROR_NONE; - s->write_buffer_cancel_error = GRPC_ERROR_NONE; - s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - s->write_buffer_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - - s->stream_list_prev = NULL; - gpr_mu_lock(&t->mu->mu); - s->listed = true; - ref_stream(s, "inproc_init_stream:list"); - s->stream_list_next = t->stream_list; - if (t->stream_list) { - t->stream_list->stream_list_prev = s; - } - t->stream_list = s; - gpr_mu_unlock(&t->mu->mu); - - if (!server_data) { - ref_transport(t); - inproc_transport *st = t->other_side; - ref_transport(st); - s->other_side = NULL; // will get filled in soon - // Pass the client-side stream address to the server-side for a ref - ref_stream(s, "inproc_init_stream:clt"); // ref it now on behalf of server - // side to avoid destruction - INPROC_LOG(GPR_DEBUG, "calling accept stream cb %p %p", - st->accept_stream_cb, st->accept_stream_data); - (*st->accept_stream_cb)(exec_ctx, st->accept_stream_data, &st->base, - (void *)s); - } else { - // This is the server-side and is being called through accept_stream_cb - inproc_stream *cs = (inproc_stream *)server_data; - s->other_side = cs; - // Ref the server-side stream on behalf of the client now - ref_stream(s, "inproc_init_stream:srv"); - - // Now we are about to affect the other side, so lock the transport - // to make sure that it doesn't get destroyed - gpr_mu_lock(&s->t->mu->mu); - cs->other_side = s; - // Now transfer from the other side's write_buffer if any to the to_read - // buffer - if (cs->write_buffer_initial_md_filled) { - fill_in_metadata(exec_ctx, s, &cs->write_buffer_initial_md, - cs->write_buffer_initial_md_flags, - &s->to_read_initial_md, &s->to_read_initial_md_flags, - &s->to_read_initial_md_filled); - s->deadline = gpr_time_min(s->deadline, cs->write_buffer_deadline); - grpc_metadata_batch_clear(exec_ctx, &cs->write_buffer_initial_md); - cs->write_buffer_initial_md_filled = false; - } - while (!slice_buffer_list_empty(&cs->write_buffer_message)) { - slice_buffer_list_append_entry( - &s->to_read_message, - slice_buffer_list_pophead(&cs->write_buffer_message)); - } - if (cs->write_buffer_trailing_md_filled) { - fill_in_metadata(exec_ctx, s, &cs->write_buffer_trailing_md, 0, - &s->to_read_trailing_md, NULL, - &s->to_read_trailing_md_filled); - grpc_metadata_batch_clear(exec_ctx, &cs->write_buffer_trailing_md); - cs->write_buffer_trailing_md_filled = false; - } - if (cs->write_buffer_cancel_error != GRPC_ERROR_NONE) { - s->cancel_other_error = cs->write_buffer_cancel_error; - cs->write_buffer_cancel_error = GRPC_ERROR_NONE; - } - - gpr_mu_unlock(&s->t->mu->mu); - } - return 0; // return value is not important -} - -static void close_stream_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s) { - if (!s->closed) { - // Release the metadata that we would have written out - grpc_metadata_batch_destroy(exec_ctx, &s->write_buffer_initial_md); - grpc_metadata_batch_destroy(exec_ctx, &s->write_buffer_trailing_md); - - if (s->listed) { - inproc_stream *p = s->stream_list_prev; - inproc_stream *n = s->stream_list_next; - if (p != NULL) { - p->stream_list_next = n; - } else { - s->t->stream_list = n; - } - if (n != NULL) { - n->stream_list_prev = p; - } - s->listed = false; - unref_stream(exec_ctx, s, "close_stream:list"); - } - s->closed = true; - unref_stream(exec_ctx, s, "close_stream:closing"); - } -} - -// This function means that we are done talking/listening to the other side -static void close_other_side_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s, - const char *reason) { - if (s->other_side != NULL) { - // First release the metadata that came from the other side's arena - grpc_metadata_batch_destroy(exec_ctx, &s->to_read_initial_md); - grpc_metadata_batch_destroy(exec_ctx, &s->to_read_trailing_md); - - unref_stream(exec_ctx, s->other_side, reason); - s->other_side_closed = true; - s->other_side = NULL; - } else if (!s->other_side_closed) { - s->write_buffer_other_side_closed = true; - } -} - -static void fail_helper_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s, - grpc_error *error) { - INPROC_LOG(GPR_DEBUG, "read_state_machine %p fail_helper", s); - // If we're failing this side, we need to make sure that - // we also send or have already sent trailing metadata - if (!s->trailing_md_sent) { - // Send trailing md to the other side indicating cancellation - s->trailing_md_sent = true; - - grpc_metadata_batch fake_md; - grpc_metadata_batch_init(&fake_md); - - inproc_stream *other = s->other_side; - grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md - : &other->to_read_trailing_md; - bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled - : &other->to_read_trailing_md_filled; - fill_in_metadata(exec_ctx, s, &fake_md, 0, dest, NULL, destfilled); - grpc_metadata_batch_destroy(exec_ctx, &fake_md); - - if (other != NULL) { - if (other->cancel_other_error == GRPC_ERROR_NONE) { - other->cancel_other_error = GRPC_ERROR_REF(error); - } - if (other->reads_needed) { - if (!other->read_closure_scheduled) { - GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, - GRPC_ERROR_REF(error)); - other->read_closure_scheduled = true; - } - other->reads_needed = false; - } - } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) { - s->write_buffer_cancel_error = GRPC_ERROR_REF(error); - } - } - if (s->recv_initial_md_op) { - grpc_error *err; - if (!s->t->is_client) { - // If this is a server, provide initial metadata with a path and authority - // since it expects that as well as no error yet - grpc_metadata_batch fake_md; - grpc_metadata_batch_init(&fake_md); - grpc_linked_mdelem *path_md = - (grpc_linked_mdelem *)gpr_arena_alloc(s->arena, sizeof(*path_md)); - path_md->md = - grpc_mdelem_from_slices(exec_ctx, g_fake_path_key, g_fake_path_value); - GPR_ASSERT(grpc_metadata_batch_link_tail(exec_ctx, &fake_md, path_md) == - GRPC_ERROR_NONE); - grpc_linked_mdelem *auth_md = - (grpc_linked_mdelem *)gpr_arena_alloc(s->arena, sizeof(*auth_md)); - auth_md->md = - grpc_mdelem_from_slices(exec_ctx, g_fake_auth_key, g_fake_auth_value); - GPR_ASSERT(grpc_metadata_batch_link_tail(exec_ctx, &fake_md, auth_md) == - GRPC_ERROR_NONE); - - fill_in_metadata( - exec_ctx, s, &fake_md, 0, - s->recv_initial_md_op->payload->recv_initial_metadata - .recv_initial_metadata, - s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags, - NULL); - grpc_metadata_batch_destroy(exec_ctx, &fake_md); - err = GRPC_ERROR_NONE; - } else { - err = GRPC_ERROR_REF(error); - } - INPROC_LOG(GPR_DEBUG, - "fail_helper %p scheduling initial-metadata-ready %p %p", s, - error, err); - GRPC_CLOSURE_SCHED(exec_ctx, - s->recv_initial_md_op->payload->recv_initial_metadata - .recv_initial_metadata_ready, - err); - // Last use of err so no need to REF and then UNREF it - - if ((s->recv_initial_md_op != s->recv_message_op) && - (s->recv_initial_md_op != s->recv_trailing_md_op)) { - INPROC_LOG(GPR_DEBUG, - "fail_helper %p scheduling initial-metadata-on-complete %p", - error, s); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_initial_md_op->on_complete, - GRPC_ERROR_REF(error)); - } - s->recv_initial_md_op = NULL; - } - if (s->recv_message_op) { - INPROC_LOG(GPR_DEBUG, "fail_helper %p scheduling message-ready %p", s, - error); - GRPC_CLOSURE_SCHED( - exec_ctx, s->recv_message_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_REF(error)); - if (s->recv_message_op != s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, "fail_helper %p scheduling message-on-complete %p", - s, error); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, - GRPC_ERROR_REF(error)); - } - s->recv_message_op = NULL; - } - if (s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, - "fail_helper %p scheduling trailing-md-on-complete %p", s, - error); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, - GRPC_ERROR_REF(error)); - s->recv_trailing_md_op = NULL; - } - close_other_side_locked(exec_ctx, s, "fail_helper:other_side"); - close_stream_locked(exec_ctx, s); - - GRPC_ERROR_UNREF(error); -} - -static void read_state_machine(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - // This function gets called when we have contents in the unprocessed reads - // Get what we want based on our ops wanted - // Schedule our appropriate closures - // and then return to reads_needed state if still needed - - // Since this is a closure directly invoked by the combiner, it should not - // unref the error parameter explicitly; the combiner will do that implicitly - grpc_error *new_err = GRPC_ERROR_NONE; - - bool needs_close = false; - - INPROC_LOG(GPR_DEBUG, "read_state_machine %p", arg); - inproc_stream *s = (inproc_stream *)arg; - gpr_mu *mu = &s->t->mu->mu; // keep aside in case s gets closed - gpr_mu_lock(mu); - s->read_closure_scheduled = false; - // cancellation takes precedence - if (s->cancel_self_error != GRPC_ERROR_NONE) { - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(s->cancel_self_error)); - goto done; - } else if (s->cancel_other_error != GRPC_ERROR_NONE) { - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(s->cancel_other_error)); - goto done; - } else if (error != GRPC_ERROR_NONE) { - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(error)); - goto done; - } - - if (s->recv_initial_md_op) { - if (!s->to_read_initial_md_filled) { - // We entered the state machine on some other kind of read even though - // we still haven't satisfied initial md . That's an error. - new_err = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unexpected frame sequencing"); - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p scheduling on_complete errors for no " - "initial md %p", - s, new_err); - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); - goto done; - } else if (s->initial_md_recvd) { - new_err = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd initial md"); - INPROC_LOG( - GPR_DEBUG, - "read_state_machine %p scheduling on_complete errors for already " - "recvd initial md %p", - s, new_err); - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); - goto done; - } - - s->initial_md_recvd = true; - new_err = fill_in_metadata( - exec_ctx, s, &s->to_read_initial_md, s->to_read_initial_md_flags, - s->recv_initial_md_op->payload->recv_initial_metadata - .recv_initial_metadata, - s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags, NULL); - s->recv_initial_md_op->payload->recv_initial_metadata.recv_initial_metadata - ->deadline = s->deadline; - grpc_metadata_batch_clear(exec_ctx, &s->to_read_initial_md); - s->to_read_initial_md_filled = false; - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p scheduling initial-metadata-ready %p", s, - new_err); - GRPC_CLOSURE_SCHED(exec_ctx, - s->recv_initial_md_op->payload->recv_initial_metadata - .recv_initial_metadata_ready, - GRPC_ERROR_REF(new_err)); - if ((s->recv_initial_md_op != s->recv_message_op) && - (s->recv_initial_md_op != s->recv_trailing_md_op)) { - INPROC_LOG( - GPR_DEBUG, - "read_state_machine %p scheduling initial-metadata-on-complete %p", s, - new_err); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_initial_md_op->on_complete, - GRPC_ERROR_REF(new_err)); - } - s->recv_initial_md_op = NULL; - - if (new_err != GRPC_ERROR_NONE) { - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p scheduling on_complete errors2 %p", s, - new_err); - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); - goto done; - } - } - if (s->to_read_initial_md_filled) { - new_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unexpected recv frame"); - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); - goto done; - } - if (!slice_buffer_list_empty(&s->to_read_message) && s->recv_message_op) { - inproc_slice_byte_stream_init( - &s->recv_message_stream, - slice_buffer_list_pophead(&s->to_read_message)); - *s->recv_message_op->payload->recv_message.recv_message = - &s->recv_message_stream.base; - INPROC_LOG(GPR_DEBUG, "read_state_machine %p scheduling message-ready", s); - GRPC_CLOSURE_SCHED( - exec_ctx, s->recv_message_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - if (s->recv_message_op != s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p scheduling message-on-complete %p", s, - new_err); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, - GRPC_ERROR_REF(new_err)); - } - s->recv_message_op = NULL; - } - if (s->to_read_trailing_md_filled) { - if (s->trailing_md_recvd) { - new_err = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd trailing md"); - INPROC_LOG( - GPR_DEBUG, - "read_state_machine %p scheduling on_complete errors for already " - "recvd trailing md %p", - s, new_err); - fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); - goto done; - } - if (s->recv_message_op != NULL) { - // This message needs to be wrapped up because it will never be - // satisfied - INPROC_LOG(GPR_DEBUG, "read_state_machine %p scheduling message-ready", - s); - GRPC_CLOSURE_SCHED( - exec_ctx, - s->recv_message_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - if (s->recv_message_op != s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p scheduling message-on-complete %p", s, - new_err); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, - GRPC_ERROR_REF(new_err)); - } - s->recv_message_op = NULL; - } - if (s->recv_trailing_md_op != NULL) { - // We wanted trailing metadata and we got it - s->trailing_md_recvd = true; - new_err = - fill_in_metadata(exec_ctx, s, &s->to_read_trailing_md, 0, - s->recv_trailing_md_op->payload - ->recv_trailing_metadata.recv_trailing_metadata, - NULL, NULL); - grpc_metadata_batch_clear(exec_ctx, &s->to_read_trailing_md); - s->to_read_trailing_md_filled = false; - - // We should schedule the recv_trailing_md_op completion if - // 1. this stream is the client-side - // 2. this stream is the server-side AND has already sent its trailing md - // (If the server hasn't already sent its trailing md, it doesn't have - // a final status, so don't mark this op complete) - if (s->t->is_client || s->trailing_md_sent) { - INPROC_LOG( - GPR_DEBUG, - "read_state_machine %p scheduling trailing-md-on-complete %p", s, - new_err); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, - GRPC_ERROR_REF(new_err)); - s->recv_trailing_md_op = NULL; - needs_close = true; - } else { - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p server needs to delay handling " - "trailing-md-on-complete %p", - s, new_err); - } - } else { - INPROC_LOG( - GPR_DEBUG, - "read_state_machine %p has trailing md but not yet waiting for it", - s); - } - } - if (s->trailing_md_recvd && s->recv_message_op) { - // No further message will come on this stream, so finish off the - // recv_message_op - INPROC_LOG(GPR_DEBUG, "read_state_machine %p scheduling message-ready", s); - GRPC_CLOSURE_SCHED( - exec_ctx, s->recv_message_op->payload->recv_message.recv_message_ready, - GRPC_ERROR_NONE); - if (s->recv_message_op != s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, - "read_state_machine %p scheduling message-on-complete %p", s, - new_err); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, - GRPC_ERROR_REF(new_err)); - } - s->recv_message_op = NULL; - } - if (s->recv_message_op || s->recv_trailing_md_op) { - // Didn't get the item we wanted so we still need to get - // rescheduled - INPROC_LOG(GPR_DEBUG, "read_state_machine %p still needs closure %p %p", s, - s->recv_message_op, s->recv_trailing_md_op); - s->reads_needed = true; - } -done: - if (needs_close) { - close_other_side_locked(exec_ctx, s, "read_state_machine"); - close_stream_locked(exec_ctx, s); - } - gpr_mu_unlock(mu); - GRPC_ERROR_UNREF(new_err); -} - -static grpc_closure do_nothing_closure; - -static bool cancel_stream_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s, - grpc_error *error) { - bool ret = false; // was the cancel accepted - INPROC_LOG(GPR_DEBUG, "cancel_stream %p with %s", s, - grpc_error_string(error)); - if (s->cancel_self_error == GRPC_ERROR_NONE) { - ret = true; - s->cancel_self_error = GRPC_ERROR_REF(error); - if (s->reads_needed) { - if (!s->read_closure_scheduled) { - GRPC_CLOSURE_SCHED(exec_ctx, &s->read_closure, - GRPC_ERROR_REF(s->cancel_self_error)); - s->read_closure_scheduled = true; - } - s->reads_needed = false; - } - // Send trailing md to the other side indicating cancellation, even if we - // already have - s->trailing_md_sent = true; - - grpc_metadata_batch cancel_md; - grpc_metadata_batch_init(&cancel_md); - - inproc_stream *other = s->other_side; - grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md - : &other->to_read_trailing_md; - bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled - : &other->to_read_trailing_md_filled; - fill_in_metadata(exec_ctx, s, &cancel_md, 0, dest, NULL, destfilled); - grpc_metadata_batch_destroy(exec_ctx, &cancel_md); - - if (other != NULL) { - if (other->cancel_other_error == GRPC_ERROR_NONE) { - other->cancel_other_error = GRPC_ERROR_REF(s->cancel_self_error); - } - if (other->reads_needed) { - if (!other->read_closure_scheduled) { - GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, - GRPC_ERROR_REF(other->cancel_other_error)); - other->read_closure_scheduled = true; - } - other->reads_needed = false; - } - } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) { - s->write_buffer_cancel_error = GRPC_ERROR_REF(s->cancel_self_error); - } - - // if we are a server and already received trailing md but - // couldn't complete that because we hadn't yet sent out trailing - // md, now's the chance - if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, - "cancel_stream %p scheduling trailing-md-on-complete %p", s, - s->cancel_self_error); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, - GRPC_ERROR_REF(s->cancel_self_error)); - s->recv_trailing_md_op = NULL; - } - } - - close_other_side_locked(exec_ctx, s, "cancel_stream:other_side"); - close_stream_locked(exec_ctx, s); - - GRPC_ERROR_UNREF(error); - return ret; -} - -static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, - grpc_transport_stream_op_batch *op) { - INPROC_LOG(GPR_DEBUG, "perform_stream_op %p %p %p", gt, gs, op); - inproc_stream *s = (inproc_stream *)gs; - gpr_mu *mu = &s->t->mu->mu; // save aside in case s gets closed - gpr_mu_lock(mu); - - if (GRPC_TRACER_ON(grpc_inproc_trace)) { - if (op->send_initial_metadata) { - log_metadata(op->payload->send_initial_metadata.send_initial_metadata, - s->t->is_client, true); - } - if (op->send_trailing_metadata) { - log_metadata(op->payload->send_trailing_metadata.send_trailing_metadata, - s->t->is_client, false); - } - } - grpc_error *error = GRPC_ERROR_NONE; - grpc_closure *on_complete = op->on_complete; - if (on_complete == NULL) { - on_complete = &do_nothing_closure; - } - - if (op->cancel_stream) { - // Call cancel_stream_locked without ref'ing the cancel_error because - // this function is responsible to make sure that that field gets unref'ed - cancel_stream_locked(exec_ctx, s, op->payload->cancel_stream.cancel_error); - // this op can complete without an error - } else if (s->cancel_self_error != GRPC_ERROR_NONE) { - // already self-canceled so still give it an error - error = GRPC_ERROR_REF(s->cancel_self_error); - } else { - INPROC_LOG(GPR_DEBUG, "perform_stream_op %p%s%s%s%s%s%s", s, - op->send_initial_metadata ? " send_initial_metadata" : "", - op->send_message ? " send_message" : "", - op->send_trailing_metadata ? " send_trailing_metadata" : "", - op->recv_initial_metadata ? " recv_initial_metadata" : "", - op->recv_message ? " recv_message" : "", - op->recv_trailing_metadata ? " recv_trailing_metadata" : ""); - } - - bool needs_close = false; - - if (error == GRPC_ERROR_NONE && - (op->send_initial_metadata || op->send_message || - op->send_trailing_metadata)) { - inproc_stream *other = s->other_side; - if (s->t->is_closed) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Endpoint already shutdown"); - } - if (error == GRPC_ERROR_NONE && op->send_initial_metadata) { - grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_initial_md - : &other->to_read_initial_md; - uint32_t *destflags = (other == NULL) ? &s->write_buffer_initial_md_flags - : &other->to_read_initial_md_flags; - bool *destfilled = (other == NULL) ? &s->write_buffer_initial_md_filled - : &other->to_read_initial_md_filled; - if (*destfilled || s->initial_md_sent) { - // The buffer is already in use; that's an error! - INPROC_LOG(GPR_DEBUG, "Extra initial metadata %p", s); - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra initial metadata"); - } else { - if (!other->closed) { - fill_in_metadata( - exec_ctx, s, - op->payload->send_initial_metadata.send_initial_metadata, - op->payload->send_initial_metadata.send_initial_metadata_flags, - dest, destflags, destfilled); - } - if (s->t->is_client) { - gpr_timespec *dl = - (other == NULL) ? &s->write_buffer_deadline : &other->deadline; - *dl = gpr_time_min(*dl, op->payload->send_initial_metadata - .send_initial_metadata->deadline); - s->initial_md_sent = true; - } - } - } - if (error == GRPC_ERROR_NONE && op->send_message) { - size_t remaining = op->payload->send_message.send_message->length; - grpc_slice_buffer *dest = slice_buffer_list_append( - (other == NULL) ? &s->write_buffer_message : &other->to_read_message); - do { - grpc_slice message_slice; - grpc_closure unused; - GPR_ASSERT(grpc_byte_stream_next(exec_ctx, - op->payload->send_message.send_message, - SIZE_MAX, &unused)); - error = grpc_byte_stream_pull( - exec_ctx, op->payload->send_message.send_message, &message_slice); - if (error != GRPC_ERROR_NONE) { - cancel_stream_locked(exec_ctx, s, GRPC_ERROR_REF(error)); - break; - } - GPR_ASSERT(error == GRPC_ERROR_NONE); - remaining -= GRPC_SLICE_LENGTH(message_slice); - grpc_slice_buffer_add(dest, message_slice); - } while (remaining != 0); - grpc_byte_stream_destroy(exec_ctx, - op->payload->send_message.send_message); - } - if (error == GRPC_ERROR_NONE && op->send_trailing_metadata) { - grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md - : &other->to_read_trailing_md; - bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled - : &other->to_read_trailing_md_filled; - if (*destfilled || s->trailing_md_sent) { - // The buffer is already in use; that's an error! - INPROC_LOG(GPR_DEBUG, "Extra trailing metadata %p", s); - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra trailing metadata"); - } else { - if (!other->closed) { - fill_in_metadata( - exec_ctx, s, - op->payload->send_trailing_metadata.send_trailing_metadata, 0, - dest, NULL, destfilled); - } - s->trailing_md_sent = true; - if (!s->t->is_client && s->trailing_md_recvd && - s->recv_trailing_md_op) { - INPROC_LOG(GPR_DEBUG, - "perform_stream_op %p scheduling trailing-md-on-complete", - s); - GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, - GRPC_ERROR_NONE); - s->recv_trailing_md_op = NULL; - needs_close = true; - } - } - } - if (other != NULL && other->reads_needed) { - if (!other->read_closure_scheduled) { - GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, error); - other->read_closure_scheduled = true; - } - other->reads_needed = false; - } - } - if (error == GRPC_ERROR_NONE && - (op->recv_initial_metadata || op->recv_message || - op->recv_trailing_metadata)) { - // If there are any reads, mark it so that the read closure will react to - // them - if (op->recv_initial_metadata) { - s->recv_initial_md_op = op; - } - if (op->recv_message) { - s->recv_message_op = op; - } - if (op->recv_trailing_metadata) { - s->recv_trailing_md_op = op; - } - - // We want to initiate the closure if: - // 1. There is initial metadata and something ready to take that - // 2. There is a message and something ready to take it - // 3. There is trailing metadata, even if nothing specifically wants - // that because that can shut down the message as well - if ((s->to_read_initial_md_filled && op->recv_initial_metadata) || - ((!slice_buffer_list_empty(&s->to_read_message) || - s->trailing_md_recvd) && - op->recv_message) || - (s->to_read_trailing_md_filled)) { - if (!s->read_closure_scheduled) { - GRPC_CLOSURE_SCHED(exec_ctx, &s->read_closure, GRPC_ERROR_NONE); - s->read_closure_scheduled = true; - } - } else { - s->reads_needed = true; - } - } else { - if (error != GRPC_ERROR_NONE) { - // Schedule op's read closures that we didn't push to read state machine - if (op->recv_initial_metadata) { - INPROC_LOG( - GPR_DEBUG, - "perform_stream_op error %p scheduling initial-metadata-ready %p", - s, error); - GRPC_CLOSURE_SCHED( - exec_ctx, - op->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_REF(error)); - } - if (op->recv_message) { - INPROC_LOG( - GPR_DEBUG, - "perform_stream_op error %p scheduling recv message-ready %p", s, - error); - GRPC_CLOSURE_SCHED(exec_ctx, - op->payload->recv_message.recv_message_ready, - GRPC_ERROR_REF(error)); - } - } - INPROC_LOG(GPR_DEBUG, "perform_stream_op %p scheduling on_complete %p", s, - error); - GRPC_CLOSURE_SCHED(exec_ctx, on_complete, GRPC_ERROR_REF(error)); - } - if (needs_close) { - close_other_side_locked(exec_ctx, s, "perform_stream_op:other_side"); - close_stream_locked(exec_ctx, s); - } - gpr_mu_unlock(mu); - GRPC_ERROR_UNREF(error); -} - -static void close_transport_locked(grpc_exec_ctx *exec_ctx, - inproc_transport *t) { - INPROC_LOG(GPR_DEBUG, "close_transport %p %d", t, t->is_closed); - grpc_connectivity_state_set( - exec_ctx, &t->connectivity, GRPC_CHANNEL_SHUTDOWN, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Closing transport."), - "close transport"); - if (!t->is_closed) { - t->is_closed = true; - /* Also end all streams on this transport */ - while (t->stream_list != NULL) { - // cancel_stream_locked also adjusts stream list - cancel_stream_locked( - exec_ctx, t->stream_list, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); - } - } -} - -static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_transport_op *op) { - inproc_transport *t = (inproc_transport *)gt; - INPROC_LOG(GPR_DEBUG, "perform_transport_op %p %p", t, op); - gpr_mu_lock(&t->mu->mu); - if (op->on_connectivity_state_change) { - grpc_connectivity_state_notify_on_state_change( - exec_ctx, &t->connectivity, op->connectivity_state, - op->on_connectivity_state_change); - } - if (op->set_accept_stream) { - t->accept_stream_cb = op->set_accept_stream_fn; - t->accept_stream_data = op->set_accept_stream_user_data; - } - if (op->on_consumed) { - GRPC_CLOSURE_SCHED(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); - } - - bool do_close = false; - if (op->goaway_error != GRPC_ERROR_NONE) { - do_close = true; - GRPC_ERROR_UNREF(op->goaway_error); - } - if (op->disconnect_with_error != GRPC_ERROR_NONE) { - do_close = true; - GRPC_ERROR_UNREF(op->disconnect_with_error); - } - - if (do_close) { - close_transport_locked(exec_ctx, t); - } - gpr_mu_unlock(&t->mu->mu); -} - -static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, - grpc_closure *then_schedule_closure) { - INPROC_LOG(GPR_DEBUG, "destroy_stream %p %p", gs, then_schedule_closure); - inproc_stream *s = (inproc_stream *)gs; - s->closure_at_destroy = then_schedule_closure; - really_destroy_stream(exec_ctx, s); -} - -static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) { - inproc_transport *t = (inproc_transport *)gt; - INPROC_LOG(GPR_DEBUG, "destroy_transport %p", t); - gpr_mu_lock(&t->mu->mu); - close_transport_locked(exec_ctx, t); - gpr_mu_unlock(&t->mu->mu); - unref_transport(exec_ctx, t->other_side); - unref_transport(exec_ctx, t); -} - -/******************************************************************************* - * INTEGRATION GLUE - */ - -static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_pollset *pollset) { - // Nothing to do here -} - -static void set_pollset_set(grpc_exec_ctx *exec_ctx, grpc_transport *gt, - grpc_stream *gs, grpc_pollset_set *pollset_set) { - // Nothing to do here -} - -static grpc_endpoint *get_endpoint(grpc_exec_ctx *exec_ctx, grpc_transport *t) { - return NULL; -} - -/******************************************************************************* - * GLOBAL INIT AND DESTROY - */ -static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {} - -void grpc_inproc_transport_init(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_CLOSURE_INIT(&do_nothing_closure, do_nothing, NULL, - grpc_schedule_on_exec_ctx); - g_empty_slice = grpc_slice_from_static_buffer(NULL, 0); - - grpc_slice key_tmp = grpc_slice_from_static_string(":path"); - g_fake_path_key = grpc_slice_intern(key_tmp); - grpc_slice_unref_internal(&exec_ctx, key_tmp); - - g_fake_path_value = grpc_slice_from_static_string("/"); - - grpc_slice auth_tmp = grpc_slice_from_static_string(":authority"); - g_fake_auth_key = grpc_slice_intern(auth_tmp); - grpc_slice_unref_internal(&exec_ctx, auth_tmp); - - g_fake_auth_value = grpc_slice_from_static_string("inproc-fail"); - grpc_exec_ctx_finish(&exec_ctx); -} - -static const grpc_transport_vtable inproc_vtable = { - sizeof(inproc_stream), "inproc", init_stream, - set_pollset, set_pollset_set, perform_stream_op, - perform_transport_op, destroy_stream, destroy_transport, - get_endpoint}; - -/******************************************************************************* - * Main inproc transport functions - */ -static void inproc_transports_create(grpc_exec_ctx *exec_ctx, - grpc_transport **server_transport, - const grpc_channel_args *server_args, - grpc_transport **client_transport, - const grpc_channel_args *client_args) { - INPROC_LOG(GPR_DEBUG, "inproc_transports_create"); - inproc_transport *st = (inproc_transport *)gpr_zalloc(sizeof(*st)); - inproc_transport *ct = (inproc_transport *)gpr_zalloc(sizeof(*ct)); - // Share one lock between both sides since both sides get affected - st->mu = ct->mu = (shared_mu *)gpr_malloc(sizeof(*st->mu)); - gpr_mu_init(&st->mu->mu); - gpr_ref_init(&st->mu->refs, 2); - st->base.vtable = &inproc_vtable; - ct->base.vtable = &inproc_vtable; - // Start each side of transport with 2 refs since they each have a ref - // to the other - gpr_ref_init(&st->refs, 2); - gpr_ref_init(&ct->refs, 2); - st->is_client = false; - ct->is_client = true; - grpc_connectivity_state_init(&st->connectivity, GRPC_CHANNEL_READY, - "inproc_server"); - grpc_connectivity_state_init(&ct->connectivity, GRPC_CHANNEL_READY, - "inproc_client"); - st->other_side = ct; - ct->other_side = st; - st->stream_list = NULL; - ct->stream_list = NULL; - *server_transport = (grpc_transport *)st; - *client_transport = (grpc_transport *)ct; -} - -grpc_channel *grpc_inproc_channel_create(grpc_server *server, - grpc_channel_args *args, - void *reserved) { - GRPC_API_TRACE("grpc_inproc_channel_create(server=%p, args=%p)", 2, - (server, args)); - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - const grpc_channel_args *server_args = grpc_server_get_channel_args(server); - - // Add a default authority channel argument for the client - - grpc_arg default_authority_arg; - default_authority_arg.type = GRPC_ARG_STRING; - default_authority_arg.key = (char *)GRPC_ARG_DEFAULT_AUTHORITY; - default_authority_arg.value.string = (char *)"inproc.authority"; - grpc_channel_args *client_args = - grpc_channel_args_copy_and_add(args, &default_authority_arg, 1); - - grpc_transport *server_transport; - grpc_transport *client_transport; - inproc_transports_create(&exec_ctx, &server_transport, server_args, - &client_transport, client_args); - - grpc_server_setup_transport(&exec_ctx, server, server_transport, NULL, - server_args); - grpc_channel *channel = - grpc_channel_create(&exec_ctx, "inproc", client_args, - GRPC_CLIENT_DIRECT_CHANNEL, client_transport); - - // Free up created channel args - grpc_channel_args_destroy(&exec_ctx, client_args); - - // Now finish scheduled operations - grpc_exec_ctx_finish(&exec_ctx); - - return channel; -} - -void grpc_inproc_transport_shutdown(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_unref_internal(&exec_ctx, g_empty_slice); - grpc_slice_unref_internal(&exec_ctx, g_fake_path_key); - grpc_slice_unref_internal(&exec_ctx, g_fake_path_value); - grpc_slice_unref_internal(&exec_ctx, g_fake_auth_key); - grpc_slice_unref_internal(&exec_ctx, g_fake_auth_value); - grpc_exec_ctx_finish(&exec_ctx); -} diff --git a/src/core/ext/transport/inproc/inproc_transport.cc b/src/core/ext/transport/inproc/inproc_transport.cc new file mode 100644 index 0000000000..31739d07dd --- /dev/null +++ b/src/core/ext/transport/inproc/inproc_transport.cc @@ -0,0 +1,1299 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/ext/transport/inproc/inproc_transport.h" +#include +#include +#include +#include +#include +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/channel_stack_type.h" +#include "src/core/lib/surface/server.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/transport_impl.h" + +#define INPROC_LOG(...) \ + do { \ + if (GRPC_TRACER_ON(grpc_inproc_trace)) gpr_log(__VA_ARGS__); \ + } while (0) + +static grpc_slice g_empty_slice; +static grpc_slice g_fake_path_key; +static grpc_slice g_fake_path_value; +static grpc_slice g_fake_auth_key; +static grpc_slice g_fake_auth_value; + +typedef struct { + gpr_mu mu; + gpr_refcount refs; +} shared_mu; + +typedef struct inproc_transport { + grpc_transport base; + shared_mu *mu; + gpr_refcount refs; + bool is_client; + grpc_connectivity_state_tracker connectivity; + void (*accept_stream_cb)(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_transport *transport, const void *server_data); + void *accept_stream_data; + bool is_closed; + struct inproc_transport *other_side; + struct inproc_stream *stream_list; +} inproc_transport; + +typedef struct sb_list_entry { + grpc_slice_buffer sb; + struct sb_list_entry *next; +} sb_list_entry; + +// Specialize grpc_byte_stream for our use case +typedef struct { + grpc_byte_stream base; + sb_list_entry *le; + grpc_error *shutdown_error; +} inproc_slice_byte_stream; + +typedef struct { + // TODO (vjpai): Add some inlined elements to avoid alloc in simple cases + sb_list_entry *head; + sb_list_entry *tail; +} slice_buffer_list; + +static void slice_buffer_list_init(slice_buffer_list *l) { + l->head = NULL; + l->tail = NULL; +} + +static void sb_list_entry_destroy(grpc_exec_ctx *exec_ctx, sb_list_entry *le) { + grpc_slice_buffer_destroy_internal(exec_ctx, &le->sb); + gpr_free(le); +} + +static void slice_buffer_list_destroy(grpc_exec_ctx *exec_ctx, + slice_buffer_list *l) { + sb_list_entry *curr = l->head; + while (curr != NULL) { + sb_list_entry *le = curr; + curr = curr->next; + sb_list_entry_destroy(exec_ctx, le); + } + l->head = NULL; + l->tail = NULL; +} + +static bool slice_buffer_list_empty(slice_buffer_list *l) { + return l->head == NULL; +} + +static void slice_buffer_list_append_entry(slice_buffer_list *l, + sb_list_entry *next) { + next->next = NULL; + if (l->tail) { + l->tail->next = next; + l->tail = next; + } else { + l->head = next; + l->tail = next; + } +} + +static grpc_slice_buffer *slice_buffer_list_append(slice_buffer_list *l) { + sb_list_entry *next = (sb_list_entry *)gpr_malloc(sizeof(*next)); + grpc_slice_buffer_init(&next->sb); + slice_buffer_list_append_entry(l, next); + return &next->sb; +} + +static sb_list_entry *slice_buffer_list_pophead(slice_buffer_list *l) { + sb_list_entry *ret = l->head; + l->head = l->head->next; + if (l->head == NULL) { + l->tail = NULL; + } + return ret; +} + +typedef struct inproc_stream { + inproc_transport *t; + grpc_metadata_batch to_read_initial_md; + uint32_t to_read_initial_md_flags; + bool to_read_initial_md_filled; + slice_buffer_list to_read_message; + grpc_metadata_batch to_read_trailing_md; + bool to_read_trailing_md_filled; + bool reads_needed; + bool read_closure_scheduled; + grpc_closure read_closure; + // Write buffer used only during gap at init time when client-side + // stream is set up but server side stream is not yet set up + grpc_metadata_batch write_buffer_initial_md; + bool write_buffer_initial_md_filled; + uint32_t write_buffer_initial_md_flags; + gpr_timespec write_buffer_deadline; + slice_buffer_list write_buffer_message; + grpc_metadata_batch write_buffer_trailing_md; + bool write_buffer_trailing_md_filled; + grpc_error *write_buffer_cancel_error; + + struct inproc_stream *other_side; + bool other_side_closed; // won't talk anymore + bool write_buffer_other_side_closed; // on hold + grpc_stream_refcount *refs; + grpc_closure *closure_at_destroy; + + gpr_arena *arena; + + grpc_transport_stream_op_batch *recv_initial_md_op; + grpc_transport_stream_op_batch *recv_message_op; + grpc_transport_stream_op_batch *recv_trailing_md_op; + + inproc_slice_byte_stream recv_message_stream; + + bool initial_md_sent; + bool trailing_md_sent; + bool initial_md_recvd; + bool trailing_md_recvd; + + bool closed; + + grpc_error *cancel_self_error; + grpc_error *cancel_other_error; + + gpr_timespec deadline; + + bool listed; + struct inproc_stream *stream_list_prev; + struct inproc_stream *stream_list_next; +} inproc_stream; + +static bool inproc_slice_byte_stream_next(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *bs, size_t max, + grpc_closure *on_complete) { + // Because inproc transport always provides the entire message atomically, + // the byte stream always has data available when this function is called. + // Thus, this function always returns true (unlike other transports) and + // there is never any need to schedule a closure + return true; +} + +static grpc_error *inproc_slice_byte_stream_pull(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *bs, + grpc_slice *slice) { + inproc_slice_byte_stream *stream = (inproc_slice_byte_stream *)bs; + if (stream->shutdown_error != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(stream->shutdown_error); + } + *slice = grpc_slice_buffer_take_first(&stream->le->sb); + return GRPC_ERROR_NONE; +} + +static void inproc_slice_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *bs, + grpc_error *error) { + inproc_slice_byte_stream *stream = (inproc_slice_byte_stream *)bs; + GRPC_ERROR_UNREF(stream->shutdown_error); + stream->shutdown_error = error; +} + +static void inproc_slice_byte_stream_destroy(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *bs) { + inproc_slice_byte_stream *stream = (inproc_slice_byte_stream *)bs; + sb_list_entry_destroy(exec_ctx, stream->le); + GRPC_ERROR_UNREF(stream->shutdown_error); +} + +static const grpc_byte_stream_vtable inproc_slice_byte_stream_vtable = { + inproc_slice_byte_stream_next, inproc_slice_byte_stream_pull, + inproc_slice_byte_stream_shutdown, inproc_slice_byte_stream_destroy}; + +void inproc_slice_byte_stream_init(inproc_slice_byte_stream *s, + sb_list_entry *le) { + s->base.length = (uint32_t)le->sb.length; + s->base.flags = 0; + s->base.vtable = &inproc_slice_byte_stream_vtable; + s->le = le; + s->shutdown_error = GRPC_ERROR_NONE; +} + +static void ref_transport(inproc_transport *t) { + INPROC_LOG(GPR_DEBUG, "ref_transport %p", t); + gpr_ref(&t->refs); +} + +static void really_destroy_transport(grpc_exec_ctx *exec_ctx, + inproc_transport *t) { + INPROC_LOG(GPR_DEBUG, "really_destroy_transport %p", t); + grpc_connectivity_state_destroy(exec_ctx, &t->connectivity); + if (gpr_unref(&t->mu->refs)) { + gpr_free(t->mu); + } + gpr_free(t); +} + +static void unref_transport(grpc_exec_ctx *exec_ctx, inproc_transport *t) { + INPROC_LOG(GPR_DEBUG, "unref_transport %p", t); + if (gpr_unref(&t->refs)) { + really_destroy_transport(exec_ctx, t); + } +} + +#ifndef NDEBUG +#define STREAM_REF(refs, reason) grpc_stream_ref(refs, reason) +#define STREAM_UNREF(e, refs, reason) grpc_stream_unref(e, refs, reason) +#else +#define STREAM_REF(refs, reason) grpc_stream_ref(refs) +#define STREAM_UNREF(e, refs, reason) grpc_stream_unref(e, refs) +#endif + +static void ref_stream(inproc_stream *s, const char *reason) { + INPROC_LOG(GPR_DEBUG, "ref_stream %p %s", s, reason); + STREAM_REF(s->refs, reason); +} + +static void unref_stream(grpc_exec_ctx *exec_ctx, inproc_stream *s, + const char *reason) { + INPROC_LOG(GPR_DEBUG, "unref_stream %p %s", s, reason); + STREAM_UNREF(exec_ctx, s->refs, reason); +} + +static void really_destroy_stream(grpc_exec_ctx *exec_ctx, inproc_stream *s) { + INPROC_LOG(GPR_DEBUG, "really_destroy_stream %p", s); + + slice_buffer_list_destroy(exec_ctx, &s->to_read_message); + slice_buffer_list_destroy(exec_ctx, &s->write_buffer_message); + GRPC_ERROR_UNREF(s->write_buffer_cancel_error); + GRPC_ERROR_UNREF(s->cancel_self_error); + GRPC_ERROR_UNREF(s->cancel_other_error); + + unref_transport(exec_ctx, s->t); + + if (s->closure_at_destroy) { + GRPC_CLOSURE_SCHED(exec_ctx, s->closure_at_destroy, GRPC_ERROR_NONE); + } +} + +static void read_state_machine(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); + +static void log_metadata(const grpc_metadata_batch *md_batch, bool is_client, + bool is_initial) { + for (grpc_linked_mdelem *md = md_batch->list.head; md != NULL; + md = md->next) { + char *key = grpc_slice_to_c_string(GRPC_MDKEY(md->md)); + char *value = grpc_slice_to_c_string(GRPC_MDVALUE(md->md)); + gpr_log(GPR_INFO, "INPROC:%s:%s: %s: %s", is_initial ? "HDR" : "TRL", + is_client ? "CLI" : "SVR", key, value); + gpr_free(key); + gpr_free(value); + } +} + +static grpc_error *fill_in_metadata(grpc_exec_ctx *exec_ctx, inproc_stream *s, + const grpc_metadata_batch *metadata, + uint32_t flags, grpc_metadata_batch *out_md, + uint32_t *outflags, bool *markfilled) { + if (GRPC_TRACER_ON(grpc_inproc_trace)) { + log_metadata(metadata, s->t->is_client, outflags != NULL); + } + + if (outflags != NULL) { + *outflags = flags; + } + if (markfilled != NULL) { + *markfilled = true; + } + grpc_error *error = GRPC_ERROR_NONE; + for (grpc_linked_mdelem *elem = metadata->list.head; + (elem != NULL) && (error == GRPC_ERROR_NONE); elem = elem->next) { + grpc_linked_mdelem *nelem = + (grpc_linked_mdelem *)gpr_arena_alloc(s->arena, sizeof(*nelem)); + nelem->md = grpc_mdelem_from_slices( + exec_ctx, grpc_slice_intern(GRPC_MDKEY(elem->md)), + grpc_slice_intern(GRPC_MDVALUE(elem->md))); + + error = grpc_metadata_batch_link_tail(exec_ctx, out_md, nelem); + } + return error; +} + +static int init_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_stream_refcount *refcount, + const void *server_data, gpr_arena *arena) { + INPROC_LOG(GPR_DEBUG, "init_stream %p %p %p", gt, gs, server_data); + inproc_transport *t = (inproc_transport *)gt; + inproc_stream *s = (inproc_stream *)gs; + s->arena = arena; + + s->refs = refcount; + // Ref this stream right now + ref_stream(s, "inproc_init_stream:init"); + + grpc_metadata_batch_init(&s->to_read_initial_md); + s->to_read_initial_md_flags = 0; + s->to_read_initial_md_filled = false; + grpc_metadata_batch_init(&s->to_read_trailing_md); + s->to_read_trailing_md_filled = false; + grpc_metadata_batch_init(&s->write_buffer_initial_md); + s->write_buffer_initial_md_flags = 0; + s->write_buffer_initial_md_filled = false; + grpc_metadata_batch_init(&s->write_buffer_trailing_md); + s->write_buffer_trailing_md_filled = false; + slice_buffer_list_init(&s->to_read_message); + slice_buffer_list_init(&s->write_buffer_message); + s->reads_needed = false; + s->read_closure_scheduled = false; + GRPC_CLOSURE_INIT(&s->read_closure, read_state_machine, s, + grpc_schedule_on_exec_ctx); + s->t = t; + s->closure_at_destroy = NULL; + s->other_side_closed = false; + + s->initial_md_sent = s->trailing_md_sent = s->initial_md_recvd = + s->trailing_md_recvd = false; + + s->closed = false; + + s->cancel_self_error = GRPC_ERROR_NONE; + s->cancel_other_error = GRPC_ERROR_NONE; + s->write_buffer_cancel_error = GRPC_ERROR_NONE; + s->deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + s->write_buffer_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + + s->stream_list_prev = NULL; + gpr_mu_lock(&t->mu->mu); + s->listed = true; + ref_stream(s, "inproc_init_stream:list"); + s->stream_list_next = t->stream_list; + if (t->stream_list) { + t->stream_list->stream_list_prev = s; + } + t->stream_list = s; + gpr_mu_unlock(&t->mu->mu); + + if (!server_data) { + ref_transport(t); + inproc_transport *st = t->other_side; + ref_transport(st); + s->other_side = NULL; // will get filled in soon + // Pass the client-side stream address to the server-side for a ref + ref_stream(s, "inproc_init_stream:clt"); // ref it now on behalf of server + // side to avoid destruction + INPROC_LOG(GPR_DEBUG, "calling accept stream cb %p %p", + st->accept_stream_cb, st->accept_stream_data); + (*st->accept_stream_cb)(exec_ctx, st->accept_stream_data, &st->base, + (void *)s); + } else { + // This is the server-side and is being called through accept_stream_cb + inproc_stream *cs = (inproc_stream *)server_data; + s->other_side = cs; + // Ref the server-side stream on behalf of the client now + ref_stream(s, "inproc_init_stream:srv"); + + // Now we are about to affect the other side, so lock the transport + // to make sure that it doesn't get destroyed + gpr_mu_lock(&s->t->mu->mu); + cs->other_side = s; + // Now transfer from the other side's write_buffer if any to the to_read + // buffer + if (cs->write_buffer_initial_md_filled) { + fill_in_metadata(exec_ctx, s, &cs->write_buffer_initial_md, + cs->write_buffer_initial_md_flags, + &s->to_read_initial_md, &s->to_read_initial_md_flags, + &s->to_read_initial_md_filled); + s->deadline = gpr_time_min(s->deadline, cs->write_buffer_deadline); + grpc_metadata_batch_clear(exec_ctx, &cs->write_buffer_initial_md); + cs->write_buffer_initial_md_filled = false; + } + while (!slice_buffer_list_empty(&cs->write_buffer_message)) { + slice_buffer_list_append_entry( + &s->to_read_message, + slice_buffer_list_pophead(&cs->write_buffer_message)); + } + if (cs->write_buffer_trailing_md_filled) { + fill_in_metadata(exec_ctx, s, &cs->write_buffer_trailing_md, 0, + &s->to_read_trailing_md, NULL, + &s->to_read_trailing_md_filled); + grpc_metadata_batch_clear(exec_ctx, &cs->write_buffer_trailing_md); + cs->write_buffer_trailing_md_filled = false; + } + if (cs->write_buffer_cancel_error != GRPC_ERROR_NONE) { + s->cancel_other_error = cs->write_buffer_cancel_error; + cs->write_buffer_cancel_error = GRPC_ERROR_NONE; + } + + gpr_mu_unlock(&s->t->mu->mu); + } + return 0; // return value is not important +} + +static void close_stream_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s) { + if (!s->closed) { + // Release the metadata that we would have written out + grpc_metadata_batch_destroy(exec_ctx, &s->write_buffer_initial_md); + grpc_metadata_batch_destroy(exec_ctx, &s->write_buffer_trailing_md); + + if (s->listed) { + inproc_stream *p = s->stream_list_prev; + inproc_stream *n = s->stream_list_next; + if (p != NULL) { + p->stream_list_next = n; + } else { + s->t->stream_list = n; + } + if (n != NULL) { + n->stream_list_prev = p; + } + s->listed = false; + unref_stream(exec_ctx, s, "close_stream:list"); + } + s->closed = true; + unref_stream(exec_ctx, s, "close_stream:closing"); + } +} + +// This function means that we are done talking/listening to the other side +static void close_other_side_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s, + const char *reason) { + if (s->other_side != NULL) { + // First release the metadata that came from the other side's arena + grpc_metadata_batch_destroy(exec_ctx, &s->to_read_initial_md); + grpc_metadata_batch_destroy(exec_ctx, &s->to_read_trailing_md); + + unref_stream(exec_ctx, s->other_side, reason); + s->other_side_closed = true; + s->other_side = NULL; + } else if (!s->other_side_closed) { + s->write_buffer_other_side_closed = true; + } +} + +static void fail_helper_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s, + grpc_error *error) { + INPROC_LOG(GPR_DEBUG, "read_state_machine %p fail_helper", s); + // If we're failing this side, we need to make sure that + // we also send or have already sent trailing metadata + if (!s->trailing_md_sent) { + // Send trailing md to the other side indicating cancellation + s->trailing_md_sent = true; + + grpc_metadata_batch fake_md; + grpc_metadata_batch_init(&fake_md); + + inproc_stream *other = s->other_side; + grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md + : &other->to_read_trailing_md; + bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled + : &other->to_read_trailing_md_filled; + fill_in_metadata(exec_ctx, s, &fake_md, 0, dest, NULL, destfilled); + grpc_metadata_batch_destroy(exec_ctx, &fake_md); + + if (other != NULL) { + if (other->cancel_other_error == GRPC_ERROR_NONE) { + other->cancel_other_error = GRPC_ERROR_REF(error); + } + if (other->reads_needed) { + if (!other->read_closure_scheduled) { + GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, + GRPC_ERROR_REF(error)); + other->read_closure_scheduled = true; + } + other->reads_needed = false; + } + } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) { + s->write_buffer_cancel_error = GRPC_ERROR_REF(error); + } + } + if (s->recv_initial_md_op) { + grpc_error *err; + if (!s->t->is_client) { + // If this is a server, provide initial metadata with a path and authority + // since it expects that as well as no error yet + grpc_metadata_batch fake_md; + grpc_metadata_batch_init(&fake_md); + grpc_linked_mdelem *path_md = + (grpc_linked_mdelem *)gpr_arena_alloc(s->arena, sizeof(*path_md)); + path_md->md = + grpc_mdelem_from_slices(exec_ctx, g_fake_path_key, g_fake_path_value); + GPR_ASSERT(grpc_metadata_batch_link_tail(exec_ctx, &fake_md, path_md) == + GRPC_ERROR_NONE); + grpc_linked_mdelem *auth_md = + (grpc_linked_mdelem *)gpr_arena_alloc(s->arena, sizeof(*auth_md)); + auth_md->md = + grpc_mdelem_from_slices(exec_ctx, g_fake_auth_key, g_fake_auth_value); + GPR_ASSERT(grpc_metadata_batch_link_tail(exec_ctx, &fake_md, auth_md) == + GRPC_ERROR_NONE); + + fill_in_metadata( + exec_ctx, s, &fake_md, 0, + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata, + s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags, + NULL); + grpc_metadata_batch_destroy(exec_ctx, &fake_md); + err = GRPC_ERROR_NONE; + } else { + err = GRPC_ERROR_REF(error); + } + INPROC_LOG(GPR_DEBUG, + "fail_helper %p scheduling initial-metadata-ready %p %p", s, + error, err); + GRPC_CLOSURE_SCHED(exec_ctx, + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata_ready, + err); + // Last use of err so no need to REF and then UNREF it + + if ((s->recv_initial_md_op != s->recv_message_op) && + (s->recv_initial_md_op != s->recv_trailing_md_op)) { + INPROC_LOG(GPR_DEBUG, + "fail_helper %p scheduling initial-metadata-on-complete %p", + error, s); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_initial_md_op->on_complete, + GRPC_ERROR_REF(error)); + } + s->recv_initial_md_op = NULL; + } + if (s->recv_message_op) { + INPROC_LOG(GPR_DEBUG, "fail_helper %p scheduling message-ready %p", s, + error); + GRPC_CLOSURE_SCHED( + exec_ctx, s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error)); + if (s->recv_message_op != s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, "fail_helper %p scheduling message-on-complete %p", + s, error); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, + GRPC_ERROR_REF(error)); + } + s->recv_message_op = NULL; + } + if (s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, + "fail_helper %p scheduling trailing-md-on-complete %p", s, + error); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, + GRPC_ERROR_REF(error)); + s->recv_trailing_md_op = NULL; + } + close_other_side_locked(exec_ctx, s, "fail_helper:other_side"); + close_stream_locked(exec_ctx, s); + + GRPC_ERROR_UNREF(error); +} + +static void read_state_machine(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + // This function gets called when we have contents in the unprocessed reads + // Get what we want based on our ops wanted + // Schedule our appropriate closures + // and then return to reads_needed state if still needed + + // Since this is a closure directly invoked by the combiner, it should not + // unref the error parameter explicitly; the combiner will do that implicitly + grpc_error *new_err = GRPC_ERROR_NONE; + + bool needs_close = false; + + INPROC_LOG(GPR_DEBUG, "read_state_machine %p", arg); + inproc_stream *s = (inproc_stream *)arg; + gpr_mu *mu = &s->t->mu->mu; // keep aside in case s gets closed + gpr_mu_lock(mu); + s->read_closure_scheduled = false; + // cancellation takes precedence + if (s->cancel_self_error != GRPC_ERROR_NONE) { + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(s->cancel_self_error)); + goto done; + } else if (s->cancel_other_error != GRPC_ERROR_NONE) { + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(s->cancel_other_error)); + goto done; + } else if (error != GRPC_ERROR_NONE) { + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(error)); + goto done; + } + + if (s->recv_initial_md_op) { + if (!s->to_read_initial_md_filled) { + // We entered the state machine on some other kind of read even though + // we still haven't satisfied initial md . That's an error. + new_err = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unexpected frame sequencing"); + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p scheduling on_complete errors for no " + "initial md %p", + s, new_err); + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); + goto done; + } else if (s->initial_md_recvd) { + new_err = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd initial md"); + INPROC_LOG( + GPR_DEBUG, + "read_state_machine %p scheduling on_complete errors for already " + "recvd initial md %p", + s, new_err); + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); + goto done; + } + + s->initial_md_recvd = true; + new_err = fill_in_metadata( + exec_ctx, s, &s->to_read_initial_md, s->to_read_initial_md_flags, + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata, + s->recv_initial_md_op->payload->recv_initial_metadata.recv_flags, NULL); + s->recv_initial_md_op->payload->recv_initial_metadata.recv_initial_metadata + ->deadline = s->deadline; + grpc_metadata_batch_clear(exec_ctx, &s->to_read_initial_md); + s->to_read_initial_md_filled = false; + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p scheduling initial-metadata-ready %p", s, + new_err); + GRPC_CLOSURE_SCHED(exec_ctx, + s->recv_initial_md_op->payload->recv_initial_metadata + .recv_initial_metadata_ready, + GRPC_ERROR_REF(new_err)); + if ((s->recv_initial_md_op != s->recv_message_op) && + (s->recv_initial_md_op != s->recv_trailing_md_op)) { + INPROC_LOG( + GPR_DEBUG, + "read_state_machine %p scheduling initial-metadata-on-complete %p", s, + new_err); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_initial_md_op->on_complete, + GRPC_ERROR_REF(new_err)); + } + s->recv_initial_md_op = NULL; + + if (new_err != GRPC_ERROR_NONE) { + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p scheduling on_complete errors2 %p", s, + new_err); + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); + goto done; + } + } + if (s->to_read_initial_md_filled) { + new_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unexpected recv frame"); + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); + goto done; + } + if (!slice_buffer_list_empty(&s->to_read_message) && s->recv_message_op) { + inproc_slice_byte_stream_init( + &s->recv_message_stream, + slice_buffer_list_pophead(&s->to_read_message)); + *s->recv_message_op->payload->recv_message.recv_message = + &s->recv_message_stream.base; + INPROC_LOG(GPR_DEBUG, "read_state_machine %p scheduling message-ready", s); + GRPC_CLOSURE_SCHED( + exec_ctx, s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + if (s->recv_message_op != s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p scheduling message-on-complete %p", s, + new_err); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, + GRPC_ERROR_REF(new_err)); + } + s->recv_message_op = NULL; + } + if (s->to_read_trailing_md_filled) { + if (s->trailing_md_recvd) { + new_err = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Already recvd trailing md"); + INPROC_LOG( + GPR_DEBUG, + "read_state_machine %p scheduling on_complete errors for already " + "recvd trailing md %p", + s, new_err); + fail_helper_locked(exec_ctx, s, GRPC_ERROR_REF(new_err)); + goto done; + } + if (s->recv_message_op != NULL) { + // This message needs to be wrapped up because it will never be + // satisfied + INPROC_LOG(GPR_DEBUG, "read_state_machine %p scheduling message-ready", + s); + GRPC_CLOSURE_SCHED( + exec_ctx, + s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + if (s->recv_message_op != s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p scheduling message-on-complete %p", s, + new_err); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, + GRPC_ERROR_REF(new_err)); + } + s->recv_message_op = NULL; + } + if (s->recv_trailing_md_op != NULL) { + // We wanted trailing metadata and we got it + s->trailing_md_recvd = true; + new_err = + fill_in_metadata(exec_ctx, s, &s->to_read_trailing_md, 0, + s->recv_trailing_md_op->payload + ->recv_trailing_metadata.recv_trailing_metadata, + NULL, NULL); + grpc_metadata_batch_clear(exec_ctx, &s->to_read_trailing_md); + s->to_read_trailing_md_filled = false; + + // We should schedule the recv_trailing_md_op completion if + // 1. this stream is the client-side + // 2. this stream is the server-side AND has already sent its trailing md + // (If the server hasn't already sent its trailing md, it doesn't have + // a final status, so don't mark this op complete) + if (s->t->is_client || s->trailing_md_sent) { + INPROC_LOG( + GPR_DEBUG, + "read_state_machine %p scheduling trailing-md-on-complete %p", s, + new_err); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, + GRPC_ERROR_REF(new_err)); + s->recv_trailing_md_op = NULL; + needs_close = true; + } else { + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p server needs to delay handling " + "trailing-md-on-complete %p", + s, new_err); + } + } else { + INPROC_LOG( + GPR_DEBUG, + "read_state_machine %p has trailing md but not yet waiting for it", + s); + } + } + if (s->trailing_md_recvd && s->recv_message_op) { + // No further message will come on this stream, so finish off the + // recv_message_op + INPROC_LOG(GPR_DEBUG, "read_state_machine %p scheduling message-ready", s); + GRPC_CLOSURE_SCHED( + exec_ctx, s->recv_message_op->payload->recv_message.recv_message_ready, + GRPC_ERROR_NONE); + if (s->recv_message_op != s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, + "read_state_machine %p scheduling message-on-complete %p", s, + new_err); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_message_op->on_complete, + GRPC_ERROR_REF(new_err)); + } + s->recv_message_op = NULL; + } + if (s->recv_message_op || s->recv_trailing_md_op) { + // Didn't get the item we wanted so we still need to get + // rescheduled + INPROC_LOG(GPR_DEBUG, "read_state_machine %p still needs closure %p %p", s, + s->recv_message_op, s->recv_trailing_md_op); + s->reads_needed = true; + } +done: + if (needs_close) { + close_other_side_locked(exec_ctx, s, "read_state_machine"); + close_stream_locked(exec_ctx, s); + } + gpr_mu_unlock(mu); + GRPC_ERROR_UNREF(new_err); +} + +static grpc_closure do_nothing_closure; + +static bool cancel_stream_locked(grpc_exec_ctx *exec_ctx, inproc_stream *s, + grpc_error *error) { + bool ret = false; // was the cancel accepted + INPROC_LOG(GPR_DEBUG, "cancel_stream %p with %s", s, + grpc_error_string(error)); + if (s->cancel_self_error == GRPC_ERROR_NONE) { + ret = true; + s->cancel_self_error = GRPC_ERROR_REF(error); + if (s->reads_needed) { + if (!s->read_closure_scheduled) { + GRPC_CLOSURE_SCHED(exec_ctx, &s->read_closure, + GRPC_ERROR_REF(s->cancel_self_error)); + s->read_closure_scheduled = true; + } + s->reads_needed = false; + } + // Send trailing md to the other side indicating cancellation, even if we + // already have + s->trailing_md_sent = true; + + grpc_metadata_batch cancel_md; + grpc_metadata_batch_init(&cancel_md); + + inproc_stream *other = s->other_side; + grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md + : &other->to_read_trailing_md; + bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled + : &other->to_read_trailing_md_filled; + fill_in_metadata(exec_ctx, s, &cancel_md, 0, dest, NULL, destfilled); + grpc_metadata_batch_destroy(exec_ctx, &cancel_md); + + if (other != NULL) { + if (other->cancel_other_error == GRPC_ERROR_NONE) { + other->cancel_other_error = GRPC_ERROR_REF(s->cancel_self_error); + } + if (other->reads_needed) { + if (!other->read_closure_scheduled) { + GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, + GRPC_ERROR_REF(other->cancel_other_error)); + other->read_closure_scheduled = true; + } + other->reads_needed = false; + } + } else if (s->write_buffer_cancel_error == GRPC_ERROR_NONE) { + s->write_buffer_cancel_error = GRPC_ERROR_REF(s->cancel_self_error); + } + + // if we are a server and already received trailing md but + // couldn't complete that because we hadn't yet sent out trailing + // md, now's the chance + if (!s->t->is_client && s->trailing_md_recvd && s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, + "cancel_stream %p scheduling trailing-md-on-complete %p", s, + s->cancel_self_error); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, + GRPC_ERROR_REF(s->cancel_self_error)); + s->recv_trailing_md_op = NULL; + } + } + + close_other_side_locked(exec_ctx, s, "cancel_stream:other_side"); + close_stream_locked(exec_ctx, s); + + GRPC_ERROR_UNREF(error); + return ret; +} + +static void perform_stream_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, + grpc_transport_stream_op_batch *op) { + INPROC_LOG(GPR_DEBUG, "perform_stream_op %p %p %p", gt, gs, op); + inproc_stream *s = (inproc_stream *)gs; + gpr_mu *mu = &s->t->mu->mu; // save aside in case s gets closed + gpr_mu_lock(mu); + + if (GRPC_TRACER_ON(grpc_inproc_trace)) { + if (op->send_initial_metadata) { + log_metadata(op->payload->send_initial_metadata.send_initial_metadata, + s->t->is_client, true); + } + if (op->send_trailing_metadata) { + log_metadata(op->payload->send_trailing_metadata.send_trailing_metadata, + s->t->is_client, false); + } + } + grpc_error *error = GRPC_ERROR_NONE; + grpc_closure *on_complete = op->on_complete; + if (on_complete == NULL) { + on_complete = &do_nothing_closure; + } + + if (op->cancel_stream) { + // Call cancel_stream_locked without ref'ing the cancel_error because + // this function is responsible to make sure that that field gets unref'ed + cancel_stream_locked(exec_ctx, s, op->payload->cancel_stream.cancel_error); + // this op can complete without an error + } else if (s->cancel_self_error != GRPC_ERROR_NONE) { + // already self-canceled so still give it an error + error = GRPC_ERROR_REF(s->cancel_self_error); + } else { + INPROC_LOG(GPR_DEBUG, "perform_stream_op %p%s%s%s%s%s%s", s, + op->send_initial_metadata ? " send_initial_metadata" : "", + op->send_message ? " send_message" : "", + op->send_trailing_metadata ? " send_trailing_metadata" : "", + op->recv_initial_metadata ? " recv_initial_metadata" : "", + op->recv_message ? " recv_message" : "", + op->recv_trailing_metadata ? " recv_trailing_metadata" : ""); + } + + bool needs_close = false; + + if (error == GRPC_ERROR_NONE && + (op->send_initial_metadata || op->send_message || + op->send_trailing_metadata)) { + inproc_stream *other = s->other_side; + if (s->t->is_closed) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Endpoint already shutdown"); + } + if (error == GRPC_ERROR_NONE && op->send_initial_metadata) { + grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_initial_md + : &other->to_read_initial_md; + uint32_t *destflags = (other == NULL) ? &s->write_buffer_initial_md_flags + : &other->to_read_initial_md_flags; + bool *destfilled = (other == NULL) ? &s->write_buffer_initial_md_filled + : &other->to_read_initial_md_filled; + if (*destfilled || s->initial_md_sent) { + // The buffer is already in use; that's an error! + INPROC_LOG(GPR_DEBUG, "Extra initial metadata %p", s); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra initial metadata"); + } else { + if (!other->closed) { + fill_in_metadata( + exec_ctx, s, + op->payload->send_initial_metadata.send_initial_metadata, + op->payload->send_initial_metadata.send_initial_metadata_flags, + dest, destflags, destfilled); + } + if (s->t->is_client) { + gpr_timespec *dl = + (other == NULL) ? &s->write_buffer_deadline : &other->deadline; + *dl = gpr_time_min(*dl, op->payload->send_initial_metadata + .send_initial_metadata->deadline); + s->initial_md_sent = true; + } + } + } + if (error == GRPC_ERROR_NONE && op->send_message) { + size_t remaining = op->payload->send_message.send_message->length; + grpc_slice_buffer *dest = slice_buffer_list_append( + (other == NULL) ? &s->write_buffer_message : &other->to_read_message); + do { + grpc_slice message_slice; + grpc_closure unused; + GPR_ASSERT(grpc_byte_stream_next(exec_ctx, + op->payload->send_message.send_message, + SIZE_MAX, &unused)); + error = grpc_byte_stream_pull( + exec_ctx, op->payload->send_message.send_message, &message_slice); + if (error != GRPC_ERROR_NONE) { + cancel_stream_locked(exec_ctx, s, GRPC_ERROR_REF(error)); + break; + } + GPR_ASSERT(error == GRPC_ERROR_NONE); + remaining -= GRPC_SLICE_LENGTH(message_slice); + grpc_slice_buffer_add(dest, message_slice); + } while (remaining != 0); + grpc_byte_stream_destroy(exec_ctx, + op->payload->send_message.send_message); + } + if (error == GRPC_ERROR_NONE && op->send_trailing_metadata) { + grpc_metadata_batch *dest = (other == NULL) ? &s->write_buffer_trailing_md + : &other->to_read_trailing_md; + bool *destfilled = (other == NULL) ? &s->write_buffer_trailing_md_filled + : &other->to_read_trailing_md_filled; + if (*destfilled || s->trailing_md_sent) { + // The buffer is already in use; that's an error! + INPROC_LOG(GPR_DEBUG, "Extra trailing metadata %p", s); + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Extra trailing metadata"); + } else { + if (!other->closed) { + fill_in_metadata( + exec_ctx, s, + op->payload->send_trailing_metadata.send_trailing_metadata, 0, + dest, NULL, destfilled); + } + s->trailing_md_sent = true; + if (!s->t->is_client && s->trailing_md_recvd && + s->recv_trailing_md_op) { + INPROC_LOG(GPR_DEBUG, + "perform_stream_op %p scheduling trailing-md-on-complete", + s); + GRPC_CLOSURE_SCHED(exec_ctx, s->recv_trailing_md_op->on_complete, + GRPC_ERROR_NONE); + s->recv_trailing_md_op = NULL; + needs_close = true; + } + } + } + if (other != NULL && other->reads_needed) { + if (!other->read_closure_scheduled) { + GRPC_CLOSURE_SCHED(exec_ctx, &other->read_closure, error); + other->read_closure_scheduled = true; + } + other->reads_needed = false; + } + } + if (error == GRPC_ERROR_NONE && + (op->recv_initial_metadata || op->recv_message || + op->recv_trailing_metadata)) { + // If there are any reads, mark it so that the read closure will react to + // them + if (op->recv_initial_metadata) { + s->recv_initial_md_op = op; + } + if (op->recv_message) { + s->recv_message_op = op; + } + if (op->recv_trailing_metadata) { + s->recv_trailing_md_op = op; + } + + // We want to initiate the closure if: + // 1. There is initial metadata and something ready to take that + // 2. There is a message and something ready to take it + // 3. There is trailing metadata, even if nothing specifically wants + // that because that can shut down the message as well + if ((s->to_read_initial_md_filled && op->recv_initial_metadata) || + ((!slice_buffer_list_empty(&s->to_read_message) || + s->trailing_md_recvd) && + op->recv_message) || + (s->to_read_trailing_md_filled)) { + if (!s->read_closure_scheduled) { + GRPC_CLOSURE_SCHED(exec_ctx, &s->read_closure, GRPC_ERROR_NONE); + s->read_closure_scheduled = true; + } + } else { + s->reads_needed = true; + } + } else { + if (error != GRPC_ERROR_NONE) { + // Schedule op's read closures that we didn't push to read state machine + if (op->recv_initial_metadata) { + INPROC_LOG( + GPR_DEBUG, + "perform_stream_op error %p scheduling initial-metadata-ready %p", + s, error); + GRPC_CLOSURE_SCHED( + exec_ctx, + op->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); + } + if (op->recv_message) { + INPROC_LOG( + GPR_DEBUG, + "perform_stream_op error %p scheduling recv message-ready %p", s, + error); + GRPC_CLOSURE_SCHED(exec_ctx, + op->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error)); + } + } + INPROC_LOG(GPR_DEBUG, "perform_stream_op %p scheduling on_complete %p", s, + error); + GRPC_CLOSURE_SCHED(exec_ctx, on_complete, GRPC_ERROR_REF(error)); + } + if (needs_close) { + close_other_side_locked(exec_ctx, s, "perform_stream_op:other_side"); + close_stream_locked(exec_ctx, s); + } + gpr_mu_unlock(mu); + GRPC_ERROR_UNREF(error); +} + +static void close_transport_locked(grpc_exec_ctx *exec_ctx, + inproc_transport *t) { + INPROC_LOG(GPR_DEBUG, "close_transport %p %d", t, t->is_closed); + grpc_connectivity_state_set( + exec_ctx, &t->connectivity, GRPC_CHANNEL_SHUTDOWN, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Closing transport."), + "close transport"); + if (!t->is_closed) { + t->is_closed = true; + /* Also end all streams on this transport */ + while (t->stream_list != NULL) { + // cancel_stream_locked also adjusts stream list + cancel_stream_locked( + exec_ctx, t->stream_list, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Transport closed"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE)); + } + } +} + +static void perform_transport_op(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_transport_op *op) { + inproc_transport *t = (inproc_transport *)gt; + INPROC_LOG(GPR_DEBUG, "perform_transport_op %p %p", t, op); + gpr_mu_lock(&t->mu->mu); + if (op->on_connectivity_state_change) { + grpc_connectivity_state_notify_on_state_change( + exec_ctx, &t->connectivity, op->connectivity_state, + op->on_connectivity_state_change); + } + if (op->set_accept_stream) { + t->accept_stream_cb = op->set_accept_stream_fn; + t->accept_stream_data = op->set_accept_stream_user_data; + } + if (op->on_consumed) { + GRPC_CLOSURE_SCHED(exec_ctx, op->on_consumed, GRPC_ERROR_NONE); + } + + bool do_close = false; + if (op->goaway_error != GRPC_ERROR_NONE) { + do_close = true; + GRPC_ERROR_UNREF(op->goaway_error); + } + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + do_close = true; + GRPC_ERROR_UNREF(op->disconnect_with_error); + } + + if (do_close) { + close_transport_locked(exec_ctx, t); + } + gpr_mu_unlock(&t->mu->mu); +} + +static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, + grpc_closure *then_schedule_closure) { + INPROC_LOG(GPR_DEBUG, "destroy_stream %p %p", gs, then_schedule_closure); + inproc_stream *s = (inproc_stream *)gs; + s->closure_at_destroy = then_schedule_closure; + really_destroy_stream(exec_ctx, s); +} + +static void destroy_transport(grpc_exec_ctx *exec_ctx, grpc_transport *gt) { + inproc_transport *t = (inproc_transport *)gt; + INPROC_LOG(GPR_DEBUG, "destroy_transport %p", t); + gpr_mu_lock(&t->mu->mu); + close_transport_locked(exec_ctx, t); + gpr_mu_unlock(&t->mu->mu); + unref_transport(exec_ctx, t->other_side); + unref_transport(exec_ctx, t); +} + +/******************************************************************************* + * INTEGRATION GLUE + */ + +static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_pollset *pollset) { + // Nothing to do here +} + +static void set_pollset_set(grpc_exec_ctx *exec_ctx, grpc_transport *gt, + grpc_stream *gs, grpc_pollset_set *pollset_set) { + // Nothing to do here +} + +static grpc_endpoint *get_endpoint(grpc_exec_ctx *exec_ctx, grpc_transport *t) { + return NULL; +} + +/******************************************************************************* + * GLOBAL INIT AND DESTROY + */ +static void do_nothing(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) {} + +void grpc_inproc_transport_init(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_CLOSURE_INIT(&do_nothing_closure, do_nothing, NULL, + grpc_schedule_on_exec_ctx); + g_empty_slice = grpc_slice_from_static_buffer(NULL, 0); + + grpc_slice key_tmp = grpc_slice_from_static_string(":path"); + g_fake_path_key = grpc_slice_intern(key_tmp); + grpc_slice_unref_internal(&exec_ctx, key_tmp); + + g_fake_path_value = grpc_slice_from_static_string("/"); + + grpc_slice auth_tmp = grpc_slice_from_static_string(":authority"); + g_fake_auth_key = grpc_slice_intern(auth_tmp); + grpc_slice_unref_internal(&exec_ctx, auth_tmp); + + g_fake_auth_value = grpc_slice_from_static_string("inproc-fail"); + grpc_exec_ctx_finish(&exec_ctx); +} + +static const grpc_transport_vtable inproc_vtable = { + sizeof(inproc_stream), "inproc", init_stream, + set_pollset, set_pollset_set, perform_stream_op, + perform_transport_op, destroy_stream, destroy_transport, + get_endpoint}; + +/******************************************************************************* + * Main inproc transport functions + */ +static void inproc_transports_create(grpc_exec_ctx *exec_ctx, + grpc_transport **server_transport, + const grpc_channel_args *server_args, + grpc_transport **client_transport, + const grpc_channel_args *client_args) { + INPROC_LOG(GPR_DEBUG, "inproc_transports_create"); + inproc_transport *st = (inproc_transport *)gpr_zalloc(sizeof(*st)); + inproc_transport *ct = (inproc_transport *)gpr_zalloc(sizeof(*ct)); + // Share one lock between both sides since both sides get affected + st->mu = ct->mu = (shared_mu *)gpr_malloc(sizeof(*st->mu)); + gpr_mu_init(&st->mu->mu); + gpr_ref_init(&st->mu->refs, 2); + st->base.vtable = &inproc_vtable; + ct->base.vtable = &inproc_vtable; + // Start each side of transport with 2 refs since they each have a ref + // to the other + gpr_ref_init(&st->refs, 2); + gpr_ref_init(&ct->refs, 2); + st->is_client = false; + ct->is_client = true; + grpc_connectivity_state_init(&st->connectivity, GRPC_CHANNEL_READY, + "inproc_server"); + grpc_connectivity_state_init(&ct->connectivity, GRPC_CHANNEL_READY, + "inproc_client"); + st->other_side = ct; + ct->other_side = st; + st->stream_list = NULL; + ct->stream_list = NULL; + *server_transport = (grpc_transport *)st; + *client_transport = (grpc_transport *)ct; +} + +grpc_channel *grpc_inproc_channel_create(grpc_server *server, + grpc_channel_args *args, + void *reserved) { + GRPC_API_TRACE("grpc_inproc_channel_create(server=%p, args=%p)", 2, + (server, args)); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + const grpc_channel_args *server_args = grpc_server_get_channel_args(server); + + // Add a default authority channel argument for the client + + grpc_arg default_authority_arg; + default_authority_arg.type = GRPC_ARG_STRING; + default_authority_arg.key = (char *)GRPC_ARG_DEFAULT_AUTHORITY; + default_authority_arg.value.string = (char *)"inproc.authority"; + grpc_channel_args *client_args = + grpc_channel_args_copy_and_add(args, &default_authority_arg, 1); + + grpc_transport *server_transport; + grpc_transport *client_transport; + inproc_transports_create(&exec_ctx, &server_transport, server_args, + &client_transport, client_args); + + grpc_server_setup_transport(&exec_ctx, server, server_transport, NULL, + server_args); + grpc_channel *channel = + grpc_channel_create(&exec_ctx, "inproc", client_args, + GRPC_CLIENT_DIRECT_CHANNEL, client_transport); + + // Free up created channel args + grpc_channel_args_destroy(&exec_ctx, client_args); + + // Now finish scheduled operations + grpc_exec_ctx_finish(&exec_ctx); + + return channel; +} + +void grpc_inproc_transport_shutdown(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_unref_internal(&exec_ctx, g_empty_slice); + grpc_slice_unref_internal(&exec_ctx, g_fake_path_key); + grpc_slice_unref_internal(&exec_ctx, g_fake_path_value); + grpc_slice_unref_internal(&exec_ctx, g_fake_auth_key); + grpc_slice_unref_internal(&exec_ctx, g_fake_auth_value); + grpc_exec_ctx_finish(&exec_ctx); +} diff --git a/src/core/lib/channel/channel_args.c b/src/core/lib/channel/channel_args.c deleted file mode 100644 index 30248b3c60..0000000000 --- a/src/core/lib/channel/channel_args.c +++ /dev/null @@ -1,501 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/support/string.h" - -static grpc_arg copy_arg(const grpc_arg *src) { - grpc_arg dst; - dst.type = src->type; - dst.key = gpr_strdup(src->key); - switch (dst.type) { - case GRPC_ARG_STRING: - dst.value.string = gpr_strdup(src->value.string); - break; - case GRPC_ARG_INTEGER: - dst.value.integer = src->value.integer; - break; - case GRPC_ARG_POINTER: - dst.value.pointer = src->value.pointer; - dst.value.pointer.p = - src->value.pointer.vtable->copy(src->value.pointer.p); - break; - } - return dst; -} - -grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, - const grpc_arg *to_add, - size_t num_to_add) { - return grpc_channel_args_copy_and_add_and_remove(src, NULL, 0, to_add, - num_to_add); -} - -grpc_channel_args *grpc_channel_args_copy_and_remove( - const grpc_channel_args *src, const char **to_remove, - size_t num_to_remove) { - return grpc_channel_args_copy_and_add_and_remove(src, to_remove, - num_to_remove, NULL, 0); -} - -static bool should_remove_arg(const grpc_arg *arg, const char **to_remove, - size_t num_to_remove) { - for (size_t i = 0; i < num_to_remove; ++i) { - if (strcmp(arg->key, to_remove[i]) == 0) return true; - } - return false; -} - -grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( - const grpc_channel_args *src, const char **to_remove, size_t num_to_remove, - const grpc_arg *to_add, size_t num_to_add) { - // Figure out how many args we'll be copying. - size_t num_args_to_copy = 0; - if (src != NULL) { - for (size_t i = 0; i < src->num_args; ++i) { - if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { - ++num_args_to_copy; - } - } - } - // Create result. - grpc_channel_args *dst = - (grpc_channel_args *)gpr_malloc(sizeof(grpc_channel_args)); - dst->num_args = num_args_to_copy + num_to_add; - if (dst->num_args == 0) { - dst->args = NULL; - return dst; - } - dst->args = (grpc_arg *)gpr_malloc(sizeof(grpc_arg) * dst->num_args); - // Copy args from src that are not being removed. - size_t dst_idx = 0; - if (src != NULL) { - for (size_t i = 0; i < src->num_args; ++i) { - if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { - dst->args[dst_idx++] = copy_arg(&src->args[i]); - } - } - } - // Add args from to_add. - for (size_t i = 0; i < num_to_add; ++i) { - dst->args[dst_idx++] = copy_arg(&to_add[i]); - } - GPR_ASSERT(dst_idx == dst->num_args); - return dst; -} - -grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src) { - return grpc_channel_args_copy_and_add(src, NULL, 0); -} - -grpc_channel_args *grpc_channel_args_union(const grpc_channel_args *a, - const grpc_channel_args *b) { - const size_t max_out = (a->num_args + b->num_args); - grpc_arg *uniques = (grpc_arg *)gpr_malloc(sizeof(*uniques) * max_out); - for (size_t i = 0; i < a->num_args; ++i) uniques[i] = a->args[i]; - - size_t uniques_idx = a->num_args; - for (size_t i = 0; i < b->num_args; ++i) { - const char *b_key = b->args[i].key; - if (grpc_channel_args_find(a, b_key) == NULL) { // not found - uniques[uniques_idx++] = b->args[i]; - } - } - grpc_channel_args *result = - grpc_channel_args_copy_and_add(NULL, uniques, uniques_idx); - gpr_free(uniques); - return result; -} - -static int cmp_arg(const grpc_arg *a, const grpc_arg *b) { - int c = GPR_ICMP(a->type, b->type); - if (c != 0) return c; - c = strcmp(a->key, b->key); - if (c != 0) return c; - switch (a->type) { - case GRPC_ARG_STRING: - return strcmp(a->value.string, b->value.string); - case GRPC_ARG_INTEGER: - return GPR_ICMP(a->value.integer, b->value.integer); - case GRPC_ARG_POINTER: - c = GPR_ICMP(a->value.pointer.p, b->value.pointer.p); - if (c != 0) { - c = GPR_ICMP(a->value.pointer.vtable, b->value.pointer.vtable); - if (c == 0) { - c = a->value.pointer.vtable->cmp(a->value.pointer.p, - b->value.pointer.p); - } - } - return c; - } - GPR_UNREACHABLE_CODE(return 0); -} - -/* stabilizing comparison function: since channel_args ordering matters for - * keys with the same name, we need to preserve that ordering */ -static int cmp_key_stable(const void *ap, const void *bp) { - const grpc_arg *const *a = (const grpc_arg *const *)ap; - const grpc_arg *const *b = (const grpc_arg *const *)bp; - int c = strcmp((*a)->key, (*b)->key); - if (c == 0) c = GPR_ICMP(*a, *b); - return c; -} - -grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a) { - grpc_arg **args = (grpc_arg **)gpr_malloc(sizeof(grpc_arg *) * a->num_args); - for (size_t i = 0; i < a->num_args; i++) { - args[i] = &a->args[i]; - } - if (a->num_args > 1) - qsort(args, a->num_args, sizeof(grpc_arg *), cmp_key_stable); - - grpc_channel_args *b = - (grpc_channel_args *)gpr_malloc(sizeof(grpc_channel_args)); - b->num_args = a->num_args; - b->args = (grpc_arg *)gpr_malloc(sizeof(grpc_arg) * b->num_args); - for (size_t i = 0; i < a->num_args; i++) { - b->args[i] = copy_arg(args[i]); - } - - gpr_free(args); - return b; -} - -void grpc_channel_args_destroy(grpc_exec_ctx *exec_ctx, grpc_channel_args *a) { - size_t i; - if (!a) return; - for (i = 0; i < a->num_args; i++) { - switch (a->args[i].type) { - case GRPC_ARG_STRING: - gpr_free(a->args[i].value.string); - break; - case GRPC_ARG_INTEGER: - break; - case GRPC_ARG_POINTER: - a->args[i].value.pointer.vtable->destroy(exec_ctx, - a->args[i].value.pointer.p); - break; - } - gpr_free(a->args[i].key); - } - gpr_free(a->args); - gpr_free(a); -} - -grpc_compression_algorithm grpc_channel_args_get_compression_algorithm( - const grpc_channel_args *a) { - size_t i; - if (a == NULL) return GRPC_COMPRESS_NONE; - for (i = 0; i < a->num_args; ++i) { - if (a->args[i].type == GRPC_ARG_INTEGER && - !strcmp(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, a->args[i].key)) { - return (grpc_compression_algorithm)a->args[i].value.integer; - break; - } - } - return GRPC_COMPRESS_NONE; -} - -grpc_stream_compression_algorithm -grpc_channel_args_get_stream_compression_algorithm(const grpc_channel_args *a) { - size_t i; - if (a == NULL) return GRPC_STREAM_COMPRESS_NONE; - for (i = 0; i < a->num_args; ++i) { - if (a->args[i].type == GRPC_ARG_INTEGER && - !strcmp(GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, - a->args[i].key)) { - return (grpc_stream_compression_algorithm)a->args[i].value.integer; - break; - } - } - return GRPC_STREAM_COMPRESS_NONE; -} - -grpc_channel_args *grpc_channel_args_set_compression_algorithm( - grpc_channel_args *a, grpc_compression_algorithm algorithm) { - GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT); - grpc_arg tmp; - tmp.type = GRPC_ARG_INTEGER; - tmp.key = (char *)GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM; - tmp.value.integer = algorithm; - return grpc_channel_args_copy_and_add(a, &tmp, 1); -} - -grpc_channel_args *grpc_channel_args_set_stream_compression_algorithm( - grpc_channel_args *a, grpc_stream_compression_algorithm algorithm) { - GPR_ASSERT(algorithm < GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT); - grpc_arg tmp; - tmp.type = GRPC_ARG_INTEGER; - tmp.key = (char *)GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM; - tmp.value.integer = algorithm; - return grpc_channel_args_copy_and_add(a, &tmp, 1); -} - -/** Returns 1 if the argument for compression algorithm's enabled states bitset - * was found in \a a, returning the arg's value in \a states. Otherwise, returns - * 0. */ -static int find_compression_algorithm_states_bitset(const grpc_channel_args *a, - int **states_arg) { - if (a != NULL) { - size_t i; - for (i = 0; i < a->num_args; ++i) { - if (a->args[i].type == GRPC_ARG_INTEGER && - !strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, - a->args[i].key)) { - *states_arg = &a->args[i].value.integer; - **states_arg |= 0x1; /* forcefully enable support for no compression */ - return 1; - } - } - } - return 0; /* GPR_FALSE */ -} - -/** Returns 1 if the argument for compression algorithm's enabled states bitset - * was found in \a a, returning the arg's value in \a states. Otherwise, returns - * 0. */ -static int find_stream_compression_algorithm_states_bitset( - const grpc_channel_args *a, int **states_arg) { - if (a != NULL) { - size_t i; - for (i = 0; i < a->num_args; ++i) { - if (a->args[i].type == GRPC_ARG_INTEGER && - !strcmp(GRPC_STREAM_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, - a->args[i].key)) { - *states_arg = &a->args[i].value.integer; - **states_arg |= 0x1; /* forcefully enable support for no compression */ - return 1; - } - } - } - return 0; /* GPR_FALSE */ -} - -grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( - grpc_exec_ctx *exec_ctx, grpc_channel_args **a, - grpc_compression_algorithm algorithm, int state) { - int *states_arg = NULL; - grpc_channel_args *result = *a; - const int states_arg_found = - find_compression_algorithm_states_bitset(*a, &states_arg); - - if (grpc_channel_args_get_compression_algorithm(*a) == algorithm && - state == 0) { - const char *algo_name = NULL; - GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0); - gpr_log(GPR_ERROR, - "Tried to disable default compression algorithm '%s'. The " - "operation has been ignored.", - algo_name); - } else if (states_arg_found) { - if (state != 0) { - GPR_BITSET((unsigned *)states_arg, algorithm); - } else if (algorithm != GRPC_COMPRESS_NONE) { - GPR_BITCLEAR((unsigned *)states_arg, algorithm); - } - } else { - /* create a new arg */ - grpc_arg tmp; - tmp.type = GRPC_ARG_INTEGER; - tmp.key = (char *)GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET; - /* all enabled by default */ - tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; - if (state != 0) { - GPR_BITSET((unsigned *)&tmp.value.integer, algorithm); - } else if (algorithm != GRPC_COMPRESS_NONE) { - GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm); - } - result = grpc_channel_args_copy_and_add(*a, &tmp, 1); - grpc_channel_args_destroy(exec_ctx, *a); - *a = result; - } - return result; -} - -grpc_channel_args *grpc_channel_args_stream_compression_algorithm_set_state( - grpc_exec_ctx *exec_ctx, grpc_channel_args **a, - grpc_stream_compression_algorithm algorithm, int state) { - int *states_arg = NULL; - grpc_channel_args *result = *a; - const int states_arg_found = - find_stream_compression_algorithm_states_bitset(*a, &states_arg); - - if (grpc_channel_args_get_stream_compression_algorithm(*a) == algorithm && - state == 0) { - const char *algo_name = NULL; - GPR_ASSERT(grpc_stream_compression_algorithm_name(algorithm, &algo_name) != - 0); - gpr_log(GPR_ERROR, - "Tried to disable default stream compression algorithm '%s'. The " - "operation has been ignored.", - algo_name); - } else if (states_arg_found) { - if (state != 0) { - GPR_BITSET((unsigned *)states_arg, algorithm); - } else if (algorithm != GRPC_STREAM_COMPRESS_NONE) { - GPR_BITCLEAR((unsigned *)states_arg, algorithm); - } - } else { - /* create a new arg */ - grpc_arg tmp; - tmp.type = GRPC_ARG_INTEGER; - tmp.key = (char *)GRPC_STREAM_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET; - /* all enabled by default */ - tmp.value.integer = (1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 1; - if (state != 0) { - GPR_BITSET((unsigned *)&tmp.value.integer, algorithm); - } else if (algorithm != GRPC_STREAM_COMPRESS_NONE) { - GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm); - } - result = grpc_channel_args_copy_and_add(*a, &tmp, 1); - grpc_channel_args_destroy(exec_ctx, *a); - *a = result; - } - return result; -} - -uint32_t grpc_channel_args_compression_algorithm_get_states( - const grpc_channel_args *a) { - int *states_arg; - if (find_compression_algorithm_states_bitset(a, &states_arg)) { - return (uint32_t)*states_arg; - } else { - return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */ - } -} - -uint32_t grpc_channel_args_stream_compression_algorithm_get_states( - const grpc_channel_args *a) { - int *states_arg; - if (find_stream_compression_algorithm_states_bitset(a, &states_arg)) { - return (uint32_t)*states_arg; - } else { - return (1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - - 1; /* All algs. enabled */ - } -} - -grpc_channel_args *grpc_channel_args_set_socket_mutator( - grpc_channel_args *a, grpc_socket_mutator *mutator) { - grpc_arg tmp = grpc_socket_mutator_to_arg(mutator); - return grpc_channel_args_copy_and_add(a, &tmp, 1); -} - -int grpc_channel_args_compare(const grpc_channel_args *a, - const grpc_channel_args *b) { - int c = GPR_ICMP(a->num_args, b->num_args); - if (c != 0) return c; - for (size_t i = 0; i < a->num_args; i++) { - c = cmp_arg(&a->args[i], &b->args[i]); - if (c != 0) return c; - } - return 0; -} - -const grpc_arg *grpc_channel_args_find(const grpc_channel_args *args, - const char *name) { - if (args != NULL) { - for (size_t i = 0; i < args->num_args; ++i) { - if (strcmp(args->args[i].key, name) == 0) { - return &args->args[i]; - } - } - } - return NULL; -} - -int grpc_channel_arg_get_integer(const grpc_arg *arg, - const grpc_integer_options options) { - if (arg == NULL) return options.default_value; - if (arg->type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key); - return options.default_value; - } - if (arg->value.integer < options.min_value) { - gpr_log(GPR_ERROR, "%s ignored: it must be >= %d", arg->key, - options.min_value); - return options.default_value; - } - if (arg->value.integer > options.max_value) { - gpr_log(GPR_ERROR, "%s ignored: it must be <= %d", arg->key, - options.max_value); - return options.default_value; - } - return arg->value.integer; -} - -bool grpc_channel_arg_get_bool(const grpc_arg *arg, bool default_value) { - if (arg == NULL) return default_value; - if (arg->type != GRPC_ARG_INTEGER) { - gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key); - return default_value; - } - switch (arg->value.integer) { - case 0: - return false; - case 1: - return true; - default: - gpr_log(GPR_ERROR, "%s treated as bool but set to %d (assuming true)", - arg->key, arg->value.integer); - return true; - } -} - -bool grpc_channel_args_want_minimal_stack(const grpc_channel_args *args) { - return grpc_channel_arg_get_bool( - grpc_channel_args_find(args, GRPC_ARG_MINIMAL_STACK), false); -} - -grpc_arg grpc_channel_arg_string_create(char *name, char *value) { - grpc_arg arg; - arg.type = GRPC_ARG_STRING; - arg.key = name; - arg.value.string = value; - return arg; -} - -grpc_arg grpc_channel_arg_integer_create(char *name, int value) { - grpc_arg arg; - arg.type = GRPC_ARG_INTEGER; - arg.key = name; - arg.value.integer = value; - return arg; -} - -grpc_arg grpc_channel_arg_pointer_create( - char *name, void *value, const grpc_arg_pointer_vtable *vtable) { - grpc_arg arg; - arg.type = GRPC_ARG_POINTER; - arg.key = name; - arg.value.pointer.p = value; - arg.value.pointer.vtable = vtable; - return arg; -} diff --git a/src/core/lib/channel/channel_args.cc b/src/core/lib/channel/channel_args.cc new file mode 100644 index 0000000000..30248b3c60 --- /dev/null +++ b/src/core/lib/channel/channel_args.cc @@ -0,0 +1,501 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/support/string.h" + +static grpc_arg copy_arg(const grpc_arg *src) { + grpc_arg dst; + dst.type = src->type; + dst.key = gpr_strdup(src->key); + switch (dst.type) { + case GRPC_ARG_STRING: + dst.value.string = gpr_strdup(src->value.string); + break; + case GRPC_ARG_INTEGER: + dst.value.integer = src->value.integer; + break; + case GRPC_ARG_POINTER: + dst.value.pointer = src->value.pointer; + dst.value.pointer.p = + src->value.pointer.vtable->copy(src->value.pointer.p); + break; + } + return dst; +} + +grpc_channel_args *grpc_channel_args_copy_and_add(const grpc_channel_args *src, + const grpc_arg *to_add, + size_t num_to_add) { + return grpc_channel_args_copy_and_add_and_remove(src, NULL, 0, to_add, + num_to_add); +} + +grpc_channel_args *grpc_channel_args_copy_and_remove( + const grpc_channel_args *src, const char **to_remove, + size_t num_to_remove) { + return grpc_channel_args_copy_and_add_and_remove(src, to_remove, + num_to_remove, NULL, 0); +} + +static bool should_remove_arg(const grpc_arg *arg, const char **to_remove, + size_t num_to_remove) { + for (size_t i = 0; i < num_to_remove; ++i) { + if (strcmp(arg->key, to_remove[i]) == 0) return true; + } + return false; +} + +grpc_channel_args *grpc_channel_args_copy_and_add_and_remove( + const grpc_channel_args *src, const char **to_remove, size_t num_to_remove, + const grpc_arg *to_add, size_t num_to_add) { + // Figure out how many args we'll be copying. + size_t num_args_to_copy = 0; + if (src != NULL) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + ++num_args_to_copy; + } + } + } + // Create result. + grpc_channel_args *dst = + (grpc_channel_args *)gpr_malloc(sizeof(grpc_channel_args)); + dst->num_args = num_args_to_copy + num_to_add; + if (dst->num_args == 0) { + dst->args = NULL; + return dst; + } + dst->args = (grpc_arg *)gpr_malloc(sizeof(grpc_arg) * dst->num_args); + // Copy args from src that are not being removed. + size_t dst_idx = 0; + if (src != NULL) { + for (size_t i = 0; i < src->num_args; ++i) { + if (!should_remove_arg(&src->args[i], to_remove, num_to_remove)) { + dst->args[dst_idx++] = copy_arg(&src->args[i]); + } + } + } + // Add args from to_add. + for (size_t i = 0; i < num_to_add; ++i) { + dst->args[dst_idx++] = copy_arg(&to_add[i]); + } + GPR_ASSERT(dst_idx == dst->num_args); + return dst; +} + +grpc_channel_args *grpc_channel_args_copy(const grpc_channel_args *src) { + return grpc_channel_args_copy_and_add(src, NULL, 0); +} + +grpc_channel_args *grpc_channel_args_union(const grpc_channel_args *a, + const grpc_channel_args *b) { + const size_t max_out = (a->num_args + b->num_args); + grpc_arg *uniques = (grpc_arg *)gpr_malloc(sizeof(*uniques) * max_out); + for (size_t i = 0; i < a->num_args; ++i) uniques[i] = a->args[i]; + + size_t uniques_idx = a->num_args; + for (size_t i = 0; i < b->num_args; ++i) { + const char *b_key = b->args[i].key; + if (grpc_channel_args_find(a, b_key) == NULL) { // not found + uniques[uniques_idx++] = b->args[i]; + } + } + grpc_channel_args *result = + grpc_channel_args_copy_and_add(NULL, uniques, uniques_idx); + gpr_free(uniques); + return result; +} + +static int cmp_arg(const grpc_arg *a, const grpc_arg *b) { + int c = GPR_ICMP(a->type, b->type); + if (c != 0) return c; + c = strcmp(a->key, b->key); + if (c != 0) return c; + switch (a->type) { + case GRPC_ARG_STRING: + return strcmp(a->value.string, b->value.string); + case GRPC_ARG_INTEGER: + return GPR_ICMP(a->value.integer, b->value.integer); + case GRPC_ARG_POINTER: + c = GPR_ICMP(a->value.pointer.p, b->value.pointer.p); + if (c != 0) { + c = GPR_ICMP(a->value.pointer.vtable, b->value.pointer.vtable); + if (c == 0) { + c = a->value.pointer.vtable->cmp(a->value.pointer.p, + b->value.pointer.p); + } + } + return c; + } + GPR_UNREACHABLE_CODE(return 0); +} + +/* stabilizing comparison function: since channel_args ordering matters for + * keys with the same name, we need to preserve that ordering */ +static int cmp_key_stable(const void *ap, const void *bp) { + const grpc_arg *const *a = (const grpc_arg *const *)ap; + const grpc_arg *const *b = (const grpc_arg *const *)bp; + int c = strcmp((*a)->key, (*b)->key); + if (c == 0) c = GPR_ICMP(*a, *b); + return c; +} + +grpc_channel_args *grpc_channel_args_normalize(const grpc_channel_args *a) { + grpc_arg **args = (grpc_arg **)gpr_malloc(sizeof(grpc_arg *) * a->num_args); + for (size_t i = 0; i < a->num_args; i++) { + args[i] = &a->args[i]; + } + if (a->num_args > 1) + qsort(args, a->num_args, sizeof(grpc_arg *), cmp_key_stable); + + grpc_channel_args *b = + (grpc_channel_args *)gpr_malloc(sizeof(grpc_channel_args)); + b->num_args = a->num_args; + b->args = (grpc_arg *)gpr_malloc(sizeof(grpc_arg) * b->num_args); + for (size_t i = 0; i < a->num_args; i++) { + b->args[i] = copy_arg(args[i]); + } + + gpr_free(args); + return b; +} + +void grpc_channel_args_destroy(grpc_exec_ctx *exec_ctx, grpc_channel_args *a) { + size_t i; + if (!a) return; + for (i = 0; i < a->num_args; i++) { + switch (a->args[i].type) { + case GRPC_ARG_STRING: + gpr_free(a->args[i].value.string); + break; + case GRPC_ARG_INTEGER: + break; + case GRPC_ARG_POINTER: + a->args[i].value.pointer.vtable->destroy(exec_ctx, + a->args[i].value.pointer.p); + break; + } + gpr_free(a->args[i].key); + } + gpr_free(a->args); + gpr_free(a); +} + +grpc_compression_algorithm grpc_channel_args_get_compression_algorithm( + const grpc_channel_args *a) { + size_t i; + if (a == NULL) return GRPC_COMPRESS_NONE; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, a->args[i].key)) { + return (grpc_compression_algorithm)a->args[i].value.integer; + break; + } + } + return GRPC_COMPRESS_NONE; +} + +grpc_stream_compression_algorithm +grpc_channel_args_get_stream_compression_algorithm(const grpc_channel_args *a) { + size_t i; + if (a == NULL) return GRPC_STREAM_COMPRESS_NONE; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM, + a->args[i].key)) { + return (grpc_stream_compression_algorithm)a->args[i].value.integer; + break; + } + } + return GRPC_STREAM_COMPRESS_NONE; +} + +grpc_channel_args *grpc_channel_args_set_compression_algorithm( + grpc_channel_args *a, grpc_compression_algorithm algorithm) { + GPR_ASSERT(algorithm < GRPC_COMPRESS_ALGORITHMS_COUNT); + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = (char *)GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM; + tmp.value.integer = algorithm; + return grpc_channel_args_copy_and_add(a, &tmp, 1); +} + +grpc_channel_args *grpc_channel_args_set_stream_compression_algorithm( + grpc_channel_args *a, grpc_stream_compression_algorithm algorithm) { + GPR_ASSERT(algorithm < GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT); + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = (char *)GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM; + tmp.value.integer = algorithm; + return grpc_channel_args_copy_and_add(a, &tmp, 1); +} + +/** Returns 1 if the argument for compression algorithm's enabled states bitset + * was found in \a a, returning the arg's value in \a states. Otherwise, returns + * 0. */ +static int find_compression_algorithm_states_bitset(const grpc_channel_args *a, + int **states_arg) { + if (a != NULL) { + size_t i; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, + a->args[i].key)) { + *states_arg = &a->args[i].value.integer; + **states_arg |= 0x1; /* forcefully enable support for no compression */ + return 1; + } + } + } + return 0; /* GPR_FALSE */ +} + +/** Returns 1 if the argument for compression algorithm's enabled states bitset + * was found in \a a, returning the arg's value in \a states. Otherwise, returns + * 0. */ +static int find_stream_compression_algorithm_states_bitset( + const grpc_channel_args *a, int **states_arg) { + if (a != NULL) { + size_t i; + for (i = 0; i < a->num_args; ++i) { + if (a->args[i].type == GRPC_ARG_INTEGER && + !strcmp(GRPC_STREAM_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET, + a->args[i].key)) { + *states_arg = &a->args[i].value.integer; + **states_arg |= 0x1; /* forcefully enable support for no compression */ + return 1; + } + } + } + return 0; /* GPR_FALSE */ +} + +grpc_channel_args *grpc_channel_args_compression_algorithm_set_state( + grpc_exec_ctx *exec_ctx, grpc_channel_args **a, + grpc_compression_algorithm algorithm, int state) { + int *states_arg = NULL; + grpc_channel_args *result = *a; + const int states_arg_found = + find_compression_algorithm_states_bitset(*a, &states_arg); + + if (grpc_channel_args_get_compression_algorithm(*a) == algorithm && + state == 0) { + const char *algo_name = NULL; + GPR_ASSERT(grpc_compression_algorithm_name(algorithm, &algo_name) != 0); + gpr_log(GPR_ERROR, + "Tried to disable default compression algorithm '%s'. The " + "operation has been ignored.", + algo_name); + } else if (states_arg_found) { + if (state != 0) { + GPR_BITSET((unsigned *)states_arg, algorithm); + } else if (algorithm != GRPC_COMPRESS_NONE) { + GPR_BITCLEAR((unsigned *)states_arg, algorithm); + } + } else { + /* create a new arg */ + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = (char *)GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET; + /* all enabled by default */ + tmp.value.integer = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; + if (state != 0) { + GPR_BITSET((unsigned *)&tmp.value.integer, algorithm); + } else if (algorithm != GRPC_COMPRESS_NONE) { + GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm); + } + result = grpc_channel_args_copy_and_add(*a, &tmp, 1); + grpc_channel_args_destroy(exec_ctx, *a); + *a = result; + } + return result; +} + +grpc_channel_args *grpc_channel_args_stream_compression_algorithm_set_state( + grpc_exec_ctx *exec_ctx, grpc_channel_args **a, + grpc_stream_compression_algorithm algorithm, int state) { + int *states_arg = NULL; + grpc_channel_args *result = *a; + const int states_arg_found = + find_stream_compression_algorithm_states_bitset(*a, &states_arg); + + if (grpc_channel_args_get_stream_compression_algorithm(*a) == algorithm && + state == 0) { + const char *algo_name = NULL; + GPR_ASSERT(grpc_stream_compression_algorithm_name(algorithm, &algo_name) != + 0); + gpr_log(GPR_ERROR, + "Tried to disable default stream compression algorithm '%s'. The " + "operation has been ignored.", + algo_name); + } else if (states_arg_found) { + if (state != 0) { + GPR_BITSET((unsigned *)states_arg, algorithm); + } else if (algorithm != GRPC_STREAM_COMPRESS_NONE) { + GPR_BITCLEAR((unsigned *)states_arg, algorithm); + } + } else { + /* create a new arg */ + grpc_arg tmp; + tmp.type = GRPC_ARG_INTEGER; + tmp.key = (char *)GRPC_STREAM_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET; + /* all enabled by default */ + tmp.value.integer = (1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 1; + if (state != 0) { + GPR_BITSET((unsigned *)&tmp.value.integer, algorithm); + } else if (algorithm != GRPC_STREAM_COMPRESS_NONE) { + GPR_BITCLEAR((unsigned *)&tmp.value.integer, algorithm); + } + result = grpc_channel_args_copy_and_add(*a, &tmp, 1); + grpc_channel_args_destroy(exec_ctx, *a); + *a = result; + } + return result; +} + +uint32_t grpc_channel_args_compression_algorithm_get_states( + const grpc_channel_args *a) { + int *states_arg; + if (find_compression_algorithm_states_bitset(a, &states_arg)) { + return (uint32_t)*states_arg; + } else { + return (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; /* All algs. enabled */ + } +} + +uint32_t grpc_channel_args_stream_compression_algorithm_get_states( + const grpc_channel_args *a) { + int *states_arg; + if (find_stream_compression_algorithm_states_bitset(a, &states_arg)) { + return (uint32_t)*states_arg; + } else { + return (1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - + 1; /* All algs. enabled */ + } +} + +grpc_channel_args *grpc_channel_args_set_socket_mutator( + grpc_channel_args *a, grpc_socket_mutator *mutator) { + grpc_arg tmp = grpc_socket_mutator_to_arg(mutator); + return grpc_channel_args_copy_and_add(a, &tmp, 1); +} + +int grpc_channel_args_compare(const grpc_channel_args *a, + const grpc_channel_args *b) { + int c = GPR_ICMP(a->num_args, b->num_args); + if (c != 0) return c; + for (size_t i = 0; i < a->num_args; i++) { + c = cmp_arg(&a->args[i], &b->args[i]); + if (c != 0) return c; + } + return 0; +} + +const grpc_arg *grpc_channel_args_find(const grpc_channel_args *args, + const char *name) { + if (args != NULL) { + for (size_t i = 0; i < args->num_args; ++i) { + if (strcmp(args->args[i].key, name) == 0) { + return &args->args[i]; + } + } + } + return NULL; +} + +int grpc_channel_arg_get_integer(const grpc_arg *arg, + const grpc_integer_options options) { + if (arg == NULL) return options.default_value; + if (arg->type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key); + return options.default_value; + } + if (arg->value.integer < options.min_value) { + gpr_log(GPR_ERROR, "%s ignored: it must be >= %d", arg->key, + options.min_value); + return options.default_value; + } + if (arg->value.integer > options.max_value) { + gpr_log(GPR_ERROR, "%s ignored: it must be <= %d", arg->key, + options.max_value); + return options.default_value; + } + return arg->value.integer; +} + +bool grpc_channel_arg_get_bool(const grpc_arg *arg, bool default_value) { + if (arg == NULL) return default_value; + if (arg->type != GRPC_ARG_INTEGER) { + gpr_log(GPR_ERROR, "%s ignored: it must be an integer", arg->key); + return default_value; + } + switch (arg->value.integer) { + case 0: + return false; + case 1: + return true; + default: + gpr_log(GPR_ERROR, "%s treated as bool but set to %d (assuming true)", + arg->key, arg->value.integer); + return true; + } +} + +bool grpc_channel_args_want_minimal_stack(const grpc_channel_args *args) { + return grpc_channel_arg_get_bool( + grpc_channel_args_find(args, GRPC_ARG_MINIMAL_STACK), false); +} + +grpc_arg grpc_channel_arg_string_create(char *name, char *value) { + grpc_arg arg; + arg.type = GRPC_ARG_STRING; + arg.key = name; + arg.value.string = value; + return arg; +} + +grpc_arg grpc_channel_arg_integer_create(char *name, int value) { + grpc_arg arg; + arg.type = GRPC_ARG_INTEGER; + arg.key = name; + arg.value.integer = value; + return arg; +} + +grpc_arg grpc_channel_arg_pointer_create( + char *name, void *value, const grpc_arg_pointer_vtable *vtable) { + grpc_arg arg; + arg.type = GRPC_ARG_POINTER; + arg.key = name; + arg.value.pointer.p = value; + arg.value.pointer.vtable = vtable; + return arg; +} diff --git a/src/core/lib/channel/channel_stack.c b/src/core/lib/channel/channel_stack.c deleted file mode 100644 index 775c8bc667..0000000000 --- a/src/core/lib/channel/channel_stack.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/channel/channel_stack.h" -#include -#include - -#include -#include - -grpc_tracer_flag grpc_trace_channel = GRPC_TRACER_INITIALIZER(false, "channel"); - -/* Memory layouts. - - Channel stack is laid out as: { - grpc_channel_stack stk; - padding to GPR_MAX_ALIGNMENT - grpc_channel_element[stk.count]; - per-filter memory, aligned to GPR_MAX_ALIGNMENT - } - - Call stack is laid out as: { - grpc_call_stack stk; - padding to GPR_MAX_ALIGNMENT - grpc_call_element[stk.count]; - per-filter memory, aligned to GPR_MAX_ALIGNMENT - } */ - -/* Given a size, round up to the next multiple of sizeof(void*) */ -#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ - (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) - -size_t grpc_channel_stack_size(const grpc_channel_filter **filters, - size_t filter_count) { - /* always need the header, and size for the channel elements */ - size_t size = - ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) + - ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element)); - size_t i; - - GPR_ASSERT((GPR_MAX_ALIGNMENT & (GPR_MAX_ALIGNMENT - 1)) == 0 && - "GPR_MAX_ALIGNMENT must be a power of two"); - - /* add the size for each filter */ - for (i = 0; i < filter_count; i++) { - size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); - } - - return size; -} - -#define CHANNEL_ELEMS_FROM_STACK(stk) \ - ((grpc_channel_element *)((char *)(stk) + ROUND_UP_TO_ALIGNMENT_SIZE( \ - sizeof(grpc_channel_stack)))) - -#define CALL_ELEMS_FROM_STACK(stk) \ - ((grpc_call_element *)((char *)(stk) + \ - ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)))) - -grpc_channel_element *grpc_channel_stack_element( - grpc_channel_stack *channel_stack, size_t index) { - return CHANNEL_ELEMS_FROM_STACK(channel_stack) + index; -} - -grpc_channel_element *grpc_channel_stack_last_element( - grpc_channel_stack *channel_stack) { - return grpc_channel_stack_element(channel_stack, channel_stack->count - 1); -} - -grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack, - size_t index) { - return CALL_ELEMS_FROM_STACK(call_stack) + index; -} - -grpc_error *grpc_channel_stack_init( - grpc_exec_ctx *exec_ctx, int initial_refs, grpc_iomgr_cb_func destroy, - void *destroy_arg, const grpc_channel_filter **filters, size_t filter_count, - const grpc_channel_args *channel_args, grpc_transport *optional_transport, - const char *name, grpc_channel_stack *stack) { - size_t call_size = - ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) + - ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element)); - grpc_channel_element *elems; - grpc_channel_element_args args; - char *user_data; - size_t i; - - stack->count = filter_count; - GRPC_STREAM_REF_INIT(&stack->refcount, initial_refs, destroy, destroy_arg, - name); - elems = CHANNEL_ELEMS_FROM_STACK(stack); - user_data = - ((char *)elems) + - ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element)); - - /* init per-filter data */ - grpc_error *first_error = GRPC_ERROR_NONE; - for (i = 0; i < filter_count; i++) { - args.channel_stack = stack; - args.channel_args = channel_args; - args.optional_transport = optional_transport; - args.is_first = i == 0; - args.is_last = i == (filter_count - 1); - elems[i].filter = filters[i]; - elems[i].channel_data = user_data; - grpc_error *error = - elems[i].filter->init_channel_elem(exec_ctx, &elems[i], &args); - if (error != GRPC_ERROR_NONE) { - if (first_error == GRPC_ERROR_NONE) { - first_error = error; - } else { - GRPC_ERROR_UNREF(error); - } - } - user_data += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); - call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data); - } - - GPR_ASSERT(user_data > (char *)stack); - GPR_ASSERT((uintptr_t)(user_data - (char *)stack) == - grpc_channel_stack_size(filters, filter_count)); - - stack->call_stack_size = call_size; - return first_error; -} - -void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, - grpc_channel_stack *stack) { - grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(stack); - size_t count = stack->count; - size_t i; - - /* destroy per-filter data */ - for (i = 0; i < count; i++) { - channel_elems[i].filter->destroy_channel_elem(exec_ctx, &channel_elems[i]); - } -} - -grpc_error *grpc_call_stack_init(grpc_exec_ctx *exec_ctx, - grpc_channel_stack *channel_stack, - int initial_refs, grpc_iomgr_cb_func destroy, - void *destroy_arg, - const grpc_call_element_args *elem_args) { - grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack); - size_t count = channel_stack->count; - grpc_call_element *call_elems; - char *user_data; - size_t i; - - elem_args->call_stack->count = count; - GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy, - destroy_arg, "CALL_STACK"); - call_elems = CALL_ELEMS_FROM_STACK(elem_args->call_stack); - user_data = ((char *)call_elems) + - ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); - - /* init per-filter data */ - grpc_error *first_error = GRPC_ERROR_NONE; - for (i = 0; i < count; i++) { - call_elems[i].filter = channel_elems[i].filter; - call_elems[i].channel_data = channel_elems[i].channel_data; - call_elems[i].call_data = user_data; - grpc_error *error = call_elems[i].filter->init_call_elem( - exec_ctx, &call_elems[i], elem_args); - if (error != GRPC_ERROR_NONE) { - if (first_error == GRPC_ERROR_NONE) { - first_error = error; - } else { - GRPC_ERROR_UNREF(error); - } - } - user_data += - ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); - } - return first_error; -} - -void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_call_stack *call_stack, - grpc_polling_entity *pollent) { - size_t count = call_stack->count; - grpc_call_element *call_elems; - char *user_data; - size_t i; - - call_elems = CALL_ELEMS_FROM_STACK(call_stack); - user_data = ((char *)call_elems) + - ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); - - /* init per-filter data */ - for (i = 0; i < count; i++) { - call_elems[i].filter->set_pollset_or_pollset_set(exec_ctx, &call_elems[i], - pollent); - user_data += - ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); - } -} - -void grpc_call_stack_ignore_set_pollset_or_pollset_set( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_polling_entity *pollent) {} - -void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack, - const grpc_call_final_info *final_info, - grpc_closure *then_schedule_closure) { - grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack); - size_t count = stack->count; - size_t i; - - /* destroy per-filter data */ - for (i = 0; i < count; i++) { - elems[i].filter->destroy_call_elem( - exec_ctx, &elems[i], final_info, - i == count - 1 ? then_schedule_closure : NULL); - } -} - -void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - grpc_call_element *next_elem = elem + 1; - GRPC_CALL_LOG_OP(GPR_INFO, next_elem, op); - next_elem->filter->start_transport_stream_op_batch(exec_ctx, next_elem, op); -} - -void grpc_channel_next_get_info(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - const grpc_channel_info *channel_info) { - grpc_channel_element *next_elem = elem + 1; - next_elem->filter->get_channel_info(exec_ctx, next_elem, channel_info); -} - -void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, - grpc_transport_op *op) { - grpc_channel_element *next_elem = elem + 1; - next_elem->filter->start_transport_op(exec_ctx, next_elem, op); -} - -grpc_channel_stack *grpc_channel_stack_from_top_element( - grpc_channel_element *elem) { - return (grpc_channel_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( - sizeof(grpc_channel_stack))); -} - -grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) { - return (grpc_call_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( - sizeof(grpc_call_stack))); -} diff --git a/src/core/lib/channel/channel_stack.cc b/src/core/lib/channel/channel_stack.cc new file mode 100644 index 0000000000..775c8bc667 --- /dev/null +++ b/src/core/lib/channel/channel_stack.cc @@ -0,0 +1,262 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/channel/channel_stack.h" +#include +#include + +#include +#include + +grpc_tracer_flag grpc_trace_channel = GRPC_TRACER_INITIALIZER(false, "channel"); + +/* Memory layouts. + + Channel stack is laid out as: { + grpc_channel_stack stk; + padding to GPR_MAX_ALIGNMENT + grpc_channel_element[stk.count]; + per-filter memory, aligned to GPR_MAX_ALIGNMENT + } + + Call stack is laid out as: { + grpc_call_stack stk; + padding to GPR_MAX_ALIGNMENT + grpc_call_element[stk.count]; + per-filter memory, aligned to GPR_MAX_ALIGNMENT + } */ + +/* Given a size, round up to the next multiple of sizeof(void*) */ +#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ + (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) + +size_t grpc_channel_stack_size(const grpc_channel_filter **filters, + size_t filter_count) { + /* always need the header, and size for the channel elements */ + size_t size = + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_channel_stack)) + + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element)); + size_t i; + + GPR_ASSERT((GPR_MAX_ALIGNMENT & (GPR_MAX_ALIGNMENT - 1)) == 0 && + "GPR_MAX_ALIGNMENT must be a power of two"); + + /* add the size for each filter */ + for (i = 0; i < filter_count; i++) { + size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); + } + + return size; +} + +#define CHANNEL_ELEMS_FROM_STACK(stk) \ + ((grpc_channel_element *)((char *)(stk) + ROUND_UP_TO_ALIGNMENT_SIZE( \ + sizeof(grpc_channel_stack)))) + +#define CALL_ELEMS_FROM_STACK(stk) \ + ((grpc_call_element *)((char *)(stk) + \ + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)))) + +grpc_channel_element *grpc_channel_stack_element( + grpc_channel_stack *channel_stack, size_t index) { + return CHANNEL_ELEMS_FROM_STACK(channel_stack) + index; +} + +grpc_channel_element *grpc_channel_stack_last_element( + grpc_channel_stack *channel_stack) { + return grpc_channel_stack_element(channel_stack, channel_stack->count - 1); +} + +grpc_call_element *grpc_call_stack_element(grpc_call_stack *call_stack, + size_t index) { + return CALL_ELEMS_FROM_STACK(call_stack) + index; +} + +grpc_error *grpc_channel_stack_init( + grpc_exec_ctx *exec_ctx, int initial_refs, grpc_iomgr_cb_func destroy, + void *destroy_arg, const grpc_channel_filter **filters, size_t filter_count, + const grpc_channel_args *channel_args, grpc_transport *optional_transport, + const char *name, grpc_channel_stack *stack) { + size_t call_size = + ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(grpc_call_stack)) + + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_call_element)); + grpc_channel_element *elems; + grpc_channel_element_args args; + char *user_data; + size_t i; + + stack->count = filter_count; + GRPC_STREAM_REF_INIT(&stack->refcount, initial_refs, destroy, destroy_arg, + name); + elems = CHANNEL_ELEMS_FROM_STACK(stack); + user_data = + ((char *)elems) + + ROUND_UP_TO_ALIGNMENT_SIZE(filter_count * sizeof(grpc_channel_element)); + + /* init per-filter data */ + grpc_error *first_error = GRPC_ERROR_NONE; + for (i = 0; i < filter_count; i++) { + args.channel_stack = stack; + args.channel_args = channel_args; + args.optional_transport = optional_transport; + args.is_first = i == 0; + args.is_last = i == (filter_count - 1); + elems[i].filter = filters[i]; + elems[i].channel_data = user_data; + grpc_error *error = + elems[i].filter->init_channel_elem(exec_ctx, &elems[i], &args); + if (error != GRPC_ERROR_NONE) { + if (first_error == GRPC_ERROR_NONE) { + first_error = error; + } else { + GRPC_ERROR_UNREF(error); + } + } + user_data += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_channel_data); + call_size += ROUND_UP_TO_ALIGNMENT_SIZE(filters[i]->sizeof_call_data); + } + + GPR_ASSERT(user_data > (char *)stack); + GPR_ASSERT((uintptr_t)(user_data - (char *)stack) == + grpc_channel_stack_size(filters, filter_count)); + + stack->call_stack_size = call_size; + return first_error; +} + +void grpc_channel_stack_destroy(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *stack) { + grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(stack); + size_t count = stack->count; + size_t i; + + /* destroy per-filter data */ + for (i = 0; i < count; i++) { + channel_elems[i].filter->destroy_channel_elem(exec_ctx, &channel_elems[i]); + } +} + +grpc_error *grpc_call_stack_init(grpc_exec_ctx *exec_ctx, + grpc_channel_stack *channel_stack, + int initial_refs, grpc_iomgr_cb_func destroy, + void *destroy_arg, + const grpc_call_element_args *elem_args) { + grpc_channel_element *channel_elems = CHANNEL_ELEMS_FROM_STACK(channel_stack); + size_t count = channel_stack->count; + grpc_call_element *call_elems; + char *user_data; + size_t i; + + elem_args->call_stack->count = count; + GRPC_STREAM_REF_INIT(&elem_args->call_stack->refcount, initial_refs, destroy, + destroy_arg, "CALL_STACK"); + call_elems = CALL_ELEMS_FROM_STACK(elem_args->call_stack); + user_data = ((char *)call_elems) + + ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); + + /* init per-filter data */ + grpc_error *first_error = GRPC_ERROR_NONE; + for (i = 0; i < count; i++) { + call_elems[i].filter = channel_elems[i].filter; + call_elems[i].channel_data = channel_elems[i].channel_data; + call_elems[i].call_data = user_data; + grpc_error *error = call_elems[i].filter->init_call_elem( + exec_ctx, &call_elems[i], elem_args); + if (error != GRPC_ERROR_NONE) { + if (first_error == GRPC_ERROR_NONE) { + first_error = error; + } else { + GRPC_ERROR_UNREF(error); + } + } + user_data += + ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); + } + return first_error; +} + +void grpc_call_stack_set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_call_stack *call_stack, + grpc_polling_entity *pollent) { + size_t count = call_stack->count; + grpc_call_element *call_elems; + char *user_data; + size_t i; + + call_elems = CALL_ELEMS_FROM_STACK(call_stack); + user_data = ((char *)call_elems) + + ROUND_UP_TO_ALIGNMENT_SIZE(count * sizeof(grpc_call_element)); + + /* init per-filter data */ + for (i = 0; i < count; i++) { + call_elems[i].filter->set_pollset_or_pollset_set(exec_ctx, &call_elems[i], + pollent); + user_data += + ROUND_UP_TO_ALIGNMENT_SIZE(call_elems[i].filter->sizeof_call_data); + } +} + +void grpc_call_stack_ignore_set_pollset_or_pollset_set( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_polling_entity *pollent) {} + +void grpc_call_stack_destroy(grpc_exec_ctx *exec_ctx, grpc_call_stack *stack, + const grpc_call_final_info *final_info, + grpc_closure *then_schedule_closure) { + grpc_call_element *elems = CALL_ELEMS_FROM_STACK(stack); + size_t count = stack->count; + size_t i; + + /* destroy per-filter data */ + for (i = 0; i < count; i++) { + elems[i].filter->destroy_call_elem( + exec_ctx, &elems[i], final_info, + i == count - 1 ? then_schedule_closure : NULL); + } +} + +void grpc_call_next_op(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + grpc_call_element *next_elem = elem + 1; + GRPC_CALL_LOG_OP(GPR_INFO, next_elem, op); + next_elem->filter->start_transport_stream_op_batch(exec_ctx, next_elem, op); +} + +void grpc_channel_next_get_info(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + const grpc_channel_info *channel_info) { + grpc_channel_element *next_elem = elem + 1; + next_elem->filter->get_channel_info(exec_ctx, next_elem, channel_info); +} + +void grpc_channel_next_op(grpc_exec_ctx *exec_ctx, grpc_channel_element *elem, + grpc_transport_op *op) { + grpc_channel_element *next_elem = elem + 1; + next_elem->filter->start_transport_op(exec_ctx, next_elem, op); +} + +grpc_channel_stack *grpc_channel_stack_from_top_element( + grpc_channel_element *elem) { + return (grpc_channel_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( + sizeof(grpc_channel_stack))); +} + +grpc_call_stack *grpc_call_stack_from_top_element(grpc_call_element *elem) { + return (grpc_call_stack *)((char *)(elem)-ROUND_UP_TO_ALIGNMENT_SIZE( + sizeof(grpc_call_stack))); +} diff --git a/src/core/lib/channel/channel_stack_builder.c b/src/core/lib/channel/channel_stack_builder.c deleted file mode 100644 index b663ebfb52..0000000000 --- a/src/core/lib/channel/channel_stack_builder.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/channel/channel_stack_builder.h" - -#include - -#include -#include - -grpc_tracer_flag grpc_trace_channel_stack_builder = - GRPC_TRACER_INITIALIZER(false, "channel_stack_builder"); - -typedef struct filter_node { - struct filter_node *next; - struct filter_node *prev; - const grpc_channel_filter *filter; - grpc_post_filter_create_init_func init; - void *init_arg; -} filter_node; - -struct grpc_channel_stack_builder { - // sentinel nodes for filters that have been added - filter_node begin; - filter_node end; - // various set/get-able parameters - grpc_channel_args *args; - grpc_transport *transport; - char *target; - const char *name; -}; - -struct grpc_channel_stack_builder_iterator { - grpc_channel_stack_builder *builder; - filter_node *node; -}; - -grpc_channel_stack_builder *grpc_channel_stack_builder_create(void) { - grpc_channel_stack_builder *b = - (grpc_channel_stack_builder *)gpr_zalloc(sizeof(*b)); - - b->begin.filter = NULL; - b->end.filter = NULL; - b->begin.next = &b->end; - b->begin.prev = &b->end; - b->end.next = &b->begin; - b->end.prev = &b->begin; - - return b; -} - -void grpc_channel_stack_builder_set_target(grpc_channel_stack_builder *b, - const char *target) { - gpr_free(b->target); - b->target = gpr_strdup(target); -} - -const char *grpc_channel_stack_builder_get_target( - grpc_channel_stack_builder *b) { - return b->target; -} - -static grpc_channel_stack_builder_iterator *create_iterator_at_filter_node( - grpc_channel_stack_builder *builder, filter_node *node) { - grpc_channel_stack_builder_iterator *it = - (grpc_channel_stack_builder_iterator *)gpr_malloc(sizeof(*it)); - it->builder = builder; - it->node = node; - return it; -} - -void grpc_channel_stack_builder_iterator_destroy( - grpc_channel_stack_builder_iterator *it) { - gpr_free(it); -} - -grpc_channel_stack_builder_iterator * -grpc_channel_stack_builder_create_iterator_at_first( - grpc_channel_stack_builder *builder) { - return create_iterator_at_filter_node(builder, &builder->begin); -} - -grpc_channel_stack_builder_iterator * -grpc_channel_stack_builder_create_iterator_at_last( - grpc_channel_stack_builder *builder) { - return create_iterator_at_filter_node(builder, &builder->end); -} - -bool grpc_channel_stack_builder_iterator_is_end( - grpc_channel_stack_builder_iterator *iterator) { - return iterator->node == &iterator->builder->end; -} - -const char *grpc_channel_stack_builder_iterator_filter_name( - grpc_channel_stack_builder_iterator *iterator) { - if (iterator->node->filter == NULL) return NULL; - return iterator->node->filter->name; -} - -bool grpc_channel_stack_builder_move_next( - grpc_channel_stack_builder_iterator *iterator) { - if (iterator->node == &iterator->builder->end) return false; - iterator->node = iterator->node->next; - return true; -} - -bool grpc_channel_stack_builder_move_prev( - grpc_channel_stack_builder_iterator *iterator) { - if (iterator->node == &iterator->builder->begin) return false; - iterator->node = iterator->node->prev; - return true; -} - -grpc_channel_stack_builder_iterator *grpc_channel_stack_builder_iterator_find( - grpc_channel_stack_builder *builder, const char *filter_name) { - GPR_ASSERT(filter_name != NULL); - grpc_channel_stack_builder_iterator *it = - grpc_channel_stack_builder_create_iterator_at_first(builder); - while (grpc_channel_stack_builder_move_next(it)) { - if (grpc_channel_stack_builder_iterator_is_end(it)) break; - const char *filter_name_at_it = - grpc_channel_stack_builder_iterator_filter_name(it); - if (strcmp(filter_name, filter_name_at_it) == 0) break; - } - return it; -} - -bool grpc_channel_stack_builder_move_prev( - grpc_channel_stack_builder_iterator *iterator); - -void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder *builder, - const char *name) { - GPR_ASSERT(builder->name == NULL); - builder->name = name; -} - -void grpc_channel_stack_builder_set_channel_arguments( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, - const grpc_channel_args *args) { - if (builder->args != NULL) { - grpc_channel_args_destroy(exec_ctx, builder->args); - } - builder->args = grpc_channel_args_copy(args); -} - -void grpc_channel_stack_builder_set_transport( - grpc_channel_stack_builder *builder, grpc_transport *transport) { - GPR_ASSERT(builder->transport == NULL); - builder->transport = transport; -} - -grpc_transport *grpc_channel_stack_builder_get_transport( - grpc_channel_stack_builder *builder) { - return builder->transport; -} - -const grpc_channel_args *grpc_channel_stack_builder_get_channel_arguments( - grpc_channel_stack_builder *builder) { - return builder->args; -} - -bool grpc_channel_stack_builder_append_filter( - grpc_channel_stack_builder *builder, const grpc_channel_filter *filter, - grpc_post_filter_create_init_func post_init_func, void *user_data) { - grpc_channel_stack_builder_iterator *it = - grpc_channel_stack_builder_create_iterator_at_last(builder); - bool ok = grpc_channel_stack_builder_add_filter_before( - it, filter, post_init_func, user_data); - grpc_channel_stack_builder_iterator_destroy(it); - return ok; -} - -bool grpc_channel_stack_builder_remove_filter( - grpc_channel_stack_builder *builder, const char *filter_name) { - grpc_channel_stack_builder_iterator *it = - grpc_channel_stack_builder_iterator_find(builder, filter_name); - if (grpc_channel_stack_builder_iterator_is_end(it)) { - grpc_channel_stack_builder_iterator_destroy(it); - return false; - } - it->node->prev->next = it->node->next; - it->node->next->prev = it->node->prev; - gpr_free(it->node); - grpc_channel_stack_builder_iterator_destroy(it); - return true; -} - -bool grpc_channel_stack_builder_prepend_filter( - grpc_channel_stack_builder *builder, const grpc_channel_filter *filter, - grpc_post_filter_create_init_func post_init_func, void *user_data) { - grpc_channel_stack_builder_iterator *it = - grpc_channel_stack_builder_create_iterator_at_first(builder); - bool ok = grpc_channel_stack_builder_add_filter_after( - it, filter, post_init_func, user_data); - grpc_channel_stack_builder_iterator_destroy(it); - return ok; -} - -static void add_after(filter_node *before, const grpc_channel_filter *filter, - grpc_post_filter_create_init_func post_init_func, - void *user_data) { - filter_node *new_node = (filter_node *)gpr_malloc(sizeof(*new_node)); - new_node->next = before->next; - new_node->prev = before; - new_node->next->prev = new_node->prev->next = new_node; - new_node->filter = filter; - new_node->init = post_init_func; - new_node->init_arg = user_data; -} - -bool grpc_channel_stack_builder_add_filter_before( - grpc_channel_stack_builder_iterator *iterator, - const grpc_channel_filter *filter, - grpc_post_filter_create_init_func post_init_func, void *user_data) { - if (iterator->node == &iterator->builder->begin) return false; - add_after(iterator->node->prev, filter, post_init_func, user_data); - return true; -} - -bool grpc_channel_stack_builder_add_filter_after( - grpc_channel_stack_builder_iterator *iterator, - const grpc_channel_filter *filter, - grpc_post_filter_create_init_func post_init_func, void *user_data) { - if (iterator->node == &iterator->builder->end) return false; - add_after(iterator->node, filter, post_init_func, user_data); - return true; -} - -void grpc_channel_stack_builder_destroy(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder) { - filter_node *p = builder->begin.next; - while (p != &builder->end) { - filter_node *next = p->next; - gpr_free(p); - p = next; - } - if (builder->args != NULL) { - grpc_channel_args_destroy(exec_ctx, builder->args); - } - gpr_free(builder->target); - gpr_free(builder); -} - -grpc_error *grpc_channel_stack_builder_finish( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, - size_t prefix_bytes, int initial_refs, grpc_iomgr_cb_func destroy, - void *destroy_arg, void **result) { - // count the number of filters - size_t num_filters = 0; - for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) { - num_filters++; - } - - // create an array of filters - const grpc_channel_filter **filters = - (const grpc_channel_filter **)gpr_malloc(sizeof(*filters) * num_filters); - size_t i = 0; - for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) { - filters[i++] = p->filter; - } - - // calculate the size of the channel stack - size_t channel_stack_size = grpc_channel_stack_size(filters, num_filters); - - // allocate memory, with prefix_bytes followed by channel_stack_size - *result = gpr_zalloc(prefix_bytes + channel_stack_size); - // fetch a pointer to the channel stack - grpc_channel_stack *channel_stack = - (grpc_channel_stack *)((char *)(*result) + prefix_bytes); - // and initialize it - grpc_error *error = grpc_channel_stack_init( - exec_ctx, initial_refs, destroy, - destroy_arg == NULL ? *result : destroy_arg, filters, num_filters, - builder->args, builder->transport, builder->name, channel_stack); - - if (error != GRPC_ERROR_NONE) { - grpc_channel_stack_destroy(exec_ctx, channel_stack); - gpr_free(*result); - *result = NULL; - } else { - // run post-initialization functions - i = 0; - for (filter_node *p = builder->begin.next; p != &builder->end; - p = p->next) { - if (p->init != NULL) { - p->init(channel_stack, grpc_channel_stack_element(channel_stack, i), - p->init_arg); - } - i++; - } - } - - grpc_channel_stack_builder_destroy(exec_ctx, builder); - gpr_free((grpc_channel_filter **)filters); - - return error; -} diff --git a/src/core/lib/channel/channel_stack_builder.cc b/src/core/lib/channel/channel_stack_builder.cc new file mode 100644 index 0000000000..b663ebfb52 --- /dev/null +++ b/src/core/lib/channel/channel_stack_builder.cc @@ -0,0 +1,312 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/channel/channel_stack_builder.h" + +#include + +#include +#include + +grpc_tracer_flag grpc_trace_channel_stack_builder = + GRPC_TRACER_INITIALIZER(false, "channel_stack_builder"); + +typedef struct filter_node { + struct filter_node *next; + struct filter_node *prev; + const grpc_channel_filter *filter; + grpc_post_filter_create_init_func init; + void *init_arg; +} filter_node; + +struct grpc_channel_stack_builder { + // sentinel nodes for filters that have been added + filter_node begin; + filter_node end; + // various set/get-able parameters + grpc_channel_args *args; + grpc_transport *transport; + char *target; + const char *name; +}; + +struct grpc_channel_stack_builder_iterator { + grpc_channel_stack_builder *builder; + filter_node *node; +}; + +grpc_channel_stack_builder *grpc_channel_stack_builder_create(void) { + grpc_channel_stack_builder *b = + (grpc_channel_stack_builder *)gpr_zalloc(sizeof(*b)); + + b->begin.filter = NULL; + b->end.filter = NULL; + b->begin.next = &b->end; + b->begin.prev = &b->end; + b->end.next = &b->begin; + b->end.prev = &b->begin; + + return b; +} + +void grpc_channel_stack_builder_set_target(grpc_channel_stack_builder *b, + const char *target) { + gpr_free(b->target); + b->target = gpr_strdup(target); +} + +const char *grpc_channel_stack_builder_get_target( + grpc_channel_stack_builder *b) { + return b->target; +} + +static grpc_channel_stack_builder_iterator *create_iterator_at_filter_node( + grpc_channel_stack_builder *builder, filter_node *node) { + grpc_channel_stack_builder_iterator *it = + (grpc_channel_stack_builder_iterator *)gpr_malloc(sizeof(*it)); + it->builder = builder; + it->node = node; + return it; +} + +void grpc_channel_stack_builder_iterator_destroy( + grpc_channel_stack_builder_iterator *it) { + gpr_free(it); +} + +grpc_channel_stack_builder_iterator * +grpc_channel_stack_builder_create_iterator_at_first( + grpc_channel_stack_builder *builder) { + return create_iterator_at_filter_node(builder, &builder->begin); +} + +grpc_channel_stack_builder_iterator * +grpc_channel_stack_builder_create_iterator_at_last( + grpc_channel_stack_builder *builder) { + return create_iterator_at_filter_node(builder, &builder->end); +} + +bool grpc_channel_stack_builder_iterator_is_end( + grpc_channel_stack_builder_iterator *iterator) { + return iterator->node == &iterator->builder->end; +} + +const char *grpc_channel_stack_builder_iterator_filter_name( + grpc_channel_stack_builder_iterator *iterator) { + if (iterator->node->filter == NULL) return NULL; + return iterator->node->filter->name; +} + +bool grpc_channel_stack_builder_move_next( + grpc_channel_stack_builder_iterator *iterator) { + if (iterator->node == &iterator->builder->end) return false; + iterator->node = iterator->node->next; + return true; +} + +bool grpc_channel_stack_builder_move_prev( + grpc_channel_stack_builder_iterator *iterator) { + if (iterator->node == &iterator->builder->begin) return false; + iterator->node = iterator->node->prev; + return true; +} + +grpc_channel_stack_builder_iterator *grpc_channel_stack_builder_iterator_find( + grpc_channel_stack_builder *builder, const char *filter_name) { + GPR_ASSERT(filter_name != NULL); + grpc_channel_stack_builder_iterator *it = + grpc_channel_stack_builder_create_iterator_at_first(builder); + while (grpc_channel_stack_builder_move_next(it)) { + if (grpc_channel_stack_builder_iterator_is_end(it)) break; + const char *filter_name_at_it = + grpc_channel_stack_builder_iterator_filter_name(it); + if (strcmp(filter_name, filter_name_at_it) == 0) break; + } + return it; +} + +bool grpc_channel_stack_builder_move_prev( + grpc_channel_stack_builder_iterator *iterator); + +void grpc_channel_stack_builder_set_name(grpc_channel_stack_builder *builder, + const char *name) { + GPR_ASSERT(builder->name == NULL); + builder->name = name; +} + +void grpc_channel_stack_builder_set_channel_arguments( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, + const grpc_channel_args *args) { + if (builder->args != NULL) { + grpc_channel_args_destroy(exec_ctx, builder->args); + } + builder->args = grpc_channel_args_copy(args); +} + +void grpc_channel_stack_builder_set_transport( + grpc_channel_stack_builder *builder, grpc_transport *transport) { + GPR_ASSERT(builder->transport == NULL); + builder->transport = transport; +} + +grpc_transport *grpc_channel_stack_builder_get_transport( + grpc_channel_stack_builder *builder) { + return builder->transport; +} + +const grpc_channel_args *grpc_channel_stack_builder_get_channel_arguments( + grpc_channel_stack_builder *builder) { + return builder->args; +} + +bool grpc_channel_stack_builder_append_filter( + grpc_channel_stack_builder *builder, const grpc_channel_filter *filter, + grpc_post_filter_create_init_func post_init_func, void *user_data) { + grpc_channel_stack_builder_iterator *it = + grpc_channel_stack_builder_create_iterator_at_last(builder); + bool ok = grpc_channel_stack_builder_add_filter_before( + it, filter, post_init_func, user_data); + grpc_channel_stack_builder_iterator_destroy(it); + return ok; +} + +bool grpc_channel_stack_builder_remove_filter( + grpc_channel_stack_builder *builder, const char *filter_name) { + grpc_channel_stack_builder_iterator *it = + grpc_channel_stack_builder_iterator_find(builder, filter_name); + if (grpc_channel_stack_builder_iterator_is_end(it)) { + grpc_channel_stack_builder_iterator_destroy(it); + return false; + } + it->node->prev->next = it->node->next; + it->node->next->prev = it->node->prev; + gpr_free(it->node); + grpc_channel_stack_builder_iterator_destroy(it); + return true; +} + +bool grpc_channel_stack_builder_prepend_filter( + grpc_channel_stack_builder *builder, const grpc_channel_filter *filter, + grpc_post_filter_create_init_func post_init_func, void *user_data) { + grpc_channel_stack_builder_iterator *it = + grpc_channel_stack_builder_create_iterator_at_first(builder); + bool ok = grpc_channel_stack_builder_add_filter_after( + it, filter, post_init_func, user_data); + grpc_channel_stack_builder_iterator_destroy(it); + return ok; +} + +static void add_after(filter_node *before, const grpc_channel_filter *filter, + grpc_post_filter_create_init_func post_init_func, + void *user_data) { + filter_node *new_node = (filter_node *)gpr_malloc(sizeof(*new_node)); + new_node->next = before->next; + new_node->prev = before; + new_node->next->prev = new_node->prev->next = new_node; + new_node->filter = filter; + new_node->init = post_init_func; + new_node->init_arg = user_data; +} + +bool grpc_channel_stack_builder_add_filter_before( + grpc_channel_stack_builder_iterator *iterator, + const grpc_channel_filter *filter, + grpc_post_filter_create_init_func post_init_func, void *user_data) { + if (iterator->node == &iterator->builder->begin) return false; + add_after(iterator->node->prev, filter, post_init_func, user_data); + return true; +} + +bool grpc_channel_stack_builder_add_filter_after( + grpc_channel_stack_builder_iterator *iterator, + const grpc_channel_filter *filter, + grpc_post_filter_create_init_func post_init_func, void *user_data) { + if (iterator->node == &iterator->builder->end) return false; + add_after(iterator->node, filter, post_init_func, user_data); + return true; +} + +void grpc_channel_stack_builder_destroy(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder) { + filter_node *p = builder->begin.next; + while (p != &builder->end) { + filter_node *next = p->next; + gpr_free(p); + p = next; + } + if (builder->args != NULL) { + grpc_channel_args_destroy(exec_ctx, builder->args); + } + gpr_free(builder->target); + gpr_free(builder); +} + +grpc_error *grpc_channel_stack_builder_finish( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, + size_t prefix_bytes, int initial_refs, grpc_iomgr_cb_func destroy, + void *destroy_arg, void **result) { + // count the number of filters + size_t num_filters = 0; + for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) { + num_filters++; + } + + // create an array of filters + const grpc_channel_filter **filters = + (const grpc_channel_filter **)gpr_malloc(sizeof(*filters) * num_filters); + size_t i = 0; + for (filter_node *p = builder->begin.next; p != &builder->end; p = p->next) { + filters[i++] = p->filter; + } + + // calculate the size of the channel stack + size_t channel_stack_size = grpc_channel_stack_size(filters, num_filters); + + // allocate memory, with prefix_bytes followed by channel_stack_size + *result = gpr_zalloc(prefix_bytes + channel_stack_size); + // fetch a pointer to the channel stack + grpc_channel_stack *channel_stack = + (grpc_channel_stack *)((char *)(*result) + prefix_bytes); + // and initialize it + grpc_error *error = grpc_channel_stack_init( + exec_ctx, initial_refs, destroy, + destroy_arg == NULL ? *result : destroy_arg, filters, num_filters, + builder->args, builder->transport, builder->name, channel_stack); + + if (error != GRPC_ERROR_NONE) { + grpc_channel_stack_destroy(exec_ctx, channel_stack); + gpr_free(*result); + *result = NULL; + } else { + // run post-initialization functions + i = 0; + for (filter_node *p = builder->begin.next; p != &builder->end; + p = p->next) { + if (p->init != NULL) { + p->init(channel_stack, grpc_channel_stack_element(channel_stack, i), + p->init_arg); + } + i++; + } + } + + grpc_channel_stack_builder_destroy(exec_ctx, builder); + gpr_free((grpc_channel_filter **)filters); + + return error; +} diff --git a/src/core/lib/channel/connected_channel.c b/src/core/lib/channel/connected_channel.c deleted file mode 100644 index 4f37908958..0000000000 --- a/src/core/lib/channel/connected_channel.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/channel/connected_channel.h" - -#include -#include -#include - -#include -#include -#include -#include -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/transport.h" - -#define MAX_BUFFER_LENGTH 8192 - -typedef struct connected_channel_channel_data { - grpc_transport *transport; -} channel_data; - -typedef struct { - grpc_closure closure; - grpc_closure *original_closure; - grpc_call_combiner *call_combiner; - const char *reason; -} callback_state; - -typedef struct connected_channel_call_data { - grpc_call_combiner *call_combiner; - // Closures used for returning results on the call combiner. - callback_state on_complete[6]; // Max number of pending batches. - callback_state recv_initial_metadata_ready; - callback_state recv_message_ready; -} call_data; - -static void run_in_call_combiner(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - callback_state *state = (callback_state *)arg; - GRPC_CALL_COMBINER_START(exec_ctx, state->call_combiner, - state->original_closure, GRPC_ERROR_REF(error), - state->reason); -} - -static void run_cancel_in_call_combiner(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - run_in_call_combiner(exec_ctx, arg, error); - gpr_free(arg); -} - -static void intercept_callback(call_data *calld, callback_state *state, - bool free_when_done, const char *reason, - grpc_closure **original_closure) { - state->original_closure = *original_closure; - state->call_combiner = calld->call_combiner; - state->reason = reason; - *original_closure = GRPC_CLOSURE_INIT( - &state->closure, - free_when_done ? run_cancel_in_call_combiner : run_in_call_combiner, - state, grpc_schedule_on_exec_ctx); -} - -static callback_state *get_state_for_batch( - call_data *calld, grpc_transport_stream_op_batch *batch) { - if (batch->send_initial_metadata) return &calld->on_complete[0]; - if (batch->send_message) return &calld->on_complete[1]; - if (batch->send_trailing_metadata) return &calld->on_complete[2]; - if (batch->recv_initial_metadata) return &calld->on_complete[3]; - if (batch->recv_message) return &calld->on_complete[4]; - if (batch->recv_trailing_metadata) return &calld->on_complete[5]; - GPR_UNREACHABLE_CODE(return NULL); -} - -/* We perform a small hack to locate transport data alongside the connected - channel data in call allocations, to allow everything to be pulled in minimal - cache line requests */ -#define TRANSPORT_STREAM_FROM_CALL_DATA(calld) ((grpc_stream *)((calld) + 1)) -#define CALL_DATA_FROM_TRANSPORT_STREAM(transport_stream) \ - (((call_data *)(transport_stream)) - 1) - -/* Intercept a call operation and either push it directly up or translate it - into transport stream operations */ -static void con_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (batch->recv_initial_metadata) { - callback_state *state = &calld->recv_initial_metadata_ready; - intercept_callback( - calld, state, false, "recv_initial_metadata_ready", - &batch->payload->recv_initial_metadata.recv_initial_metadata_ready); - } - if (batch->recv_message) { - callback_state *state = &calld->recv_message_ready; - intercept_callback(calld, state, false, "recv_message_ready", - &batch->payload->recv_message.recv_message_ready); - } - if (batch->cancel_stream) { - // There can be more than one cancellation batch in flight at any - // given time, so we can't just pick out a fixed index into - // calld->on_complete like we can for the other ops. However, - // cancellation isn't in the fast path, so we just allocate a new - // closure for each one. - callback_state *state = (callback_state *)gpr_malloc(sizeof(*state)); - intercept_callback(calld, state, true, "on_complete (cancel_stream)", - &batch->on_complete); - } else { - callback_state *state = get_state_for_batch(calld, batch); - intercept_callback(calld, state, false, "on_complete", &batch->on_complete); - } - grpc_transport_perform_stream_op(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), - batch); - GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, - "passed batch to transport"); -} - -static void con_start_transport_op(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_transport_op *op) { - channel_data *chand = (channel_data *)elem->channel_data; - grpc_transport_perform_op(exec_ctx, chand->transport, op); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - calld->call_combiner = args->call_combiner; - int r = grpc_transport_init_stream( - exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), - &args->call_stack->refcount, args->server_transport_data, args->arena); - return r == 0 ? GRPC_ERROR_NONE - : GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "transport stream initialization failed"); -} - -static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_polling_entity *pollent) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - grpc_transport_set_pops(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollent); -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *then_schedule_closure) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - grpc_transport_destroy_stream(exec_ctx, chand->transport, - TRANSPORT_STREAM_FROM_CALL_DATA(calld), - then_schedule_closure); -} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *cd = (channel_data *)elem->channel_data; - GPR_ASSERT(args->is_last); - cd->transport = NULL; - return GRPC_ERROR_NONE; -} - -/* Destructor for channel_data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *cd = (channel_data *)elem->channel_data; - if (cd->transport) { - grpc_transport_destroy(exec_ctx, cd->transport); - } -} - -/* No-op. */ -static void con_get_channel_info(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - const grpc_channel_info *channel_info) {} - -const grpc_channel_filter grpc_connected_filter = { - con_start_transport_stream_op_batch, - con_start_transport_op, - sizeof(call_data), - init_call_elem, - set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - con_get_channel_info, - "connected", -}; - -static void bind_transport(grpc_channel_stack *channel_stack, - grpc_channel_element *elem, void *t) { - channel_data *cd = (channel_data *)elem->channel_data; - GPR_ASSERT(elem->filter == &grpc_connected_filter); - GPR_ASSERT(cd->transport == NULL); - cd->transport = (grpc_transport *)t; - - /* HACK(ctiller): increase call stack size for the channel to make space - for channel data. We need a cleaner (but performant) way to do this, - and I'm not sure what that is yet. - This is only "safe" because call stacks place no additional data after - the last call element, and the last call element MUST be the connected - channel. */ - channel_stack->call_stack_size += - grpc_transport_stream_size((grpc_transport *)t); -} - -bool grpc_add_connected_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, - void *arg_must_be_null) { - GPR_ASSERT(arg_must_be_null == NULL); - grpc_transport *t = grpc_channel_stack_builder_get_transport(builder); - GPR_ASSERT(t != NULL); - return grpc_channel_stack_builder_append_filter( - builder, &grpc_connected_filter, bind_transport, t); -} - -grpc_stream *grpc_connected_channel_get_stream(grpc_call_element *elem) { - call_data *calld = (call_data *)elem->call_data; - return TRANSPORT_STREAM_FROM_CALL_DATA(calld); -} diff --git a/src/core/lib/channel/connected_channel.cc b/src/core/lib/channel/connected_channel.cc new file mode 100644 index 0000000000..4f37908958 --- /dev/null +++ b/src/core/lib/channel/connected_channel.cc @@ -0,0 +1,246 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/channel/connected_channel.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/transport.h" + +#define MAX_BUFFER_LENGTH 8192 + +typedef struct connected_channel_channel_data { + grpc_transport *transport; +} channel_data; + +typedef struct { + grpc_closure closure; + grpc_closure *original_closure; + grpc_call_combiner *call_combiner; + const char *reason; +} callback_state; + +typedef struct connected_channel_call_data { + grpc_call_combiner *call_combiner; + // Closures used for returning results on the call combiner. + callback_state on_complete[6]; // Max number of pending batches. + callback_state recv_initial_metadata_ready; + callback_state recv_message_ready; +} call_data; + +static void run_in_call_combiner(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + callback_state *state = (callback_state *)arg; + GRPC_CALL_COMBINER_START(exec_ctx, state->call_combiner, + state->original_closure, GRPC_ERROR_REF(error), + state->reason); +} + +static void run_cancel_in_call_combiner(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + run_in_call_combiner(exec_ctx, arg, error); + gpr_free(arg); +} + +static void intercept_callback(call_data *calld, callback_state *state, + bool free_when_done, const char *reason, + grpc_closure **original_closure) { + state->original_closure = *original_closure; + state->call_combiner = calld->call_combiner; + state->reason = reason; + *original_closure = GRPC_CLOSURE_INIT( + &state->closure, + free_when_done ? run_cancel_in_call_combiner : run_in_call_combiner, + state, grpc_schedule_on_exec_ctx); +} + +static callback_state *get_state_for_batch( + call_data *calld, grpc_transport_stream_op_batch *batch) { + if (batch->send_initial_metadata) return &calld->on_complete[0]; + if (batch->send_message) return &calld->on_complete[1]; + if (batch->send_trailing_metadata) return &calld->on_complete[2]; + if (batch->recv_initial_metadata) return &calld->on_complete[3]; + if (batch->recv_message) return &calld->on_complete[4]; + if (batch->recv_trailing_metadata) return &calld->on_complete[5]; + GPR_UNREACHABLE_CODE(return NULL); +} + +/* We perform a small hack to locate transport data alongside the connected + channel data in call allocations, to allow everything to be pulled in minimal + cache line requests */ +#define TRANSPORT_STREAM_FROM_CALL_DATA(calld) ((grpc_stream *)((calld) + 1)) +#define CALL_DATA_FROM_TRANSPORT_STREAM(transport_stream) \ + (((call_data *)(transport_stream)) - 1) + +/* Intercept a call operation and either push it directly up or translate it + into transport stream operations */ +static void con_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (batch->recv_initial_metadata) { + callback_state *state = &calld->recv_initial_metadata_ready; + intercept_callback( + calld, state, false, "recv_initial_metadata_ready", + &batch->payload->recv_initial_metadata.recv_initial_metadata_ready); + } + if (batch->recv_message) { + callback_state *state = &calld->recv_message_ready; + intercept_callback(calld, state, false, "recv_message_ready", + &batch->payload->recv_message.recv_message_ready); + } + if (batch->cancel_stream) { + // There can be more than one cancellation batch in flight at any + // given time, so we can't just pick out a fixed index into + // calld->on_complete like we can for the other ops. However, + // cancellation isn't in the fast path, so we just allocate a new + // closure for each one. + callback_state *state = (callback_state *)gpr_malloc(sizeof(*state)); + intercept_callback(calld, state, true, "on_complete (cancel_stream)", + &batch->on_complete); + } else { + callback_state *state = get_state_for_batch(calld, batch); + intercept_callback(calld, state, false, "on_complete", &batch->on_complete); + } + grpc_transport_perform_stream_op(exec_ctx, chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), + batch); + GRPC_CALL_COMBINER_STOP(exec_ctx, calld->call_combiner, + "passed batch to transport"); +} + +static void con_start_transport_op(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_transport_op *op) { + channel_data *chand = (channel_data *)elem->channel_data; + grpc_transport_perform_op(exec_ctx, chand->transport, op); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + calld->call_combiner = args->call_combiner; + int r = grpc_transport_init_stream( + exec_ctx, chand->transport, TRANSPORT_STREAM_FROM_CALL_DATA(calld), + &args->call_stack->refcount, args->server_transport_data, args->arena); + return r == 0 ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "transport stream initialization failed"); +} + +static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_polling_entity *pollent) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + grpc_transport_set_pops(exec_ctx, chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), pollent); +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *then_schedule_closure) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + grpc_transport_destroy_stream(exec_ctx, chand->transport, + TRANSPORT_STREAM_FROM_CALL_DATA(calld), + then_schedule_closure); +} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + channel_data *cd = (channel_data *)elem->channel_data; + GPR_ASSERT(args->is_last); + cd->transport = NULL; + return GRPC_ERROR_NONE; +} + +/* Destructor for channel_data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *cd = (channel_data *)elem->channel_data; + if (cd->transport) { + grpc_transport_destroy(exec_ctx, cd->transport); + } +} + +/* No-op. */ +static void con_get_channel_info(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + const grpc_channel_info *channel_info) {} + +const grpc_channel_filter grpc_connected_filter = { + con_start_transport_stream_op_batch, + con_start_transport_op, + sizeof(call_data), + init_call_elem, + set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + con_get_channel_info, + "connected", +}; + +static void bind_transport(grpc_channel_stack *channel_stack, + grpc_channel_element *elem, void *t) { + channel_data *cd = (channel_data *)elem->channel_data; + GPR_ASSERT(elem->filter == &grpc_connected_filter); + GPR_ASSERT(cd->transport == NULL); + cd->transport = (grpc_transport *)t; + + /* HACK(ctiller): increase call stack size for the channel to make space + for channel data. We need a cleaner (but performant) way to do this, + and I'm not sure what that is yet. + This is only "safe" because call stacks place no additional data after + the last call element, and the last call element MUST be the connected + channel. */ + channel_stack->call_stack_size += + grpc_transport_stream_size((grpc_transport *)t); +} + +bool grpc_add_connected_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, + void *arg_must_be_null) { + GPR_ASSERT(arg_must_be_null == NULL); + grpc_transport *t = grpc_channel_stack_builder_get_transport(builder); + GPR_ASSERT(t != NULL); + return grpc_channel_stack_builder_append_filter( + builder, &grpc_connected_filter, bind_transport, t); +} + +grpc_stream *grpc_connected_channel_get_stream(grpc_call_element *elem) { + call_data *calld = (call_data *)elem->call_data; + return TRANSPORT_STREAM_FROM_CALL_DATA(calld); +} diff --git a/src/core/lib/channel/handshaker.c b/src/core/lib/channel/handshaker.c deleted file mode 100644 index 1753da5721..0000000000 --- a/src/core/lib/channel/handshaker.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker.h" -#include "src/core/lib/iomgr/timer.h" - -// -// grpc_handshaker -// - -void grpc_handshaker_init(const grpc_handshaker_vtable* vtable, - grpc_handshaker* handshaker) { - handshaker->vtable = vtable; -} - -void grpc_handshaker_destroy(grpc_exec_ctx* exec_ctx, - grpc_handshaker* handshaker) { - handshaker->vtable->destroy(exec_ctx, handshaker); -} - -void grpc_handshaker_shutdown(grpc_exec_ctx* exec_ctx, - grpc_handshaker* handshaker, grpc_error* why) { - handshaker->vtable->shutdown(exec_ctx, handshaker, why); -} - -void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx, - grpc_handshaker* handshaker, - grpc_tcp_server_acceptor* acceptor, - grpc_closure* on_handshake_done, - grpc_handshaker_args* args) { - handshaker->vtable->do_handshake(exec_ctx, handshaker, acceptor, - on_handshake_done, args); -} - -// -// grpc_handshake_manager -// - -struct grpc_handshake_manager { - gpr_mu mu; - gpr_refcount refs; - bool shutdown; - // An array of handshakers added via grpc_handshake_manager_add(). - size_t count; - grpc_handshaker** handshakers; - // The index of the handshaker to invoke next and closure to invoke it. - size_t index; - grpc_closure call_next_handshaker; - // The acceptor to call the handshakers with. - grpc_tcp_server_acceptor* acceptor; - // Deadline timer across all handshakers. - grpc_timer deadline_timer; - grpc_closure on_timeout; - // The final callback and user_data to invoke after the last handshaker. - grpc_closure on_handshake_done; - void* user_data; - // Handshaker args. - grpc_handshaker_args args; - // Links to the previous and next managers in a list of all pending handshakes - // Used at server side only. - grpc_handshake_manager* prev; - grpc_handshake_manager* next; -}; - -grpc_handshake_manager* grpc_handshake_manager_create() { - grpc_handshake_manager* mgr = - (grpc_handshake_manager*)gpr_zalloc(sizeof(grpc_handshake_manager)); - gpr_mu_init(&mgr->mu); - gpr_ref_init(&mgr->refs, 1); - return mgr; -} - -void grpc_handshake_manager_pending_list_add(grpc_handshake_manager** head, - grpc_handshake_manager* mgr) { - GPR_ASSERT(mgr->prev == NULL); - GPR_ASSERT(mgr->next == NULL); - mgr->next = *head; - if (*head) { - (*head)->prev = mgr; - } - *head = mgr; -} - -void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head, - grpc_handshake_manager* mgr) { - if (mgr->next != NULL) { - mgr->next->prev = mgr->prev; - } - if (mgr->prev != NULL) { - mgr->prev->next = mgr->next; - } else { - GPR_ASSERT(*head == mgr); - *head = mgr->next; - } -} - -void grpc_handshake_manager_pending_list_shutdown_all( - grpc_exec_ctx* exec_ctx, grpc_handshake_manager* head, grpc_error* why) { - while (head != NULL) { - grpc_handshake_manager_shutdown(exec_ctx, head, GRPC_ERROR_REF(why)); - head = head->next; - } - GRPC_ERROR_UNREF(why); -} - -static bool is_power_of_2(size_t n) { return (n & (n - 1)) == 0; } - -void grpc_handshake_manager_add(grpc_handshake_manager* mgr, - grpc_handshaker* handshaker) { - gpr_mu_lock(&mgr->mu); - // To avoid allocating memory for each handshaker we add, we double - // the number of elements every time we need more. - size_t realloc_count = 0; - if (mgr->count == 0) { - realloc_count = 2; - } else if (mgr->count >= 2 && is_power_of_2(mgr->count)) { - realloc_count = mgr->count * 2; - } - if (realloc_count > 0) { - mgr->handshakers = (grpc_handshaker**)gpr_realloc( - mgr->handshakers, realloc_count * sizeof(grpc_handshaker*)); - } - mgr->handshakers[mgr->count++] = handshaker; - gpr_mu_unlock(&mgr->mu); -} - -static void grpc_handshake_manager_unref(grpc_exec_ctx* exec_ctx, - grpc_handshake_manager* mgr) { - if (gpr_unref(&mgr->refs)) { - for (size_t i = 0; i < mgr->count; ++i) { - grpc_handshaker_destroy(exec_ctx, mgr->handshakers[i]); - } - gpr_free(mgr->handshakers); - gpr_mu_destroy(&mgr->mu); - gpr_free(mgr); - } -} - -void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx, - grpc_handshake_manager* mgr) { - grpc_handshake_manager_unref(exec_ctx, mgr); -} - -void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx, - grpc_handshake_manager* mgr, - grpc_error* why) { - gpr_mu_lock(&mgr->mu); - // Shutdown the handshaker that's currently in progress, if any. - if (!mgr->shutdown && mgr->index > 0) { - mgr->shutdown = true; - grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[mgr->index - 1], - GRPC_ERROR_REF(why)); - } - gpr_mu_unlock(&mgr->mu); - GRPC_ERROR_UNREF(why); -} - -// Helper function to call either the next handshaker or the -// on_handshake_done callback. -// Returns true if we've scheduled the on_handshake_done callback. -static bool call_next_handshaker_locked(grpc_exec_ctx* exec_ctx, - grpc_handshake_manager* mgr, - grpc_error* error) { - GPR_ASSERT(mgr->index <= mgr->count); - // If we got an error or we've been shut down or we're exiting early or - // we've finished the last handshaker, invoke the on_handshake_done - // callback. Otherwise, call the next handshaker. - if (error != GRPC_ERROR_NONE || mgr->shutdown || mgr->args.exit_early || - mgr->index == mgr->count) { - // Cancel deadline timer, since we're invoking the on_handshake_done - // callback now. - grpc_timer_cancel(exec_ctx, &mgr->deadline_timer); - GRPC_CLOSURE_SCHED(exec_ctx, &mgr->on_handshake_done, error); - mgr->shutdown = true; - } else { - grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index], - mgr->acceptor, &mgr->call_next_handshaker, - &mgr->args); - } - ++mgr->index; - return mgr->shutdown; -} - -// A function used as the handshaker-done callback when chaining -// handshakers together. -static void call_next_handshaker(grpc_exec_ctx* exec_ctx, void* arg, - grpc_error* error) { - grpc_handshake_manager* mgr = (grpc_handshake_manager*)arg; - gpr_mu_lock(&mgr->mu); - bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error)); - gpr_mu_unlock(&mgr->mu); - // If we're invoked the final callback, we won't be coming back - // to this function, so we can release our reference to the - // handshake manager. - if (done) { - grpc_handshake_manager_unref(exec_ctx, mgr); - } -} - -// Callback invoked when deadline is exceeded. -static void on_timeout(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { - grpc_handshake_manager* mgr = (grpc_handshake_manager*)arg; - if (error == GRPC_ERROR_NONE) { // Timer fired, rather than being cancelled. - grpc_handshake_manager_shutdown( - exec_ctx, mgr, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake timed out")); - } - grpc_handshake_manager_unref(exec_ctx, mgr); -} - -void grpc_handshake_manager_do_handshake( - grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr, - grpc_endpoint* endpoint, const grpc_channel_args* channel_args, - gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor, - grpc_iomgr_cb_func on_handshake_done, void* user_data) { - gpr_mu_lock(&mgr->mu); - GPR_ASSERT(mgr->index == 0); - GPR_ASSERT(!mgr->shutdown); - // Construct handshaker args. These will be passed through all - // handshakers and eventually be freed by the on_handshake_done callback. - mgr->args.endpoint = endpoint; - mgr->args.args = grpc_channel_args_copy(channel_args); - mgr->args.user_data = user_data; - mgr->args.read_buffer = - (grpc_slice_buffer*)gpr_malloc(sizeof(*mgr->args.read_buffer)); - grpc_slice_buffer_init(mgr->args.read_buffer); - // Initialize state needed for calling handshakers. - mgr->acceptor = acceptor; - GRPC_CLOSURE_INIT(&mgr->call_next_handshaker, call_next_handshaker, mgr, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&mgr->on_handshake_done, on_handshake_done, &mgr->args, - grpc_schedule_on_exec_ctx); - // Start deadline timer, which owns a ref. - gpr_ref(&mgr->refs); - GRPC_CLOSURE_INIT(&mgr->on_timeout, on_timeout, mgr, - grpc_schedule_on_exec_ctx); - grpc_timer_init(exec_ctx, &mgr->deadline_timer, - gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), - &mgr->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC)); - // Start first handshaker, which also owns a ref. - gpr_ref(&mgr->refs); - bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE); - gpr_mu_unlock(&mgr->mu); - if (done) { - grpc_handshake_manager_unref(exec_ctx, mgr); - } -} diff --git a/src/core/lib/channel/handshaker.cc b/src/core/lib/channel/handshaker.cc new file mode 100644 index 0000000000..1753da5721 --- /dev/null +++ b/src/core/lib/channel/handshaker.cc @@ -0,0 +1,268 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/iomgr/timer.h" + +// +// grpc_handshaker +// + +void grpc_handshaker_init(const grpc_handshaker_vtable* vtable, + grpc_handshaker* handshaker) { + handshaker->vtable = vtable; +} + +void grpc_handshaker_destroy(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker) { + handshaker->vtable->destroy(exec_ctx, handshaker); +} + +void grpc_handshaker_shutdown(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker, grpc_error* why) { + handshaker->vtable->shutdown(exec_ctx, handshaker, why); +} + +void grpc_handshaker_do_handshake(grpc_exec_ctx* exec_ctx, + grpc_handshaker* handshaker, + grpc_tcp_server_acceptor* acceptor, + grpc_closure* on_handshake_done, + grpc_handshaker_args* args) { + handshaker->vtable->do_handshake(exec_ctx, handshaker, acceptor, + on_handshake_done, args); +} + +// +// grpc_handshake_manager +// + +struct grpc_handshake_manager { + gpr_mu mu; + gpr_refcount refs; + bool shutdown; + // An array of handshakers added via grpc_handshake_manager_add(). + size_t count; + grpc_handshaker** handshakers; + // The index of the handshaker to invoke next and closure to invoke it. + size_t index; + grpc_closure call_next_handshaker; + // The acceptor to call the handshakers with. + grpc_tcp_server_acceptor* acceptor; + // Deadline timer across all handshakers. + grpc_timer deadline_timer; + grpc_closure on_timeout; + // The final callback and user_data to invoke after the last handshaker. + grpc_closure on_handshake_done; + void* user_data; + // Handshaker args. + grpc_handshaker_args args; + // Links to the previous and next managers in a list of all pending handshakes + // Used at server side only. + grpc_handshake_manager* prev; + grpc_handshake_manager* next; +}; + +grpc_handshake_manager* grpc_handshake_manager_create() { + grpc_handshake_manager* mgr = + (grpc_handshake_manager*)gpr_zalloc(sizeof(grpc_handshake_manager)); + gpr_mu_init(&mgr->mu); + gpr_ref_init(&mgr->refs, 1); + return mgr; +} + +void grpc_handshake_manager_pending_list_add(grpc_handshake_manager** head, + grpc_handshake_manager* mgr) { + GPR_ASSERT(mgr->prev == NULL); + GPR_ASSERT(mgr->next == NULL); + mgr->next = *head; + if (*head) { + (*head)->prev = mgr; + } + *head = mgr; +} + +void grpc_handshake_manager_pending_list_remove(grpc_handshake_manager** head, + grpc_handshake_manager* mgr) { + if (mgr->next != NULL) { + mgr->next->prev = mgr->prev; + } + if (mgr->prev != NULL) { + mgr->prev->next = mgr->next; + } else { + GPR_ASSERT(*head == mgr); + *head = mgr->next; + } +} + +void grpc_handshake_manager_pending_list_shutdown_all( + grpc_exec_ctx* exec_ctx, grpc_handshake_manager* head, grpc_error* why) { + while (head != NULL) { + grpc_handshake_manager_shutdown(exec_ctx, head, GRPC_ERROR_REF(why)); + head = head->next; + } + GRPC_ERROR_UNREF(why); +} + +static bool is_power_of_2(size_t n) { return (n & (n - 1)) == 0; } + +void grpc_handshake_manager_add(grpc_handshake_manager* mgr, + grpc_handshaker* handshaker) { + gpr_mu_lock(&mgr->mu); + // To avoid allocating memory for each handshaker we add, we double + // the number of elements every time we need more. + size_t realloc_count = 0; + if (mgr->count == 0) { + realloc_count = 2; + } else if (mgr->count >= 2 && is_power_of_2(mgr->count)) { + realloc_count = mgr->count * 2; + } + if (realloc_count > 0) { + mgr->handshakers = (grpc_handshaker**)gpr_realloc( + mgr->handshakers, realloc_count * sizeof(grpc_handshaker*)); + } + mgr->handshakers[mgr->count++] = handshaker; + gpr_mu_unlock(&mgr->mu); +} + +static void grpc_handshake_manager_unref(grpc_exec_ctx* exec_ctx, + grpc_handshake_manager* mgr) { + if (gpr_unref(&mgr->refs)) { + for (size_t i = 0; i < mgr->count; ++i) { + grpc_handshaker_destroy(exec_ctx, mgr->handshakers[i]); + } + gpr_free(mgr->handshakers); + gpr_mu_destroy(&mgr->mu); + gpr_free(mgr); + } +} + +void grpc_handshake_manager_destroy(grpc_exec_ctx* exec_ctx, + grpc_handshake_manager* mgr) { + grpc_handshake_manager_unref(exec_ctx, mgr); +} + +void grpc_handshake_manager_shutdown(grpc_exec_ctx* exec_ctx, + grpc_handshake_manager* mgr, + grpc_error* why) { + gpr_mu_lock(&mgr->mu); + // Shutdown the handshaker that's currently in progress, if any. + if (!mgr->shutdown && mgr->index > 0) { + mgr->shutdown = true; + grpc_handshaker_shutdown(exec_ctx, mgr->handshakers[mgr->index - 1], + GRPC_ERROR_REF(why)); + } + gpr_mu_unlock(&mgr->mu); + GRPC_ERROR_UNREF(why); +} + +// Helper function to call either the next handshaker or the +// on_handshake_done callback. +// Returns true if we've scheduled the on_handshake_done callback. +static bool call_next_handshaker_locked(grpc_exec_ctx* exec_ctx, + grpc_handshake_manager* mgr, + grpc_error* error) { + GPR_ASSERT(mgr->index <= mgr->count); + // If we got an error or we've been shut down or we're exiting early or + // we've finished the last handshaker, invoke the on_handshake_done + // callback. Otherwise, call the next handshaker. + if (error != GRPC_ERROR_NONE || mgr->shutdown || mgr->args.exit_early || + mgr->index == mgr->count) { + // Cancel deadline timer, since we're invoking the on_handshake_done + // callback now. + grpc_timer_cancel(exec_ctx, &mgr->deadline_timer); + GRPC_CLOSURE_SCHED(exec_ctx, &mgr->on_handshake_done, error); + mgr->shutdown = true; + } else { + grpc_handshaker_do_handshake(exec_ctx, mgr->handshakers[mgr->index], + mgr->acceptor, &mgr->call_next_handshaker, + &mgr->args); + } + ++mgr->index; + return mgr->shutdown; +} + +// A function used as the handshaker-done callback when chaining +// handshakers together. +static void call_next_handshaker(grpc_exec_ctx* exec_ctx, void* arg, + grpc_error* error) { + grpc_handshake_manager* mgr = (grpc_handshake_manager*)arg; + gpr_mu_lock(&mgr->mu); + bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&mgr->mu); + // If we're invoked the final callback, we won't be coming back + // to this function, so we can release our reference to the + // handshake manager. + if (done) { + grpc_handshake_manager_unref(exec_ctx, mgr); + } +} + +// Callback invoked when deadline is exceeded. +static void on_timeout(grpc_exec_ctx* exec_ctx, void* arg, grpc_error* error) { + grpc_handshake_manager* mgr = (grpc_handshake_manager*)arg; + if (error == GRPC_ERROR_NONE) { // Timer fired, rather than being cancelled. + grpc_handshake_manager_shutdown( + exec_ctx, mgr, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake timed out")); + } + grpc_handshake_manager_unref(exec_ctx, mgr); +} + +void grpc_handshake_manager_do_handshake( + grpc_exec_ctx* exec_ctx, grpc_handshake_manager* mgr, + grpc_endpoint* endpoint, const grpc_channel_args* channel_args, + gpr_timespec deadline, grpc_tcp_server_acceptor* acceptor, + grpc_iomgr_cb_func on_handshake_done, void* user_data) { + gpr_mu_lock(&mgr->mu); + GPR_ASSERT(mgr->index == 0); + GPR_ASSERT(!mgr->shutdown); + // Construct handshaker args. These will be passed through all + // handshakers and eventually be freed by the on_handshake_done callback. + mgr->args.endpoint = endpoint; + mgr->args.args = grpc_channel_args_copy(channel_args); + mgr->args.user_data = user_data; + mgr->args.read_buffer = + (grpc_slice_buffer*)gpr_malloc(sizeof(*mgr->args.read_buffer)); + grpc_slice_buffer_init(mgr->args.read_buffer); + // Initialize state needed for calling handshakers. + mgr->acceptor = acceptor; + GRPC_CLOSURE_INIT(&mgr->call_next_handshaker, call_next_handshaker, mgr, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&mgr->on_handshake_done, on_handshake_done, &mgr->args, + grpc_schedule_on_exec_ctx); + // Start deadline timer, which owns a ref. + gpr_ref(&mgr->refs); + GRPC_CLOSURE_INIT(&mgr->on_timeout, on_timeout, mgr, + grpc_schedule_on_exec_ctx); + grpc_timer_init(exec_ctx, &mgr->deadline_timer, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + &mgr->on_timeout, gpr_now(GPR_CLOCK_MONOTONIC)); + // Start first handshaker, which also owns a ref. + gpr_ref(&mgr->refs); + bool done = call_next_handshaker_locked(exec_ctx, mgr, GRPC_ERROR_NONE); + gpr_mu_unlock(&mgr->mu); + if (done) { + grpc_handshake_manager_unref(exec_ctx, mgr); + } +} diff --git a/src/core/lib/channel/handshaker_factory.c b/src/core/lib/channel/handshaker_factory.c deleted file mode 100644 index 4deb280c60..0000000000 --- a/src/core/lib/channel/handshaker_factory.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/channel/handshaker_factory.h" - -#include - -void grpc_handshaker_factory_add_handshakers( - grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory, - const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { - if (handshaker_factory != NULL) { - GPR_ASSERT(handshaker_factory->vtable != NULL); - handshaker_factory->vtable->add_handshakers(exec_ctx, handshaker_factory, - args, handshake_mgr); - } -} - -void grpc_handshaker_factory_destroy( - grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) { - if (handshaker_factory != NULL) { - GPR_ASSERT(handshaker_factory->vtable != NULL); - handshaker_factory->vtable->destroy(exec_ctx, handshaker_factory); - } -} diff --git a/src/core/lib/channel/handshaker_factory.cc b/src/core/lib/channel/handshaker_factory.cc new file mode 100644 index 0000000000..4deb280c60 --- /dev/null +++ b/src/core/lib/channel/handshaker_factory.cc @@ -0,0 +1,39 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/channel/handshaker_factory.h" + +#include + +void grpc_handshaker_factory_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory, + const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { + if (handshaker_factory != NULL) { + GPR_ASSERT(handshaker_factory->vtable != NULL); + handshaker_factory->vtable->add_handshakers(exec_ctx, handshaker_factory, + args, handshake_mgr); + } +} + +void grpc_handshaker_factory_destroy( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) { + if (handshaker_factory != NULL) { + GPR_ASSERT(handshaker_factory->vtable != NULL); + handshaker_factory->vtable->destroy(exec_ctx, handshaker_factory); + } +} diff --git a/src/core/lib/channel/handshaker_registry.c b/src/core/lib/channel/handshaker_registry.c deleted file mode 100644 index c6bc87d704..0000000000 --- a/src/core/lib/channel/handshaker_registry.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/channel/handshaker_registry.h" - -#include - -#include - -// -// grpc_handshaker_factory_list -// - -typedef struct { - grpc_handshaker_factory** list; - size_t num_factories; -} grpc_handshaker_factory_list; - -static void grpc_handshaker_factory_list_register( - grpc_handshaker_factory_list* list, bool at_start, - grpc_handshaker_factory* factory) { - list->list = (grpc_handshaker_factory**)gpr_realloc( - list->list, (list->num_factories + 1) * sizeof(grpc_handshaker_factory*)); - if (at_start) { - memmove(list->list + 1, list->list, - sizeof(grpc_handshaker_factory*) * list->num_factories); - list->list[0] = factory; - } else { - list->list[list->num_factories] = factory; - } - ++list->num_factories; -} - -static void grpc_handshaker_factory_list_add_handshakers( - grpc_exec_ctx* exec_ctx, grpc_handshaker_factory_list* list, - const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) { - for (size_t i = 0; i < list->num_factories; ++i) { - grpc_handshaker_factory_add_handshakers(exec_ctx, list->list[i], args, - handshake_mgr); - } -} - -static void grpc_handshaker_factory_list_destroy( - grpc_exec_ctx* exec_ctx, grpc_handshaker_factory_list* list) { - for (size_t i = 0; i < list->num_factories; ++i) { - grpc_handshaker_factory_destroy(exec_ctx, list->list[i]); - } - gpr_free(list->list); -} - -// -// plugin -// - -static grpc_handshaker_factory_list - g_handshaker_factory_lists[NUM_HANDSHAKER_TYPES]; - -void grpc_handshaker_factory_registry_init() { - memset(g_handshaker_factory_lists, 0, sizeof(g_handshaker_factory_lists)); -} - -void grpc_handshaker_factory_registry_shutdown(grpc_exec_ctx* exec_ctx) { - for (size_t i = 0; i < NUM_HANDSHAKER_TYPES; ++i) { - grpc_handshaker_factory_list_destroy(exec_ctx, - &g_handshaker_factory_lists[i]); - } -} - -void grpc_handshaker_factory_register(bool at_start, - grpc_handshaker_type handshaker_type, - grpc_handshaker_factory* factory) { - grpc_handshaker_factory_list_register( - &g_handshaker_factory_lists[handshaker_type], at_start, factory); -} - -void grpc_handshakers_add(grpc_exec_ctx* exec_ctx, - grpc_handshaker_type handshaker_type, - const grpc_channel_args* args, - grpc_handshake_manager* handshake_mgr) { - grpc_handshaker_factory_list_add_handshakers( - exec_ctx, &g_handshaker_factory_lists[handshaker_type], args, - handshake_mgr); -} diff --git a/src/core/lib/channel/handshaker_registry.cc b/src/core/lib/channel/handshaker_registry.cc new file mode 100644 index 0000000000..c6bc87d704 --- /dev/null +++ b/src/core/lib/channel/handshaker_registry.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/channel/handshaker_registry.h" + +#include + +#include + +// +// grpc_handshaker_factory_list +// + +typedef struct { + grpc_handshaker_factory** list; + size_t num_factories; +} grpc_handshaker_factory_list; + +static void grpc_handshaker_factory_list_register( + grpc_handshaker_factory_list* list, bool at_start, + grpc_handshaker_factory* factory) { + list->list = (grpc_handshaker_factory**)gpr_realloc( + list->list, (list->num_factories + 1) * sizeof(grpc_handshaker_factory*)); + if (at_start) { + memmove(list->list + 1, list->list, + sizeof(grpc_handshaker_factory*) * list->num_factories); + list->list[0] = factory; + } else { + list->list[list->num_factories] = factory; + } + ++list->num_factories; +} + +static void grpc_handshaker_factory_list_add_handshakers( + grpc_exec_ctx* exec_ctx, grpc_handshaker_factory_list* list, + const grpc_channel_args* args, grpc_handshake_manager* handshake_mgr) { + for (size_t i = 0; i < list->num_factories; ++i) { + grpc_handshaker_factory_add_handshakers(exec_ctx, list->list[i], args, + handshake_mgr); + } +} + +static void grpc_handshaker_factory_list_destroy( + grpc_exec_ctx* exec_ctx, grpc_handshaker_factory_list* list) { + for (size_t i = 0; i < list->num_factories; ++i) { + grpc_handshaker_factory_destroy(exec_ctx, list->list[i]); + } + gpr_free(list->list); +} + +// +// plugin +// + +static grpc_handshaker_factory_list + g_handshaker_factory_lists[NUM_HANDSHAKER_TYPES]; + +void grpc_handshaker_factory_registry_init() { + memset(g_handshaker_factory_lists, 0, sizeof(g_handshaker_factory_lists)); +} + +void grpc_handshaker_factory_registry_shutdown(grpc_exec_ctx* exec_ctx) { + for (size_t i = 0; i < NUM_HANDSHAKER_TYPES; ++i) { + grpc_handshaker_factory_list_destroy(exec_ctx, + &g_handshaker_factory_lists[i]); + } +} + +void grpc_handshaker_factory_register(bool at_start, + grpc_handshaker_type handshaker_type, + grpc_handshaker_factory* factory) { + grpc_handshaker_factory_list_register( + &g_handshaker_factory_lists[handshaker_type], at_start, factory); +} + +void grpc_handshakers_add(grpc_exec_ctx* exec_ctx, + grpc_handshaker_type handshaker_type, + const grpc_channel_args* args, + grpc_handshake_manager* handshake_mgr) { + grpc_handshaker_factory_list_add_handshakers( + exec_ctx, &g_handshaker_factory_lists[handshaker_type], args, + handshake_mgr); +} diff --git a/src/core/lib/compression/compression.c b/src/core/lib/compression/compression.c deleted file mode 100644 index 1cfac23129..0000000000 --- a/src/core/lib/compression/compression.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include -#include - -#include "src/core/lib/compression/algorithm_metadata.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/transport/static_metadata.h" - -int grpc_compression_algorithm_parse(grpc_slice name, - grpc_compression_algorithm *algorithm) { - /* we use strncmp not only because it's safer (even though in this case it - * doesn't matter, given that we are comparing against string literals, but - * because this way we needn't have "name" nil-terminated (useful for slice - * data, for example) */ - if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) { - *algorithm = GRPC_COMPRESS_NONE; - return 1; - } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) { - *algorithm = GRPC_COMPRESS_GZIP; - return 1; - } else if (grpc_slice_eq(name, GRPC_MDSTR_DEFLATE)) { - *algorithm = GRPC_COMPRESS_DEFLATE; - return 1; - } else { - return 0; - } -} - -int grpc_stream_compression_algorithm_parse( - grpc_slice name, grpc_stream_compression_algorithm *algorithm) { - if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) { - *algorithm = GRPC_STREAM_COMPRESS_NONE; - return 1; - } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) { - *algorithm = GRPC_STREAM_COMPRESS_GZIP; - return 1; - } else { - return 0; - } -} - -int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, - const char **name) { - GRPC_API_TRACE("grpc_compression_algorithm_parse(algorithm=%d, name=%p)", 2, - ((int)algorithm, name)); - switch (algorithm) { - case GRPC_COMPRESS_NONE: - *name = "identity"; - return 1; - case GRPC_COMPRESS_DEFLATE: - *name = "deflate"; - return 1; - case GRPC_COMPRESS_GZIP: - *name = "gzip"; - return 1; - case GRPC_COMPRESS_ALGORITHMS_COUNT: - return 0; - } - return 0; -} - -int grpc_stream_compression_algorithm_name( - grpc_stream_compression_algorithm algorithm, const char **name) { - GRPC_API_TRACE( - "grpc_stream_compression_algorithm_parse(algorithm=%d, name=%p)", 2, - ((int)algorithm, name)); - switch (algorithm) { - case GRPC_STREAM_COMPRESS_NONE: - *name = "identity"; - return 1; - case GRPC_STREAM_COMPRESS_GZIP: - *name = "gzip"; - return 1; - case GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT: - return 0; - } - return 0; -} - -grpc_compression_algorithm grpc_compression_algorithm_from_slice( - grpc_slice str) { - if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_COMPRESS_NONE; - if (grpc_slice_eq(str, GRPC_MDSTR_DEFLATE)) return GRPC_COMPRESS_DEFLATE; - if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_COMPRESS_GZIP; - return GRPC_COMPRESS_ALGORITHMS_COUNT; -} - -grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice( - grpc_slice str) { - if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_STREAM_COMPRESS_NONE; - if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_STREAM_COMPRESS_GZIP; - return GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT; -} - -grpc_slice grpc_compression_algorithm_slice( - grpc_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - return GRPC_MDSTR_IDENTITY; - case GRPC_COMPRESS_DEFLATE: - return GRPC_MDSTR_DEFLATE; - case GRPC_COMPRESS_GZIP: - return GRPC_MDSTR_GZIP; - case GRPC_COMPRESS_ALGORITHMS_COUNT: - return grpc_empty_slice(); - } - return grpc_empty_slice(); -} - -grpc_slice grpc_stream_compression_algorithm_slice( - grpc_stream_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_STREAM_COMPRESS_NONE: - return GRPC_MDSTR_IDENTITY; - case GRPC_STREAM_COMPRESS_GZIP: - return GRPC_MDSTR_GZIP; - case GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT: - return grpc_empty_slice(); - } - return grpc_empty_slice(); -} - -grpc_mdelem grpc_compression_encoding_mdelem( - grpc_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - return GRPC_MDELEM_GRPC_ENCODING_IDENTITY; - case GRPC_COMPRESS_DEFLATE: - return GRPC_MDELEM_GRPC_ENCODING_DEFLATE; - case GRPC_COMPRESS_GZIP: - return GRPC_MDELEM_GRPC_ENCODING_GZIP; - default: - break; - } - return GRPC_MDNULL; -} - -grpc_mdelem grpc_stream_compression_encoding_mdelem( - grpc_stream_compression_algorithm algorithm) { - switch (algorithm) { - case GRPC_STREAM_COMPRESS_NONE: - return GRPC_MDELEM_CONTENT_ENCODING_IDENTITY; - case GRPC_STREAM_COMPRESS_GZIP: - return GRPC_MDELEM_CONTENT_ENCODING_GZIP; - default: - break; - } - return GRPC_MDNULL; -} - -void grpc_compression_options_init(grpc_compression_options *opts) { - memset(opts, 0, sizeof(*opts)); - /* all enabled by default */ - opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; - opts->enabled_stream_compression_algorithms_bitset = - (1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 1; -} - -void grpc_compression_options_enable_algorithm( - grpc_compression_options *opts, grpc_compression_algorithm algorithm) { - GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm); -} - -void grpc_compression_options_disable_algorithm( - grpc_compression_options *opts, grpc_compression_algorithm algorithm) { - GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm); -} - -int grpc_compression_options_is_algorithm_enabled( - const grpc_compression_options *opts, - grpc_compression_algorithm algorithm) { - return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm); -} - -int grpc_compression_options_is_stream_compression_algorithm_enabled( - const grpc_compression_options *opts, - grpc_stream_compression_algorithm algorithm) { - return GPR_BITGET(opts->enabled_stream_compression_algorithms_bitset, - algorithm); -} - -/* TODO(dgq): Add the ability to specify parameters to the individual - * compression algorithms */ -grpc_compression_algorithm grpc_compression_algorithm_for_level( - grpc_compression_level level, uint32_t accepted_encodings) { - GRPC_API_TRACE("grpc_compression_algorithm_for_level(level=%d)", 1, - ((int)level)); - if (level > GRPC_COMPRESS_LEVEL_HIGH) { - gpr_log(GPR_ERROR, "Unknown compression level %d.", (int)level); - abort(); - } - - const size_t num_supported = - GPR_BITCOUNT(accepted_encodings) - 1; /* discard NONE */ - if (level == GRPC_COMPRESS_LEVEL_NONE || num_supported == 0) { - return GRPC_COMPRESS_NONE; - } - - GPR_ASSERT(level > 0); - - /* Establish a "ranking" or compression algorithms in increasing order of - * compression. - * This is simplistic and we will probably want to introduce other dimensions - * in the future (cpu/memory cost, etc). */ - const grpc_compression_algorithm algos_ranking[] = {GRPC_COMPRESS_GZIP, - GRPC_COMPRESS_DEFLATE}; - - /* intersect algos_ranking with the supported ones keeping the ranked order */ - grpc_compression_algorithm - sorted_supported_algos[GRPC_COMPRESS_ALGORITHMS_COUNT]; - size_t algos_supported_idx = 0; - for (size_t i = 0; i < GPR_ARRAY_SIZE(algos_ranking); i++) { - const grpc_compression_algorithm alg = algos_ranking[i]; - for (size_t j = 0; j < num_supported; j++) { - if (GPR_BITGET(accepted_encodings, alg) == 1) { - /* if \a alg in supported */ - sorted_supported_algos[algos_supported_idx++] = alg; - break; - } - } - if (algos_supported_idx == num_supported) break; - } - - switch (level) { - case GRPC_COMPRESS_LEVEL_NONE: - abort(); /* should have been handled already */ - case GRPC_COMPRESS_LEVEL_LOW: - return sorted_supported_algos[0]; - case GRPC_COMPRESS_LEVEL_MED: - return sorted_supported_algos[num_supported / 2]; - case GRPC_COMPRESS_LEVEL_HIGH: - return sorted_supported_algos[num_supported - 1]; - default: - abort(); - }; -} - -GRPCAPI grpc_stream_compression_algorithm -grpc_stream_compression_algorithm_for_level( - grpc_stream_compression_level level, uint32_t accepted_stream_encodings) { - GRPC_API_TRACE("grpc_stream_compression_algorithm_for_level(level=%d)", 1, - ((int)level)); - if (level > GRPC_STREAM_COMPRESS_LEVEL_HIGH) { - gpr_log(GPR_ERROR, "Unknown compression level %d.", (int)level); - abort(); - } - - switch (level) { - case GRPC_STREAM_COMPRESS_LEVEL_NONE: - return GRPC_STREAM_COMPRESS_NONE; - case GRPC_STREAM_COMPRESS_LEVEL_LOW: - case GRPC_STREAM_COMPRESS_LEVEL_MED: - case GRPC_STREAM_COMPRESS_LEVEL_HIGH: - if (GPR_BITGET(accepted_stream_encodings, GRPC_STREAM_COMPRESS_GZIP) == - 1) { - return GRPC_STREAM_COMPRESS_GZIP; - } else { - return GRPC_STREAM_COMPRESS_NONE; - } - default: - abort(); - } -} diff --git a/src/core/lib/compression/compression.cc b/src/core/lib/compression/compression.cc new file mode 100644 index 0000000000..1cfac23129 --- /dev/null +++ b/src/core/lib/compression/compression.cc @@ -0,0 +1,283 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/transport/static_metadata.h" + +int grpc_compression_algorithm_parse(grpc_slice name, + grpc_compression_algorithm *algorithm) { + /* we use strncmp not only because it's safer (even though in this case it + * doesn't matter, given that we are comparing against string literals, but + * because this way we needn't have "name" nil-terminated (useful for slice + * data, for example) */ + if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) { + *algorithm = GRPC_COMPRESS_NONE; + return 1; + } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) { + *algorithm = GRPC_COMPRESS_GZIP; + return 1; + } else if (grpc_slice_eq(name, GRPC_MDSTR_DEFLATE)) { + *algorithm = GRPC_COMPRESS_DEFLATE; + return 1; + } else { + return 0; + } +} + +int grpc_stream_compression_algorithm_parse( + grpc_slice name, grpc_stream_compression_algorithm *algorithm) { + if (grpc_slice_eq(name, GRPC_MDSTR_IDENTITY)) { + *algorithm = GRPC_STREAM_COMPRESS_NONE; + return 1; + } else if (grpc_slice_eq(name, GRPC_MDSTR_GZIP)) { + *algorithm = GRPC_STREAM_COMPRESS_GZIP; + return 1; + } else { + return 0; + } +} + +int grpc_compression_algorithm_name(grpc_compression_algorithm algorithm, + const char **name) { + GRPC_API_TRACE("grpc_compression_algorithm_parse(algorithm=%d, name=%p)", 2, + ((int)algorithm, name)); + switch (algorithm) { + case GRPC_COMPRESS_NONE: + *name = "identity"; + return 1; + case GRPC_COMPRESS_DEFLATE: + *name = "deflate"; + return 1; + case GRPC_COMPRESS_GZIP: + *name = "gzip"; + return 1; + case GRPC_COMPRESS_ALGORITHMS_COUNT: + return 0; + } + return 0; +} + +int grpc_stream_compression_algorithm_name( + grpc_stream_compression_algorithm algorithm, const char **name) { + GRPC_API_TRACE( + "grpc_stream_compression_algorithm_parse(algorithm=%d, name=%p)", 2, + ((int)algorithm, name)); + switch (algorithm) { + case GRPC_STREAM_COMPRESS_NONE: + *name = "identity"; + return 1; + case GRPC_STREAM_COMPRESS_GZIP: + *name = "gzip"; + return 1; + case GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT: + return 0; + } + return 0; +} + +grpc_compression_algorithm grpc_compression_algorithm_from_slice( + grpc_slice str) { + if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_COMPRESS_NONE; + if (grpc_slice_eq(str, GRPC_MDSTR_DEFLATE)) return GRPC_COMPRESS_DEFLATE; + if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_COMPRESS_GZIP; + return GRPC_COMPRESS_ALGORITHMS_COUNT; +} + +grpc_stream_compression_algorithm grpc_stream_compression_algorithm_from_slice( + grpc_slice str) { + if (grpc_slice_eq(str, GRPC_MDSTR_IDENTITY)) return GRPC_STREAM_COMPRESS_NONE; + if (grpc_slice_eq(str, GRPC_MDSTR_GZIP)) return GRPC_STREAM_COMPRESS_GZIP; + return GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT; +} + +grpc_slice grpc_compression_algorithm_slice( + grpc_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + return GRPC_MDSTR_IDENTITY; + case GRPC_COMPRESS_DEFLATE: + return GRPC_MDSTR_DEFLATE; + case GRPC_COMPRESS_GZIP: + return GRPC_MDSTR_GZIP; + case GRPC_COMPRESS_ALGORITHMS_COUNT: + return grpc_empty_slice(); + } + return grpc_empty_slice(); +} + +grpc_slice grpc_stream_compression_algorithm_slice( + grpc_stream_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_STREAM_COMPRESS_NONE: + return GRPC_MDSTR_IDENTITY; + case GRPC_STREAM_COMPRESS_GZIP: + return GRPC_MDSTR_GZIP; + case GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT: + return grpc_empty_slice(); + } + return grpc_empty_slice(); +} + +grpc_mdelem grpc_compression_encoding_mdelem( + grpc_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + return GRPC_MDELEM_GRPC_ENCODING_IDENTITY; + case GRPC_COMPRESS_DEFLATE: + return GRPC_MDELEM_GRPC_ENCODING_DEFLATE; + case GRPC_COMPRESS_GZIP: + return GRPC_MDELEM_GRPC_ENCODING_GZIP; + default: + break; + } + return GRPC_MDNULL; +} + +grpc_mdelem grpc_stream_compression_encoding_mdelem( + grpc_stream_compression_algorithm algorithm) { + switch (algorithm) { + case GRPC_STREAM_COMPRESS_NONE: + return GRPC_MDELEM_CONTENT_ENCODING_IDENTITY; + case GRPC_STREAM_COMPRESS_GZIP: + return GRPC_MDELEM_CONTENT_ENCODING_GZIP; + default: + break; + } + return GRPC_MDNULL; +} + +void grpc_compression_options_init(grpc_compression_options *opts) { + memset(opts, 0, sizeof(*opts)); + /* all enabled by default */ + opts->enabled_algorithms_bitset = (1u << GRPC_COMPRESS_ALGORITHMS_COUNT) - 1; + opts->enabled_stream_compression_algorithms_bitset = + (1u << GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) - 1; +} + +void grpc_compression_options_enable_algorithm( + grpc_compression_options *opts, grpc_compression_algorithm algorithm) { + GPR_BITSET(&opts->enabled_algorithms_bitset, algorithm); +} + +void grpc_compression_options_disable_algorithm( + grpc_compression_options *opts, grpc_compression_algorithm algorithm) { + GPR_BITCLEAR(&opts->enabled_algorithms_bitset, algorithm); +} + +int grpc_compression_options_is_algorithm_enabled( + const grpc_compression_options *opts, + grpc_compression_algorithm algorithm) { + return GPR_BITGET(opts->enabled_algorithms_bitset, algorithm); +} + +int grpc_compression_options_is_stream_compression_algorithm_enabled( + const grpc_compression_options *opts, + grpc_stream_compression_algorithm algorithm) { + return GPR_BITGET(opts->enabled_stream_compression_algorithms_bitset, + algorithm); +} + +/* TODO(dgq): Add the ability to specify parameters to the individual + * compression algorithms */ +grpc_compression_algorithm grpc_compression_algorithm_for_level( + grpc_compression_level level, uint32_t accepted_encodings) { + GRPC_API_TRACE("grpc_compression_algorithm_for_level(level=%d)", 1, + ((int)level)); + if (level > GRPC_COMPRESS_LEVEL_HIGH) { + gpr_log(GPR_ERROR, "Unknown compression level %d.", (int)level); + abort(); + } + + const size_t num_supported = + GPR_BITCOUNT(accepted_encodings) - 1; /* discard NONE */ + if (level == GRPC_COMPRESS_LEVEL_NONE || num_supported == 0) { + return GRPC_COMPRESS_NONE; + } + + GPR_ASSERT(level > 0); + + /* Establish a "ranking" or compression algorithms in increasing order of + * compression. + * This is simplistic and we will probably want to introduce other dimensions + * in the future (cpu/memory cost, etc). */ + const grpc_compression_algorithm algos_ranking[] = {GRPC_COMPRESS_GZIP, + GRPC_COMPRESS_DEFLATE}; + + /* intersect algos_ranking with the supported ones keeping the ranked order */ + grpc_compression_algorithm + sorted_supported_algos[GRPC_COMPRESS_ALGORITHMS_COUNT]; + size_t algos_supported_idx = 0; + for (size_t i = 0; i < GPR_ARRAY_SIZE(algos_ranking); i++) { + const grpc_compression_algorithm alg = algos_ranking[i]; + for (size_t j = 0; j < num_supported; j++) { + if (GPR_BITGET(accepted_encodings, alg) == 1) { + /* if \a alg in supported */ + sorted_supported_algos[algos_supported_idx++] = alg; + break; + } + } + if (algos_supported_idx == num_supported) break; + } + + switch (level) { + case GRPC_COMPRESS_LEVEL_NONE: + abort(); /* should have been handled already */ + case GRPC_COMPRESS_LEVEL_LOW: + return sorted_supported_algos[0]; + case GRPC_COMPRESS_LEVEL_MED: + return sorted_supported_algos[num_supported / 2]; + case GRPC_COMPRESS_LEVEL_HIGH: + return sorted_supported_algos[num_supported - 1]; + default: + abort(); + }; +} + +GRPCAPI grpc_stream_compression_algorithm +grpc_stream_compression_algorithm_for_level( + grpc_stream_compression_level level, uint32_t accepted_stream_encodings) { + GRPC_API_TRACE("grpc_stream_compression_algorithm_for_level(level=%d)", 1, + ((int)level)); + if (level > GRPC_STREAM_COMPRESS_LEVEL_HIGH) { + gpr_log(GPR_ERROR, "Unknown compression level %d.", (int)level); + abort(); + } + + switch (level) { + case GRPC_STREAM_COMPRESS_LEVEL_NONE: + return GRPC_STREAM_COMPRESS_NONE; + case GRPC_STREAM_COMPRESS_LEVEL_LOW: + case GRPC_STREAM_COMPRESS_LEVEL_MED: + case GRPC_STREAM_COMPRESS_LEVEL_HIGH: + if (GPR_BITGET(accepted_stream_encodings, GRPC_STREAM_COMPRESS_GZIP) == + 1) { + return GRPC_STREAM_COMPRESS_GZIP; + } else { + return GRPC_STREAM_COMPRESS_NONE; + } + default: + abort(); + } +} diff --git a/src/core/lib/compression/message_compress.c b/src/core/lib/compression/message_compress.c deleted file mode 100644 index c051e28864..0000000000 --- a/src/core/lib/compression/message_compress.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/compression/message_compress.h" - -#include - -#include -#include - -#include - -#include "src/core/lib/slice/slice_internal.h" - -#define OUTPUT_BLOCK_SIZE 1024 - -static int zlib_body(grpc_exec_ctx* exec_ctx, z_stream* zs, - grpc_slice_buffer* input, grpc_slice_buffer* output, - int (*flate)(z_stream* zs, int flush)) { - int r; - int flush; - size_t i; - grpc_slice outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE); - const uInt uint_max = ~(uInt)0; - - GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max); - zs->avail_out = (uInt)GRPC_SLICE_LENGTH(outbuf); - zs->next_out = GRPC_SLICE_START_PTR(outbuf); - flush = Z_NO_FLUSH; - for (i = 0; i < input->count; i++) { - if (i == input->count - 1) flush = Z_FINISH; - GPR_ASSERT(GRPC_SLICE_LENGTH(input->slices[i]) <= uint_max); - zs->avail_in = (uInt)GRPC_SLICE_LENGTH(input->slices[i]); - zs->next_in = GRPC_SLICE_START_PTR(input->slices[i]); - do { - if (zs->avail_out == 0) { - grpc_slice_buffer_add_indexed(output, outbuf); - outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE); - GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max); - zs->avail_out = (uInt)GRPC_SLICE_LENGTH(outbuf); - zs->next_out = GRPC_SLICE_START_PTR(outbuf); - } - r = flate(zs, flush); - if (r < 0 && r != Z_BUF_ERROR /* not fatal */) { - gpr_log(GPR_INFO, "zlib error (%d)", r); - goto error; - } - } while (zs->avail_out == 0); - if (zs->avail_in) { - gpr_log(GPR_INFO, "zlib: not all input consumed"); - goto error; - } - } - - GPR_ASSERT(outbuf.refcount); - outbuf.data.refcounted.length -= zs->avail_out; - grpc_slice_buffer_add_indexed(output, outbuf); - - return 1; - -error: - grpc_slice_unref_internal(exec_ctx, outbuf); - return 0; -} - -static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) { - return gpr_malloc(items * size); -} - -static void zfree_gpr(void* opaque, void* address) { gpr_free(address); } - -static int zlib_compress(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* input, - grpc_slice_buffer* output, int gzip) { - z_stream zs; - int r; - size_t i; - size_t count_before = output->count; - size_t length_before = output->length; - memset(&zs, 0, sizeof(zs)); - zs.zalloc = zalloc_gpr; - zs.zfree = zfree_gpr; - r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0), - 8, Z_DEFAULT_STRATEGY); - GPR_ASSERT(r == Z_OK); - r = zlib_body(exec_ctx, &zs, input, output, deflate) && - output->length < input->length; - if (!r) { - for (i = count_before; i < output->count; i++) { - grpc_slice_unref_internal(exec_ctx, output->slices[i]); - } - output->count = count_before; - output->length = length_before; - } - deflateEnd(&zs); - return r; -} - -static int zlib_decompress(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* input, - grpc_slice_buffer* output, int gzip) { - z_stream zs; - int r; - size_t i; - size_t count_before = output->count; - size_t length_before = output->length; - memset(&zs, 0, sizeof(zs)); - zs.zalloc = zalloc_gpr; - zs.zfree = zfree_gpr; - r = inflateInit2(&zs, 15 | (gzip ? 16 : 0)); - GPR_ASSERT(r == Z_OK); - r = zlib_body(exec_ctx, &zs, input, output, inflate); - if (!r) { - for (i = count_before; i < output->count; i++) { - grpc_slice_unref_internal(exec_ctx, output->slices[i]); - } - output->count = count_before; - output->length = length_before; - } - inflateEnd(&zs); - return r; -} - -static int copy(grpc_slice_buffer* input, grpc_slice_buffer* output) { - size_t i; - for (i = 0; i < input->count; i++) { - grpc_slice_buffer_add(output, grpc_slice_ref_internal(input->slices[i])); - } - return 1; -} - -static int compress_inner(grpc_exec_ctx* exec_ctx, - grpc_compression_algorithm algorithm, - grpc_slice_buffer* input, grpc_slice_buffer* output) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - /* the fallback path always needs to be send uncompressed: we simply - rely on that here */ - return 0; - case GRPC_COMPRESS_DEFLATE: - return zlib_compress(exec_ctx, input, output, 0); - case GRPC_COMPRESS_GZIP: - return zlib_compress(exec_ctx, input, output, 1); - case GRPC_COMPRESS_ALGORITHMS_COUNT: - break; - } - gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); - return 0; -} - -int grpc_msg_compress(grpc_exec_ctx* exec_ctx, - grpc_compression_algorithm algorithm, - grpc_slice_buffer* input, grpc_slice_buffer* output) { - if (!compress_inner(exec_ctx, algorithm, input, output)) { - copy(input, output); - return 0; - } - return 1; -} - -int grpc_msg_decompress(grpc_exec_ctx* exec_ctx, - grpc_compression_algorithm algorithm, - grpc_slice_buffer* input, grpc_slice_buffer* output) { - switch (algorithm) { - case GRPC_COMPRESS_NONE: - return copy(input, output); - case GRPC_COMPRESS_DEFLATE: - return zlib_decompress(exec_ctx, input, output, 0); - case GRPC_COMPRESS_GZIP: - return zlib_decompress(exec_ctx, input, output, 1); - case GRPC_COMPRESS_ALGORITHMS_COUNT: - break; - } - gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); - return 0; -} diff --git a/src/core/lib/compression/message_compress.cc b/src/core/lib/compression/message_compress.cc new file mode 100644 index 0000000000..c051e28864 --- /dev/null +++ b/src/core/lib/compression/message_compress.cc @@ -0,0 +1,189 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/compression/message_compress.h" + +#include + +#include +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" + +#define OUTPUT_BLOCK_SIZE 1024 + +static int zlib_body(grpc_exec_ctx* exec_ctx, z_stream* zs, + grpc_slice_buffer* input, grpc_slice_buffer* output, + int (*flate)(z_stream* zs, int flush)) { + int r; + int flush; + size_t i; + grpc_slice outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE); + const uInt uint_max = ~(uInt)0; + + GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max); + zs->avail_out = (uInt)GRPC_SLICE_LENGTH(outbuf); + zs->next_out = GRPC_SLICE_START_PTR(outbuf); + flush = Z_NO_FLUSH; + for (i = 0; i < input->count; i++) { + if (i == input->count - 1) flush = Z_FINISH; + GPR_ASSERT(GRPC_SLICE_LENGTH(input->slices[i]) <= uint_max); + zs->avail_in = (uInt)GRPC_SLICE_LENGTH(input->slices[i]); + zs->next_in = GRPC_SLICE_START_PTR(input->slices[i]); + do { + if (zs->avail_out == 0) { + grpc_slice_buffer_add_indexed(output, outbuf); + outbuf = GRPC_SLICE_MALLOC(OUTPUT_BLOCK_SIZE); + GPR_ASSERT(GRPC_SLICE_LENGTH(outbuf) <= uint_max); + zs->avail_out = (uInt)GRPC_SLICE_LENGTH(outbuf); + zs->next_out = GRPC_SLICE_START_PTR(outbuf); + } + r = flate(zs, flush); + if (r < 0 && r != Z_BUF_ERROR /* not fatal */) { + gpr_log(GPR_INFO, "zlib error (%d)", r); + goto error; + } + } while (zs->avail_out == 0); + if (zs->avail_in) { + gpr_log(GPR_INFO, "zlib: not all input consumed"); + goto error; + } + } + + GPR_ASSERT(outbuf.refcount); + outbuf.data.refcounted.length -= zs->avail_out; + grpc_slice_buffer_add_indexed(output, outbuf); + + return 1; + +error: + grpc_slice_unref_internal(exec_ctx, outbuf); + return 0; +} + +static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) { + return gpr_malloc(items * size); +} + +static void zfree_gpr(void* opaque, void* address) { gpr_free(address); } + +static int zlib_compress(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* input, + grpc_slice_buffer* output, int gzip) { + z_stream zs; + int r; + size_t i; + size_t count_before = output->count; + size_t length_before = output->length; + memset(&zs, 0, sizeof(zs)); + zs.zalloc = zalloc_gpr; + zs.zfree = zfree_gpr; + r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0), + 8, Z_DEFAULT_STRATEGY); + GPR_ASSERT(r == Z_OK); + r = zlib_body(exec_ctx, &zs, input, output, deflate) && + output->length < input->length; + if (!r) { + for (i = count_before; i < output->count; i++) { + grpc_slice_unref_internal(exec_ctx, output->slices[i]); + } + output->count = count_before; + output->length = length_before; + } + deflateEnd(&zs); + return r; +} + +static int zlib_decompress(grpc_exec_ctx* exec_ctx, grpc_slice_buffer* input, + grpc_slice_buffer* output, int gzip) { + z_stream zs; + int r; + size_t i; + size_t count_before = output->count; + size_t length_before = output->length; + memset(&zs, 0, sizeof(zs)); + zs.zalloc = zalloc_gpr; + zs.zfree = zfree_gpr; + r = inflateInit2(&zs, 15 | (gzip ? 16 : 0)); + GPR_ASSERT(r == Z_OK); + r = zlib_body(exec_ctx, &zs, input, output, inflate); + if (!r) { + for (i = count_before; i < output->count; i++) { + grpc_slice_unref_internal(exec_ctx, output->slices[i]); + } + output->count = count_before; + output->length = length_before; + } + inflateEnd(&zs); + return r; +} + +static int copy(grpc_slice_buffer* input, grpc_slice_buffer* output) { + size_t i; + for (i = 0; i < input->count; i++) { + grpc_slice_buffer_add(output, grpc_slice_ref_internal(input->slices[i])); + } + return 1; +} + +static int compress_inner(grpc_exec_ctx* exec_ctx, + grpc_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + /* the fallback path always needs to be send uncompressed: we simply + rely on that here */ + return 0; + case GRPC_COMPRESS_DEFLATE: + return zlib_compress(exec_ctx, input, output, 0); + case GRPC_COMPRESS_GZIP: + return zlib_compress(exec_ctx, input, output, 1); + case GRPC_COMPRESS_ALGORITHMS_COUNT: + break; + } + gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); + return 0; +} + +int grpc_msg_compress(grpc_exec_ctx* exec_ctx, + grpc_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output) { + if (!compress_inner(exec_ctx, algorithm, input, output)) { + copy(input, output); + return 0; + } + return 1; +} + +int grpc_msg_decompress(grpc_exec_ctx* exec_ctx, + grpc_compression_algorithm algorithm, + grpc_slice_buffer* input, grpc_slice_buffer* output) { + switch (algorithm) { + case GRPC_COMPRESS_NONE: + return copy(input, output); + case GRPC_COMPRESS_DEFLATE: + return zlib_decompress(exec_ctx, input, output, 0); + case GRPC_COMPRESS_GZIP: + return zlib_decompress(exec_ctx, input, output, 1); + case GRPC_COMPRESS_ALGORITHMS_COUNT: + break; + } + gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm); + return 0; +} diff --git a/src/core/lib/compression/stream_compression.c b/src/core/lib/compression/stream_compression.c deleted file mode 100644 index 411489f029..0000000000 --- a/src/core/lib/compression/stream_compression.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/lib/compression/stream_compression.h" -#include "src/core/lib/compression/stream_compression_gzip.h" - -extern const grpc_stream_compression_vtable - grpc_stream_compression_identity_vtable; - -bool grpc_stream_compress(grpc_stream_compression_context *ctx, - grpc_slice_buffer *in, grpc_slice_buffer *out, - size_t *output_size, size_t max_output_size, - grpc_stream_compression_flush flush) { - return ctx->vtable->compress(ctx, in, out, output_size, max_output_size, - flush); -} - -bool grpc_stream_decompress(grpc_stream_compression_context *ctx, - grpc_slice_buffer *in, grpc_slice_buffer *out, - size_t *output_size, size_t max_output_size, - bool *end_of_context) { - return ctx->vtable->decompress(ctx, in, out, output_size, max_output_size, - end_of_context); -} - -grpc_stream_compression_context *grpc_stream_compression_context_create( - grpc_stream_compression_method method) { - switch (method) { - case GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS: - case GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS: - return grpc_stream_compression_identity_vtable.context_create(method); - case GRPC_STREAM_COMPRESSION_GZIP_COMPRESS: - case GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS: - return grpc_stream_compression_gzip_vtable.context_create(method); - default: - gpr_log(GPR_ERROR, "Unknown stream compression method: %d", method); - return NULL; - } -} - -void grpc_stream_compression_context_destroy( - grpc_stream_compression_context *ctx) { - ctx->vtable->context_destroy(ctx); -} - -int grpc_stream_compression_method_parse( - grpc_slice value, bool is_compress, - grpc_stream_compression_method *method) { - if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) { - *method = is_compress ? GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS - : GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; - return 1; - } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) { - *method = is_compress ? GRPC_STREAM_COMPRESSION_GZIP_COMPRESS - : GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS; - return 1; - } else { - return 0; - } -} diff --git a/src/core/lib/compression/stream_compression.cc b/src/core/lib/compression/stream_compression.cc new file mode 100644 index 0000000000..411489f029 --- /dev/null +++ b/src/core/lib/compression/stream_compression.cc @@ -0,0 +1,77 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/compression/stream_compression.h" +#include "src/core/lib/compression/stream_compression_gzip.h" + +extern const grpc_stream_compression_vtable + grpc_stream_compression_identity_vtable; + +bool grpc_stream_compress(grpc_stream_compression_context *ctx, + grpc_slice_buffer *in, grpc_slice_buffer *out, + size_t *output_size, size_t max_output_size, + grpc_stream_compression_flush flush) { + return ctx->vtable->compress(ctx, in, out, output_size, max_output_size, + flush); +} + +bool grpc_stream_decompress(grpc_stream_compression_context *ctx, + grpc_slice_buffer *in, grpc_slice_buffer *out, + size_t *output_size, size_t max_output_size, + bool *end_of_context) { + return ctx->vtable->decompress(ctx, in, out, output_size, max_output_size, + end_of_context); +} + +grpc_stream_compression_context *grpc_stream_compression_context_create( + grpc_stream_compression_method method) { + switch (method) { + case GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS: + case GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS: + return grpc_stream_compression_identity_vtable.context_create(method); + case GRPC_STREAM_COMPRESSION_GZIP_COMPRESS: + case GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS: + return grpc_stream_compression_gzip_vtable.context_create(method); + default: + gpr_log(GPR_ERROR, "Unknown stream compression method: %d", method); + return NULL; + } +} + +void grpc_stream_compression_context_destroy( + grpc_stream_compression_context *ctx) { + ctx->vtable->context_destroy(ctx); +} + +int grpc_stream_compression_method_parse( + grpc_slice value, bool is_compress, + grpc_stream_compression_method *method) { + if (grpc_slice_eq(value, GRPC_MDSTR_IDENTITY)) { + *method = is_compress ? GRPC_STREAM_COMPRESSION_IDENTITY_COMPRESS + : GRPC_STREAM_COMPRESSION_IDENTITY_DECOMPRESS; + return 1; + } else if (grpc_slice_eq(value, GRPC_MDSTR_GZIP)) { + *method = is_compress ? GRPC_STREAM_COMPRESSION_GZIP_COMPRESS + : GRPC_STREAM_COMPRESSION_GZIP_DECOMPRESS; + return 1; + } else { + return 0; + } +} diff --git a/src/core/lib/debug/stats.c b/src/core/lib/debug/stats.c deleted file mode 100644 index 4096384dd9..0000000000 --- a/src/core/lib/debug/stats.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/debug/stats.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/support/string.h" - -grpc_stats_data *grpc_stats_per_cpu_storage = NULL; -static size_t g_num_cores; - -void grpc_stats_init(void) { - g_num_cores = GPR_MAX(1, gpr_cpu_num_cores()); - grpc_stats_per_cpu_storage = - (grpc_stats_data *)gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores); -} - -void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); } - -void grpc_stats_collect(grpc_stats_data *output) { - memset(output, 0, sizeof(*output)); - for (size_t core = 0; core < g_num_cores; core++) { - for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { - output->counters[i] += gpr_atm_no_barrier_load( - &grpc_stats_per_cpu_storage[core].counters[i]); - } - for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { - output->histograms[i] += gpr_atm_no_barrier_load( - &grpc_stats_per_cpu_storage[core].histograms[i]); - } - } -} - -void grpc_stats_diff(const grpc_stats_data *b, const grpc_stats_data *a, - grpc_stats_data *c) { - for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { - c->counters[i] = b->counters[i] - a->counters[i]; - } - for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { - c->histograms[i] = b->histograms[i] - a->histograms[i]; - } -} - -int grpc_stats_histo_find_bucket_slow(grpc_exec_ctx *exec_ctx, int value, - const int *table, int table_size) { - GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS(exec_ctx); - const int *const start = table; - while (table_size > 0) { - int step = table_size / 2; - const int *it = table + step; - if (value >= *it) { - table = it + 1; - table_size -= step + 1; - } else { - table_size = step; - } - } - return (int)(table - start) - 1; -} - -size_t grpc_stats_histo_count(const grpc_stats_data *stats, - grpc_stats_histograms histogram) { - size_t sum = 0; - for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) { - sum += (size_t)stats->histograms[grpc_stats_histo_start[histogram] + i]; - } - return sum; -} - -static double threshold_for_count_below(const gpr_atm *bucket_counts, - const int *bucket_boundaries, - int num_buckets, double count_below) { - double count_so_far; - double lower_bound; - double upper_bound; - int lower_idx; - int upper_idx; - - /* find the lowest bucket that gets us above count_below */ - count_so_far = 0.0; - for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) { - count_so_far += (double)bucket_counts[lower_idx]; - if (count_so_far >= count_below) { - break; - } - } - if (count_so_far == count_below) { - /* this bucket hits the threshold exactly... we should be midway through - any run of zero values following the bucket */ - for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) { - if (bucket_counts[upper_idx]) { - break; - } - } - return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0; - } else { - /* treat values as uniform throughout the bucket, and find where this value - should lie */ - lower_bound = bucket_boundaries[lower_idx]; - upper_bound = bucket_boundaries[lower_idx + 1]; - return upper_bound - - (upper_bound - lower_bound) * (count_so_far - count_below) / - (double)bucket_counts[lower_idx]; - } -} - -double grpc_stats_histo_percentile(const grpc_stats_data *stats, - grpc_stats_histograms histogram, - double percentile) { - size_t count = grpc_stats_histo_count(stats, histogram); - if (count == 0) return 0.0; - return threshold_for_count_below( - stats->histograms + grpc_stats_histo_start[histogram], - grpc_stats_histo_bucket_boundaries[histogram], - grpc_stats_histo_buckets[histogram], (double)count * percentile / 100.0); -} - -char *grpc_stats_data_as_json(const grpc_stats_data *data) { - gpr_strvec v; - char *tmp; - bool is_first = true; - gpr_strvec_init(&v); - gpr_strvec_add(&v, gpr_strdup("{")); - for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { - gpr_asprintf(&tmp, "%s\"%s\": %" PRIdPTR, is_first ? "" : ", ", - grpc_stats_counter_name[i], data->counters[i]); - gpr_strvec_add(&v, tmp); - is_first = false; - } - for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { - gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ", - grpc_stats_histogram_name[i]); - gpr_strvec_add(&v, tmp); - for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { - gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",", - data->histograms[grpc_stats_histo_start[i] + j]); - gpr_strvec_add(&v, tmp); - } - gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]); - gpr_strvec_add(&v, tmp); - for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { - gpr_asprintf(&tmp, "%s%d", j == 0 ? "" : ",", - grpc_stats_histo_bucket_boundaries[i][j]); - gpr_strvec_add(&v, tmp); - } - gpr_strvec_add(&v, gpr_strdup("]")); - is_first = false; - } - gpr_strvec_add(&v, gpr_strdup("}")); - tmp = gpr_strvec_flatten(&v, NULL); - gpr_strvec_destroy(&v); - return tmp; -} diff --git a/src/core/lib/debug/stats.cc b/src/core/lib/debug/stats.cc new file mode 100644 index 0000000000..4096384dd9 --- /dev/null +++ b/src/core/lib/debug/stats.cc @@ -0,0 +1,174 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/debug/stats.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/support/string.h" + +grpc_stats_data *grpc_stats_per_cpu_storage = NULL; +static size_t g_num_cores; + +void grpc_stats_init(void) { + g_num_cores = GPR_MAX(1, gpr_cpu_num_cores()); + grpc_stats_per_cpu_storage = + (grpc_stats_data *)gpr_zalloc(sizeof(grpc_stats_data) * g_num_cores); +} + +void grpc_stats_shutdown(void) { gpr_free(grpc_stats_per_cpu_storage); } + +void grpc_stats_collect(grpc_stats_data *output) { + memset(output, 0, sizeof(*output)); + for (size_t core = 0; core < g_num_cores; core++) { + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + output->counters[i] += gpr_atm_no_barrier_load( + &grpc_stats_per_cpu_storage[core].counters[i]); + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { + output->histograms[i] += gpr_atm_no_barrier_load( + &grpc_stats_per_cpu_storage[core].histograms[i]); + } + } +} + +void grpc_stats_diff(const grpc_stats_data *b, const grpc_stats_data *a, + grpc_stats_data *c) { + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + c->counters[i] = b->counters[i] - a->counters[i]; + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_BUCKETS; i++) { + c->histograms[i] = b->histograms[i] - a->histograms[i]; + } +} + +int grpc_stats_histo_find_bucket_slow(grpc_exec_ctx *exec_ctx, int value, + const int *table, int table_size) { + GRPC_STATS_INC_HISTOGRAM_SLOW_LOOKUPS(exec_ctx); + const int *const start = table; + while (table_size > 0) { + int step = table_size / 2; + const int *it = table + step; + if (value >= *it) { + table = it + 1; + table_size -= step + 1; + } else { + table_size = step; + } + } + return (int)(table - start) - 1; +} + +size_t grpc_stats_histo_count(const grpc_stats_data *stats, + grpc_stats_histograms histogram) { + size_t sum = 0; + for (int i = 0; i < grpc_stats_histo_buckets[histogram]; i++) { + sum += (size_t)stats->histograms[grpc_stats_histo_start[histogram] + i]; + } + return sum; +} + +static double threshold_for_count_below(const gpr_atm *bucket_counts, + const int *bucket_boundaries, + int num_buckets, double count_below) { + double count_so_far; + double lower_bound; + double upper_bound; + int lower_idx; + int upper_idx; + + /* find the lowest bucket that gets us above count_below */ + count_so_far = 0.0; + for (lower_idx = 0; lower_idx < num_buckets; lower_idx++) { + count_so_far += (double)bucket_counts[lower_idx]; + if (count_so_far >= count_below) { + break; + } + } + if (count_so_far == count_below) { + /* this bucket hits the threshold exactly... we should be midway through + any run of zero values following the bucket */ + for (upper_idx = lower_idx + 1; upper_idx < num_buckets; upper_idx++) { + if (bucket_counts[upper_idx]) { + break; + } + } + return (bucket_boundaries[lower_idx] + bucket_boundaries[upper_idx]) / 2.0; + } else { + /* treat values as uniform throughout the bucket, and find where this value + should lie */ + lower_bound = bucket_boundaries[lower_idx]; + upper_bound = bucket_boundaries[lower_idx + 1]; + return upper_bound - + (upper_bound - lower_bound) * (count_so_far - count_below) / + (double)bucket_counts[lower_idx]; + } +} + +double grpc_stats_histo_percentile(const grpc_stats_data *stats, + grpc_stats_histograms histogram, + double percentile) { + size_t count = grpc_stats_histo_count(stats, histogram); + if (count == 0) return 0.0; + return threshold_for_count_below( + stats->histograms + grpc_stats_histo_start[histogram], + grpc_stats_histo_bucket_boundaries[histogram], + grpc_stats_histo_buckets[histogram], (double)count * percentile / 100.0); +} + +char *grpc_stats_data_as_json(const grpc_stats_data *data) { + gpr_strvec v; + char *tmp; + bool is_first = true; + gpr_strvec_init(&v); + gpr_strvec_add(&v, gpr_strdup("{")); + for (size_t i = 0; i < GRPC_STATS_COUNTER_COUNT; i++) { + gpr_asprintf(&tmp, "%s\"%s\": %" PRIdPTR, is_first ? "" : ", ", + grpc_stats_counter_name[i], data->counters[i]); + gpr_strvec_add(&v, tmp); + is_first = false; + } + for (size_t i = 0; i < GRPC_STATS_HISTOGRAM_COUNT; i++) { + gpr_asprintf(&tmp, "%s\"%s\": [", is_first ? "" : ", ", + grpc_stats_histogram_name[i]); + gpr_strvec_add(&v, tmp); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + gpr_asprintf(&tmp, "%s%" PRIdPTR, j == 0 ? "" : ",", + data->histograms[grpc_stats_histo_start[i] + j]); + gpr_strvec_add(&v, tmp); + } + gpr_asprintf(&tmp, "], \"%s_bkt\": [", grpc_stats_histogram_name[i]); + gpr_strvec_add(&v, tmp); + for (int j = 0; j < grpc_stats_histo_buckets[i]; j++) { + gpr_asprintf(&tmp, "%s%d", j == 0 ? "" : ",", + grpc_stats_histo_bucket_boundaries[i][j]); + gpr_strvec_add(&v, tmp); + } + gpr_strvec_add(&v, gpr_strdup("]")); + is_first = false; + } + gpr_strvec_add(&v, gpr_strdup("}")); + tmp = gpr_strvec_flatten(&v, NULL); + gpr_strvec_destroy(&v); + return tmp; +} diff --git a/src/core/lib/debug/stats_data.c b/src/core/lib/debug/stats_data.c deleted file mode 100644 index c0aec63c1d..0000000000 --- a/src/core/lib/debug/stats_data.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * Automatically generated by tools/codegen/core/gen_stats_data.py - */ - -#include "src/core/lib/debug/stats_data.h" -#include -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/exec_ctx.h" -const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = { - "client_calls_created", - "server_calls_created", - "cqs_created", - "client_channels_created", - "client_subchannels_created", - "server_channels_created", - "syscall_poll", - "syscall_wait", - "pollset_kick", - "pollset_kicked_without_poller", - "pollset_kicked_again", - "pollset_kick_wakeup_fd", - "pollset_kick_wakeup_cv", - "pollset_kick_own_thread", - "histogram_slow_lookups", - "syscall_write", - "syscall_read", - "tcp_backup_pollers_created", - "tcp_backup_poller_polls", - "http2_op_batches", - "http2_op_cancel", - "http2_op_send_initial_metadata", - "http2_op_send_message", - "http2_op_send_trailing_metadata", - "http2_op_recv_initial_metadata", - "http2_op_recv_message", - "http2_op_recv_trailing_metadata", - "http2_settings_writes", - "http2_pings_sent", - "http2_writes_begun", - "http2_writes_offloaded", - "http2_writes_continued", - "http2_partial_writes", - "http2_initiate_write_due_to_initial_write", - "http2_initiate_write_due_to_start_new_stream", - "http2_initiate_write_due_to_send_message", - "http2_initiate_write_due_to_send_initial_metadata", - "http2_initiate_write_due_to_send_trailing_metadata", - "http2_initiate_write_due_to_retry_send_ping", - "http2_initiate_write_due_to_continue_pings", - "http2_initiate_write_due_to_goaway_sent", - "http2_initiate_write_due_to_rst_stream", - "http2_initiate_write_due_to_close_from_api", - "http2_initiate_write_due_to_stream_flow_control", - "http2_initiate_write_due_to_transport_flow_control", - "http2_initiate_write_due_to_send_settings", - "http2_initiate_write_due_to_bdp_estimator_ping", - "http2_initiate_write_due_to_flow_control_unstalled_by_setting", - "http2_initiate_write_due_to_flow_control_unstalled_by_update", - "http2_initiate_write_due_to_application_ping", - "http2_initiate_write_due_to_keepalive_ping", - "http2_initiate_write_due_to_transport_flow_control_unstalled", - "http2_initiate_write_due_to_ping_response", - "http2_initiate_write_due_to_force_rst_stream", - "hpack_recv_indexed", - "hpack_recv_lithdr_incidx", - "hpack_recv_lithdr_incidx_v", - "hpack_recv_lithdr_notidx", - "hpack_recv_lithdr_notidx_v", - "hpack_recv_lithdr_nvridx", - "hpack_recv_lithdr_nvridx_v", - "hpack_recv_uncompressed", - "hpack_recv_huffman", - "hpack_recv_binary", - "hpack_recv_binary_base64", - "hpack_send_indexed", - "hpack_send_lithdr_incidx", - "hpack_send_lithdr_incidx_v", - "hpack_send_lithdr_notidx", - "hpack_send_lithdr_notidx_v", - "hpack_send_lithdr_nvridx", - "hpack_send_lithdr_nvridx_v", - "hpack_send_uncompressed", - "hpack_send_huffman", - "hpack_send_binary", - "hpack_send_binary_base64", - "combiner_locks_initiated", - "combiner_locks_scheduled_items", - "combiner_locks_scheduled_final_items", - "combiner_locks_offloaded", - "executor_scheduled_short_items", - "executor_scheduled_long_items", - "executor_scheduled_to_self", - "executor_wakeup_initiated", - "executor_queue_drained", - "executor_push_retries", - "server_requested_calls", - "server_slowpath_requests_queued", -}; -const char *grpc_stats_counter_doc[GRPC_STATS_COUNTER_COUNT] = { - "Number of client side calls created by this process", - "Number of server side calls created by this process", - "Number of completion queues created", "Number of client channels created", - "Number of client subchannels created", "Number of server channels created", - "Number of polling syscalls (epoll_wait, poll, etc) made by this process", - "Number of sleeping syscalls made by this process", - "How many polling wakeups were performed by the process (only valid for " - "epoll1 right now)", - "How many times was a polling wakeup requested without an active poller " - "(only valid for epoll1 right now)", - "How many times was the same polling worker awoken repeatedly before " - "waking up (only valid for epoll1 right now)", - "How many times was an eventfd used as the wakeup vector for a polling " - "wakeup (only valid for epoll1 right now)", - "How many times was a condition variable used as the wakeup vector for a " - "polling wakeup (only valid for epoll1 right now)", - "How many times could a polling wakeup be satisfied by keeping the waking " - "thread awake? (only valid for epoll1 right now)", - "Number of times histogram increments went through the slow (binary " - "search) path", - "Number of write syscalls (or equivalent - eg sendmsg) made by this " - "process", - "Number of read syscalls (or equivalent - eg recvmsg) made by this process", - "Number of times a backup poller has been created (this can be expensive)", - "Number of polls performed on the backup poller", - "Number of batches received by HTTP2 transport", - "Number of cancelations received by HTTP2 transport", - "Number of batches containing send initial metadata", - "Number of batches containing send message", - "Number of batches containing send trailing metadata", - "Number of batches containing receive initial metadata", - "Number of batches containing receive message", - "Number of batches containing receive trailing metadata", - "Number of settings frames sent", "Number of HTTP2 pings sent by process", - "Number of HTTP2 writes initiated", - "Number of HTTP2 writes offloaded to the executor from application threads", - "Number of HTTP2 writes that finished seeing more data needed to be " - "written", - "Number of HTTP2 writes that were made knowing there was still more data " - "to be written (we cap maximum write size to syscall_write)", - "Number of HTTP2 writes initiated due to 'initial_write'", - "Number of HTTP2 writes initiated due to 'start_new_stream'", - "Number of HTTP2 writes initiated due to 'send_message'", - "Number of HTTP2 writes initiated due to 'send_initial_metadata'", - "Number of HTTP2 writes initiated due to 'send_trailing_metadata'", - "Number of HTTP2 writes initiated due to 'retry_send_ping'", - "Number of HTTP2 writes initiated due to 'continue_pings'", - "Number of HTTP2 writes initiated due to 'goaway_sent'", - "Number of HTTP2 writes initiated due to 'rst_stream'", - "Number of HTTP2 writes initiated due to 'close_from_api'", - "Number of HTTP2 writes initiated due to 'stream_flow_control'", - "Number of HTTP2 writes initiated due to 'transport_flow_control'", - "Number of HTTP2 writes initiated due to 'send_settings'", - "Number of HTTP2 writes initiated due to 'bdp_estimator_ping'", - "Number of HTTP2 writes initiated due to " - "'flow_control_unstalled_by_setting'", - "Number of HTTP2 writes initiated due to " - "'flow_control_unstalled_by_update'", - "Number of HTTP2 writes initiated due to 'application_ping'", - "Number of HTTP2 writes initiated due to 'keepalive_ping'", - "Number of HTTP2 writes initiated due to " - "'transport_flow_control_unstalled'", - "Number of HTTP2 writes initiated due to 'ping_response'", - "Number of HTTP2 writes initiated due to 'force_rst_stream'", - "Number of HPACK indexed fields received", - "Number of HPACK literal headers received with incremental indexing", - "Number of HPACK literal headers received with incremental indexing and " - "literal keys", - "Number of HPACK literal headers received with no indexing", - "Number of HPACK literal headers received with no indexing and literal " - "keys", - "Number of HPACK literal headers received with never-indexing", - "Number of HPACK literal headers received with never-indexing and literal " - "keys", - "Number of uncompressed strings received in metadata", - "Number of huffman encoded strings received in metadata", - "Number of binary strings received in metadata", - "Number of binary strings received encoded in base64 in metadata", - "Number of HPACK indexed fields sent", - "Number of HPACK literal headers sent with incremental indexing", - "Number of HPACK literal headers sent with incremental indexing and " - "literal keys", - "Number of HPACK literal headers sent with no indexing", - "Number of HPACK literal headers sent with no indexing and literal keys", - "Number of HPACK literal headers sent with never-indexing", - "Number of HPACK literal headers sent with never-indexing and literal keys", - "Number of uncompressed strings sent in metadata", - "Number of huffman encoded strings sent in metadata", - "Number of binary strings received in metadata", - "Number of binary strings received encoded in base64 in metadata", - "Number of combiner lock entries by process (first items queued to a " - "combiner)", - "Number of items scheduled against combiner locks", - "Number of final items scheduled against combiner locks", - "Number of combiner locks offloaded to different threads", - "Number of finite runtime closures scheduled against the executor (gRPC " - "thread pool)", - "Number of potentially infinite runtime closures scheduled against the " - "executor (gRPC thread pool)", - "Number of closures scheduled by the executor to the executor", - "Number of thread wakeups initiated within the executor", - "Number of times an executor queue was drained", - "Number of times we raced and were forced to retry pushing a closure to " - "the executor", - "How many calls were requested (not necessarily received) by the server", - "How many times was the server slow path taken (indicates too few " - "outstanding requests)", -}; -const char *grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT] = { - "call_initial_size", - "poll_events_returned", - "tcp_write_size", - "tcp_write_iov_size", - "tcp_read_size", - "tcp_read_offer", - "tcp_read_offer_iov_size", - "http2_send_message_size", - "http2_send_initial_metadata_per_write", - "http2_send_message_per_write", - "http2_send_trailing_metadata_per_write", - "http2_send_flowctl_per_write", - "server_cqs_checked", -}; -const char *grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT] = { - "Initial size of the grpc_call arena created at call start", - "How many events are called for each syscall_poll", - "Number of bytes offered to each syscall_write", - "Number of byte segments offered to each syscall_write", - "Number of bytes received by each syscall_read", - "Number of bytes offered to each syscall_read", - "Number of byte segments offered to each syscall_read", - "Size of messages received by HTTP2 transport", - "Number of streams initiated written per TCP write", - "Number of streams whose payload was written per TCP write", - "Number of streams terminated per TCP write", - "Number of flow control updates written per TCP write", - "How many completion queues were checked looking for a CQ that had " - "requested the incoming call", -}; -const int grpc_stats_table_0[65] = { - 0, 1, 2, 3, 4, 5, 7, 9, 11, 14, - 17, 21, 26, 32, 39, 47, 57, 68, 82, 98, - 117, 140, 167, 199, 238, 284, 339, 404, 482, 575, - 685, 816, 972, 1158, 1380, 1644, 1959, 2334, 2780, 3312, - 3945, 4699, 5597, 6667, 7941, 9459, 11267, 13420, 15984, 19038, - 22676, 27009, 32169, 38315, 45635, 54353, 64737, 77104, 91834, 109378, - 130273, 155159, 184799, 220100, 262144}; -const uint8_t grpc_stats_table_1[124] = { - 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, - 7, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, - 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24, - 24, 25, 25, 26, 26, 26, 27, 27, 28, 29, 29, 30, 30, 30, 31, 31, 32, 33, - 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, - 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, - 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58}; -const int grpc_stats_table_2[129] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, - 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, - 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 94, 98, 102, 106, 110, - 114, 118, 122, 126, 131, 136, 141, 146, 151, 156, 162, 168, 174, 180, 186, - 192, 199, 206, 213, 220, 228, 236, 244, 252, 260, 269, 278, 287, 297, 307, - 317, 327, 338, 349, 360, 372, 384, 396, 409, 422, 436, 450, 464, 479, 494, - 510, 526, 543, 560, 578, 596, 615, 634, 654, 674, 695, 717, 739, 762, 785, - 809, 834, 859, 885, 912, 939, 967, 996, 1024}; -const uint8_t grpc_stats_table_3[166] = { - 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, - 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, - 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 26, 27, 27, 28, - 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, 38, 39, - 40, 40, 41, 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, - 51, 52, 52, 53, 53, 54, 54, 55, 56, 57, 58, 59, 59, 60, 61, 62, 63, 63, 64, - 65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, 73, 73, 74, 75, 75, 76, - 76, 77, 78, 79, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 88, 89, 90, 90, - 91, 92, 92, 93, 94, 94, 95, 95, 96, 97, 97, 98, 98, 99}; -const int grpc_stats_table_4[65] = { - 0, 1, 2, 3, 4, 6, 8, 11, - 15, 20, 26, 34, 44, 57, 73, 94, - 121, 155, 199, 255, 327, 419, 537, 688, - 881, 1128, 1444, 1848, 2365, 3026, 3872, 4954, - 6338, 8108, 10373, 13270, 16976, 21717, 27782, 35541, - 45467, 58165, 74409, 95189, 121772, 155778, 199281, 254933, - 326126, 417200, 533707, 682750, 873414, 1117323, 1429345, 1828502, - 2339127, 2992348, 3827987, 4896985, 6264509, 8013925, 10251880, 13114801, - 16777216}; -const uint8_t grpc_stats_table_5[87] = { - 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, - 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, - 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, - 36, 37, 38, 39, 39, 40, 41, 41, 42, 43, 44, 44, 45, 45, 46, 47, 48, 48, - 49, 50, 51, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 58, 59}; -const int grpc_stats_table_6[65] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 14, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, - 51, 56, 61, 66, 72, 78, 85, 92, 100, 109, 118, 128, 139, - 151, 164, 178, 193, 209, 226, 244, 264, 285, 308, 333, 359, 387, - 418, 451, 486, 524, 565, 609, 656, 707, 762, 821, 884, 952, 1024}; -const uint8_t grpc_stats_table_7[102] = { - 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, - 6, 7, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 12, 13, 13, 14, 14, - 14, 15, 15, 16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, - 23, 24, 24, 24, 25, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, - 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, - 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, 51}; -const int grpc_stats_table_8[9] = {0, 1, 2, 4, 7, 13, 23, 39, 64}; -const uint8_t grpc_stats_table_9[9] = {0, 0, 1, 2, 2, 3, 4, 4, 5}; -void grpc_stats_inc_call_initial_size(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 262144); - if (value < 6) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, - value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4651092515166879744ull) { - int bucket = - grpc_stats_table_1[((_val.uint - 4618441417868443648ull) >> 49)] + 6; - _bkt.dbl = grpc_stats_table_0[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, - bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_0, 64)); -} -void grpc_stats_inc_poll_events_returned(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 29) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4642789003353915392ull) { - int bucket = - grpc_stats_table_3[((_val.uint - 4628855992006737920ull) >> 47)] + 29; - _bkt.dbl = grpc_stats_table_2[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_2, 128)); -} -void grpc_stats_inc_tcp_write_size(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 16777216); - if (value < 5) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, - value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4683743612465315840ull) { - int bucket = - grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; - _bkt.dbl = grpc_stats_table_4[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, - bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_4, 64)); -} -void grpc_stats_inc_tcp_write_iov_size(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 13) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4637863191261478912ull) { - int bucket = - grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; - _bkt.dbl = grpc_stats_table_6[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_6, 64)); -} -void grpc_stats_inc_tcp_read_size(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 16777216); - if (value < 5) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, - value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4683743612465315840ull) { - int bucket = - grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; - _bkt.dbl = grpc_stats_table_4[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, - bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_4, 64)); -} -void grpc_stats_inc_tcp_read_offer(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 16777216); - if (value < 5) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, - value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4683743612465315840ull) { - int bucket = - grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; - _bkt.dbl = grpc_stats_table_4[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, - bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_4, 64)); -} -void grpc_stats_inc_tcp_read_offer_iov_size(grpc_exec_ctx *exec_ctx, - int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 13) { - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4637863191261478912ull) { - int bucket = - grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; - _bkt.dbl = grpc_stats_table_6[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_6, 64)); -} -void grpc_stats_inc_http2_send_message_size(grpc_exec_ctx *exec_ctx, - int value) { - value = GPR_CLAMP(value, 0, 16777216); - if (value < 5) { - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4683743612465315840ull) { - int bucket = - grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; - _bkt.dbl = grpc_stats_table_4[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_4, 64)); -} -void grpc_stats_inc_http2_send_initial_metadata_per_write( - grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 13) { - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, - value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4637863191261478912ull) { - int bucket = - grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; - _bkt.dbl = grpc_stats_table_6[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, - bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, - grpc_stats_histo_find_bucket_slow((exec_ctx), value, grpc_stats_table_6, - 64)); -} -void grpc_stats_inc_http2_send_message_per_write(grpc_exec_ctx *exec_ctx, - int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 13) { - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4637863191261478912ull) { - int bucket = - grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; - _bkt.dbl = grpc_stats_table_6[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_6, 64)); -} -void grpc_stats_inc_http2_send_trailing_metadata_per_write( - grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 13) { - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, - value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4637863191261478912ull) { - int bucket = - grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; - _bkt.dbl = grpc_stats_table_6[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, - bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, - grpc_stats_histo_find_bucket_slow((exec_ctx), value, grpc_stats_table_6, - 64)); -} -void grpc_stats_inc_http2_send_flowctl_per_write(grpc_exec_ctx *exec_ctx, - int value) { - value = GPR_CLAMP(value, 0, 1024); - if (value < 13) { - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4637863191261478912ull) { - int bucket = - grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; - _bkt.dbl = grpc_stats_table_6[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM( - (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_6, 64)); -} -void grpc_stats_inc_server_cqs_checked(grpc_exec_ctx *exec_ctx, int value) { - value = GPR_CLAMP(value, 0, 64); - if (value < 3) { - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, value); - return; - } - union { - double dbl; - uint64_t uint; - } _val, _bkt; - _val.dbl = value; - if (_val.uint < 4625196817309499392ull) { - int bucket = - grpc_stats_table_9[((_val.uint - 4613937818241073152ull) >> 51)] + 3; - _bkt.dbl = grpc_stats_table_8[bucket]; - bucket -= (_val.uint < _bkt.uint); - GRPC_STATS_INC_HISTOGRAM((exec_ctx), - GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, bucket); - return; - } - GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, - grpc_stats_histo_find_bucket_slow( - (exec_ctx), value, grpc_stats_table_8, 8)); -} -const int grpc_stats_histo_buckets[13] = {64, 128, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 8}; -const int grpc_stats_histo_start[13] = {0, 64, 192, 256, 320, 384, 448, - 512, 576, 640, 704, 768, 832}; -const int *const grpc_stats_histo_bucket_boundaries[13] = { - grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_4, - grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_4, - grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_6, - grpc_stats_table_6, grpc_stats_table_6, grpc_stats_table_6, - grpc_stats_table_8}; -void (*const grpc_stats_inc_histogram[13])(grpc_exec_ctx *exec_ctx, int x) = { - grpc_stats_inc_call_initial_size, - grpc_stats_inc_poll_events_returned, - grpc_stats_inc_tcp_write_size, - grpc_stats_inc_tcp_write_iov_size, - grpc_stats_inc_tcp_read_size, - grpc_stats_inc_tcp_read_offer, - grpc_stats_inc_tcp_read_offer_iov_size, - grpc_stats_inc_http2_send_message_size, - grpc_stats_inc_http2_send_initial_metadata_per_write, - grpc_stats_inc_http2_send_message_per_write, - grpc_stats_inc_http2_send_trailing_metadata_per_write, - grpc_stats_inc_http2_send_flowctl_per_write, - grpc_stats_inc_server_cqs_checked}; diff --git a/src/core/lib/debug/stats_data.cc b/src/core/lib/debug/stats_data.cc new file mode 100644 index 0000000000..c0aec63c1d --- /dev/null +++ b/src/core/lib/debug/stats_data.cc @@ -0,0 +1,687 @@ +/* + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Automatically generated by tools/codegen/core/gen_stats_data.py + */ + +#include "src/core/lib/debug/stats_data.h" +#include +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/exec_ctx.h" +const char *grpc_stats_counter_name[GRPC_STATS_COUNTER_COUNT] = { + "client_calls_created", + "server_calls_created", + "cqs_created", + "client_channels_created", + "client_subchannels_created", + "server_channels_created", + "syscall_poll", + "syscall_wait", + "pollset_kick", + "pollset_kicked_without_poller", + "pollset_kicked_again", + "pollset_kick_wakeup_fd", + "pollset_kick_wakeup_cv", + "pollset_kick_own_thread", + "histogram_slow_lookups", + "syscall_write", + "syscall_read", + "tcp_backup_pollers_created", + "tcp_backup_poller_polls", + "http2_op_batches", + "http2_op_cancel", + "http2_op_send_initial_metadata", + "http2_op_send_message", + "http2_op_send_trailing_metadata", + "http2_op_recv_initial_metadata", + "http2_op_recv_message", + "http2_op_recv_trailing_metadata", + "http2_settings_writes", + "http2_pings_sent", + "http2_writes_begun", + "http2_writes_offloaded", + "http2_writes_continued", + "http2_partial_writes", + "http2_initiate_write_due_to_initial_write", + "http2_initiate_write_due_to_start_new_stream", + "http2_initiate_write_due_to_send_message", + "http2_initiate_write_due_to_send_initial_metadata", + "http2_initiate_write_due_to_send_trailing_metadata", + "http2_initiate_write_due_to_retry_send_ping", + "http2_initiate_write_due_to_continue_pings", + "http2_initiate_write_due_to_goaway_sent", + "http2_initiate_write_due_to_rst_stream", + "http2_initiate_write_due_to_close_from_api", + "http2_initiate_write_due_to_stream_flow_control", + "http2_initiate_write_due_to_transport_flow_control", + "http2_initiate_write_due_to_send_settings", + "http2_initiate_write_due_to_bdp_estimator_ping", + "http2_initiate_write_due_to_flow_control_unstalled_by_setting", + "http2_initiate_write_due_to_flow_control_unstalled_by_update", + "http2_initiate_write_due_to_application_ping", + "http2_initiate_write_due_to_keepalive_ping", + "http2_initiate_write_due_to_transport_flow_control_unstalled", + "http2_initiate_write_due_to_ping_response", + "http2_initiate_write_due_to_force_rst_stream", + "hpack_recv_indexed", + "hpack_recv_lithdr_incidx", + "hpack_recv_lithdr_incidx_v", + "hpack_recv_lithdr_notidx", + "hpack_recv_lithdr_notidx_v", + "hpack_recv_lithdr_nvridx", + "hpack_recv_lithdr_nvridx_v", + "hpack_recv_uncompressed", + "hpack_recv_huffman", + "hpack_recv_binary", + "hpack_recv_binary_base64", + "hpack_send_indexed", + "hpack_send_lithdr_incidx", + "hpack_send_lithdr_incidx_v", + "hpack_send_lithdr_notidx", + "hpack_send_lithdr_notidx_v", + "hpack_send_lithdr_nvridx", + "hpack_send_lithdr_nvridx_v", + "hpack_send_uncompressed", + "hpack_send_huffman", + "hpack_send_binary", + "hpack_send_binary_base64", + "combiner_locks_initiated", + "combiner_locks_scheduled_items", + "combiner_locks_scheduled_final_items", + "combiner_locks_offloaded", + "executor_scheduled_short_items", + "executor_scheduled_long_items", + "executor_scheduled_to_self", + "executor_wakeup_initiated", + "executor_queue_drained", + "executor_push_retries", + "server_requested_calls", + "server_slowpath_requests_queued", +}; +const char *grpc_stats_counter_doc[GRPC_STATS_COUNTER_COUNT] = { + "Number of client side calls created by this process", + "Number of server side calls created by this process", + "Number of completion queues created", "Number of client channels created", + "Number of client subchannels created", "Number of server channels created", + "Number of polling syscalls (epoll_wait, poll, etc) made by this process", + "Number of sleeping syscalls made by this process", + "How many polling wakeups were performed by the process (only valid for " + "epoll1 right now)", + "How many times was a polling wakeup requested without an active poller " + "(only valid for epoll1 right now)", + "How many times was the same polling worker awoken repeatedly before " + "waking up (only valid for epoll1 right now)", + "How many times was an eventfd used as the wakeup vector for a polling " + "wakeup (only valid for epoll1 right now)", + "How many times was a condition variable used as the wakeup vector for a " + "polling wakeup (only valid for epoll1 right now)", + "How many times could a polling wakeup be satisfied by keeping the waking " + "thread awake? (only valid for epoll1 right now)", + "Number of times histogram increments went through the slow (binary " + "search) path", + "Number of write syscalls (or equivalent - eg sendmsg) made by this " + "process", + "Number of read syscalls (or equivalent - eg recvmsg) made by this process", + "Number of times a backup poller has been created (this can be expensive)", + "Number of polls performed on the backup poller", + "Number of batches received by HTTP2 transport", + "Number of cancelations received by HTTP2 transport", + "Number of batches containing send initial metadata", + "Number of batches containing send message", + "Number of batches containing send trailing metadata", + "Number of batches containing receive initial metadata", + "Number of batches containing receive message", + "Number of batches containing receive trailing metadata", + "Number of settings frames sent", "Number of HTTP2 pings sent by process", + "Number of HTTP2 writes initiated", + "Number of HTTP2 writes offloaded to the executor from application threads", + "Number of HTTP2 writes that finished seeing more data needed to be " + "written", + "Number of HTTP2 writes that were made knowing there was still more data " + "to be written (we cap maximum write size to syscall_write)", + "Number of HTTP2 writes initiated due to 'initial_write'", + "Number of HTTP2 writes initiated due to 'start_new_stream'", + "Number of HTTP2 writes initiated due to 'send_message'", + "Number of HTTP2 writes initiated due to 'send_initial_metadata'", + "Number of HTTP2 writes initiated due to 'send_trailing_metadata'", + "Number of HTTP2 writes initiated due to 'retry_send_ping'", + "Number of HTTP2 writes initiated due to 'continue_pings'", + "Number of HTTP2 writes initiated due to 'goaway_sent'", + "Number of HTTP2 writes initiated due to 'rst_stream'", + "Number of HTTP2 writes initiated due to 'close_from_api'", + "Number of HTTP2 writes initiated due to 'stream_flow_control'", + "Number of HTTP2 writes initiated due to 'transport_flow_control'", + "Number of HTTP2 writes initiated due to 'send_settings'", + "Number of HTTP2 writes initiated due to 'bdp_estimator_ping'", + "Number of HTTP2 writes initiated due to " + "'flow_control_unstalled_by_setting'", + "Number of HTTP2 writes initiated due to " + "'flow_control_unstalled_by_update'", + "Number of HTTP2 writes initiated due to 'application_ping'", + "Number of HTTP2 writes initiated due to 'keepalive_ping'", + "Number of HTTP2 writes initiated due to " + "'transport_flow_control_unstalled'", + "Number of HTTP2 writes initiated due to 'ping_response'", + "Number of HTTP2 writes initiated due to 'force_rst_stream'", + "Number of HPACK indexed fields received", + "Number of HPACK literal headers received with incremental indexing", + "Number of HPACK literal headers received with incremental indexing and " + "literal keys", + "Number of HPACK literal headers received with no indexing", + "Number of HPACK literal headers received with no indexing and literal " + "keys", + "Number of HPACK literal headers received with never-indexing", + "Number of HPACK literal headers received with never-indexing and literal " + "keys", + "Number of uncompressed strings received in metadata", + "Number of huffman encoded strings received in metadata", + "Number of binary strings received in metadata", + "Number of binary strings received encoded in base64 in metadata", + "Number of HPACK indexed fields sent", + "Number of HPACK literal headers sent with incremental indexing", + "Number of HPACK literal headers sent with incremental indexing and " + "literal keys", + "Number of HPACK literal headers sent with no indexing", + "Number of HPACK literal headers sent with no indexing and literal keys", + "Number of HPACK literal headers sent with never-indexing", + "Number of HPACK literal headers sent with never-indexing and literal keys", + "Number of uncompressed strings sent in metadata", + "Number of huffman encoded strings sent in metadata", + "Number of binary strings received in metadata", + "Number of binary strings received encoded in base64 in metadata", + "Number of combiner lock entries by process (first items queued to a " + "combiner)", + "Number of items scheduled against combiner locks", + "Number of final items scheduled against combiner locks", + "Number of combiner locks offloaded to different threads", + "Number of finite runtime closures scheduled against the executor (gRPC " + "thread pool)", + "Number of potentially infinite runtime closures scheduled against the " + "executor (gRPC thread pool)", + "Number of closures scheduled by the executor to the executor", + "Number of thread wakeups initiated within the executor", + "Number of times an executor queue was drained", + "Number of times we raced and were forced to retry pushing a closure to " + "the executor", + "How many calls were requested (not necessarily received) by the server", + "How many times was the server slow path taken (indicates too few " + "outstanding requests)", +}; +const char *grpc_stats_histogram_name[GRPC_STATS_HISTOGRAM_COUNT] = { + "call_initial_size", + "poll_events_returned", + "tcp_write_size", + "tcp_write_iov_size", + "tcp_read_size", + "tcp_read_offer", + "tcp_read_offer_iov_size", + "http2_send_message_size", + "http2_send_initial_metadata_per_write", + "http2_send_message_per_write", + "http2_send_trailing_metadata_per_write", + "http2_send_flowctl_per_write", + "server_cqs_checked", +}; +const char *grpc_stats_histogram_doc[GRPC_STATS_HISTOGRAM_COUNT] = { + "Initial size of the grpc_call arena created at call start", + "How many events are called for each syscall_poll", + "Number of bytes offered to each syscall_write", + "Number of byte segments offered to each syscall_write", + "Number of bytes received by each syscall_read", + "Number of bytes offered to each syscall_read", + "Number of byte segments offered to each syscall_read", + "Size of messages received by HTTP2 transport", + "Number of streams initiated written per TCP write", + "Number of streams whose payload was written per TCP write", + "Number of streams terminated per TCP write", + "Number of flow control updates written per TCP write", + "How many completion queues were checked looking for a CQ that had " + "requested the incoming call", +}; +const int grpc_stats_table_0[65] = { + 0, 1, 2, 3, 4, 5, 7, 9, 11, 14, + 17, 21, 26, 32, 39, 47, 57, 68, 82, 98, + 117, 140, 167, 199, 238, 284, 339, 404, 482, 575, + 685, 816, 972, 1158, 1380, 1644, 1959, 2334, 2780, 3312, + 3945, 4699, 5597, 6667, 7941, 9459, 11267, 13420, 15984, 19038, + 22676, 27009, 32169, 38315, 45635, 54353, 64737, 77104, 91834, 109378, + 130273, 155159, 184799, 220100, 262144}; +const uint8_t grpc_stats_table_1[124] = { + 0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, + 7, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, + 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 22, 23, 24, + 24, 25, 25, 26, 26, 26, 27, 27, 28, 29, 29, 30, 30, 30, 31, 31, 32, 33, + 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, + 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, + 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58}; +const int grpc_stats_table_2[129] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, + 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 94, 98, 102, 106, 110, + 114, 118, 122, 126, 131, 136, 141, 146, 151, 156, 162, 168, 174, 180, 186, + 192, 199, 206, 213, 220, 228, 236, 244, 252, 260, 269, 278, 287, 297, 307, + 317, 327, 338, 349, 360, 372, 384, 396, 409, 422, 436, 450, 464, 479, 494, + 510, 526, 543, 560, 578, 596, 615, 634, 654, 674, 695, 717, 739, 762, 785, + 809, 834, 859, 885, 912, 939, 967, 996, 1024}; +const uint8_t grpc_stats_table_3[166] = { + 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, + 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, 38, 39, + 40, 40, 41, 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, + 51, 52, 52, 53, 53, 54, 54, 55, 56, 57, 58, 59, 59, 60, 61, 62, 63, 63, 64, + 65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, 73, 73, 74, 75, 75, 76, + 76, 77, 78, 79, 79, 80, 81, 82, 83, 84, 85, 85, 86, 87, 88, 88, 89, 90, 90, + 91, 92, 92, 93, 94, 94, 95, 95, 96, 97, 97, 98, 98, 99}; +const int grpc_stats_table_4[65] = { + 0, 1, 2, 3, 4, 6, 8, 11, + 15, 20, 26, 34, 44, 57, 73, 94, + 121, 155, 199, 255, 327, 419, 537, 688, + 881, 1128, 1444, 1848, 2365, 3026, 3872, 4954, + 6338, 8108, 10373, 13270, 16976, 21717, 27782, 35541, + 45467, 58165, 74409, 95189, 121772, 155778, 199281, 254933, + 326126, 417200, 533707, 682750, 873414, 1117323, 1429345, 1828502, + 2339127, 2992348, 3827987, 4896985, 6264509, 8013925, 10251880, 13114801, + 16777216}; +const uint8_t grpc_stats_table_5[87] = { + 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, + 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 20, 20, 21, 22, 22, 23, + 24, 25, 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, 36, + 36, 37, 38, 39, 39, 40, 41, 41, 42, 43, 44, 44, 45, 45, 46, 47, 48, 48, + 49, 50, 51, 51, 52, 53, 53, 54, 55, 56, 56, 57, 58, 58, 59}; +const int grpc_stats_table_6[65] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 14, 16, 18, 20, 22, 24, 27, 30, 33, 36, 39, 43, 47, + 51, 56, 61, 66, 72, 78, 85, 92, 100, 109, 118, 128, 139, + 151, 164, 178, 193, 209, 226, 244, 264, 285, 308, 333, 359, 387, + 418, 451, 486, 524, 565, 609, 656, 707, 762, 821, 884, 952, 1024}; +const uint8_t grpc_stats_table_7[102] = { + 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 6, 7, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, 12, 13, 13, 14, 14, + 14, 15, 15, 16, 16, 17, 17, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 24, 25, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, + 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, + 42, 42, 43, 44, 44, 45, 46, 46, 47, 48, 48, 49, 49, 50, 50, 51, 51}; +const int grpc_stats_table_8[9] = {0, 1, 2, 4, 7, 13, 23, 39, 64}; +const uint8_t grpc_stats_table_9[9] = {0, 0, 1, 2, 2, 3, 4, 4, 5}; +void grpc_stats_inc_call_initial_size(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 262144); + if (value < 6) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4651092515166879744ull) { + int bucket = + grpc_stats_table_1[((_val.uint - 4618441417868443648ull) >> 49)] + 6; + _bkt.dbl = grpc_stats_table_0[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_CALL_INITIAL_SIZE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_0, 64)); +} +void grpc_stats_inc_poll_events_returned(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 29) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4642789003353915392ull) { + int bucket = + grpc_stats_table_3[((_val.uint - 4628855992006737920ull) >> 47)] + 29; + _bkt.dbl = grpc_stats_table_2[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_POLL_EVENTS_RETURNED, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_2, 128)); +} +void grpc_stats_inc_tcp_write_size(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_SIZE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_tcp_write_iov_size(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_WRITE_IOV_SIZE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_tcp_read_size(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_SIZE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_tcp_read_offer(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_tcp_read_offer_iov_size(grpc_exec_ctx *exec_ctx, + int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_TCP_READ_OFFER_IOV_SIZE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_http2_send_message_size(grpc_exec_ctx *exec_ctx, + int value) { + value = GPR_CLAMP(value, 0, 16777216); + if (value < 5) { + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4683743612465315840ull) { + int bucket = + grpc_stats_table_5[((_val.uint - 4617315517961601024ull) >> 50)] + 5; + _bkt.dbl = grpc_stats_table_4[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_SIZE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_4, 64)); +} +void grpc_stats_inc_http2_send_initial_metadata_per_write( + grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_INITIAL_METADATA_PER_WRITE, + grpc_stats_histo_find_bucket_slow((exec_ctx), value, grpc_stats_table_6, + 64)); +} +void grpc_stats_inc_http2_send_message_per_write(grpc_exec_ctx *exec_ctx, + int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_HTTP2_SEND_MESSAGE_PER_WRITE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_http2_send_trailing_metadata_per_write( + grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, + value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, + bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_TRAILING_METADATA_PER_WRITE, + grpc_stats_histo_find_bucket_slow((exec_ctx), value, grpc_stats_table_6, + 64)); +} +void grpc_stats_inc_http2_send_flowctl_per_write(grpc_exec_ctx *exec_ctx, + int value) { + value = GPR_CLAMP(value, 0, 1024); + if (value < 13) { + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4637863191261478912ull) { + int bucket = + grpc_stats_table_7[((_val.uint - 4623507967449235456ull) >> 48)] + 13; + _bkt.dbl = grpc_stats_table_6[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM( + (exec_ctx), GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_HTTP2_SEND_FLOWCTL_PER_WRITE, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_6, 64)); +} +void grpc_stats_inc_server_cqs_checked(grpc_exec_ctx *exec_ctx, int value) { + value = GPR_CLAMP(value, 0, 64); + if (value < 3) { + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, value); + return; + } + union { + double dbl; + uint64_t uint; + } _val, _bkt; + _val.dbl = value; + if (_val.uint < 4625196817309499392ull) { + int bucket = + grpc_stats_table_9[((_val.uint - 4613937818241073152ull) >> 51)] + 3; + _bkt.dbl = grpc_stats_table_8[bucket]; + bucket -= (_val.uint < _bkt.uint); + GRPC_STATS_INC_HISTOGRAM((exec_ctx), + GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, bucket); + return; + } + GRPC_STATS_INC_HISTOGRAM((exec_ctx), GRPC_STATS_HISTOGRAM_SERVER_CQS_CHECKED, + grpc_stats_histo_find_bucket_slow( + (exec_ctx), value, grpc_stats_table_8, 8)); +} +const int grpc_stats_histo_buckets[13] = {64, 128, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 8}; +const int grpc_stats_histo_start[13] = {0, 64, 192, 256, 320, 384, 448, + 512, 576, 640, 704, 768, 832}; +const int *const grpc_stats_histo_bucket_boundaries[13] = { + grpc_stats_table_0, grpc_stats_table_2, grpc_stats_table_4, + grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_4, + grpc_stats_table_6, grpc_stats_table_4, grpc_stats_table_6, + grpc_stats_table_6, grpc_stats_table_6, grpc_stats_table_6, + grpc_stats_table_8}; +void (*const grpc_stats_inc_histogram[13])(grpc_exec_ctx *exec_ctx, int x) = { + grpc_stats_inc_call_initial_size, + grpc_stats_inc_poll_events_returned, + grpc_stats_inc_tcp_write_size, + grpc_stats_inc_tcp_write_iov_size, + grpc_stats_inc_tcp_read_size, + grpc_stats_inc_tcp_read_offer, + grpc_stats_inc_tcp_read_offer_iov_size, + grpc_stats_inc_http2_send_message_size, + grpc_stats_inc_http2_send_initial_metadata_per_write, + grpc_stats_inc_http2_send_message_per_write, + grpc_stats_inc_http2_send_trailing_metadata_per_write, + grpc_stats_inc_http2_send_flowctl_per_write, + grpc_stats_inc_server_cqs_checked}; diff --git a/src/core/lib/debug/trace.c b/src/core/lib/debug/trace.c deleted file mode 100644 index 21b0d8c3a6..0000000000 --- a/src/core/lib/debug/trace.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/debug/trace.h" - -#include - -#include -#include -#include -#include "src/core/lib/support/env.h" - -int grpc_tracer_set_enabled(const char *name, int enabled); - -typedef struct tracer { - grpc_tracer_flag *flag; - struct tracer *next; -} tracer; -static tracer *tracers; - -#ifdef GRPC_THREADSAFE_TRACER -#define TRACER_SET(flag, on) gpr_atm_no_barrier_store(&(flag).value, (on)) -#else -#define TRACER_SET(flag, on) (flag).value = (on) -#endif - -void grpc_register_tracer(grpc_tracer_flag *flag) { - tracer *t = (tracer *)gpr_malloc(sizeof(*t)); - t->flag = flag; - t->next = tracers; - TRACER_SET(*flag, false); - tracers = t; -} - -static void add(const char *beg, const char *end, char ***ss, size_t *ns) { - size_t n = *ns; - size_t np = n + 1; - char *s; - size_t len; - GPR_ASSERT(end >= beg); - len = (size_t)(end - beg); - s = (char *)gpr_malloc(len + 1); - memcpy(s, beg, len); - s[len] = 0; - *ss = (char **)gpr_realloc(*ss, sizeof(char **) * np); - (*ss)[n] = s; - *ns = np; -} - -static void split(const char *s, char ***ss, size_t *ns) { - const char *c = strchr(s, ','); - if (c == NULL) { - add(s, s + strlen(s), ss, ns); - } else { - add(s, c, ss, ns); - split(c + 1, ss, ns); - } -} - -static void parse(const char *s) { - char **strings = NULL; - size_t nstrings = 0; - size_t i; - split(s, &strings, &nstrings); - - for (i = 0; i < nstrings; i++) { - if (strings[i][0] == '-') { - grpc_tracer_set_enabled(strings[i] + 1, 0); - } else { - grpc_tracer_set_enabled(strings[i], 1); - } - } - - for (i = 0; i < nstrings; i++) { - gpr_free(strings[i]); - } - gpr_free(strings); -} - -static void list_tracers() { - gpr_log(GPR_DEBUG, "available tracers:"); - tracer *t; - for (t = tracers; t; t = t->next) { - gpr_log(GPR_DEBUG, "\t%s", t->flag->name); - } -} - -void grpc_tracer_init(const char *env_var) { - char *e = gpr_getenv(env_var); - if (e != NULL) { - parse(e); - gpr_free(e); - } -} - -void grpc_tracer_shutdown(void) { - while (tracers) { - tracer *t = tracers; - tracers = t->next; - gpr_free(t); - } -} - -int grpc_tracer_set_enabled(const char *name, int enabled) { - tracer *t; - if (0 == strcmp(name, "all")) { - for (t = tracers; t; t = t->next) { - TRACER_SET(*t->flag, enabled); - } - } else if (0 == strcmp(name, "list_tracers")) { - list_tracers(); - } else if (0 == strcmp(name, "refcount")) { - for (t = tracers; t; t = t->next) { - if (strstr(t->flag->name, "refcount") != NULL) { - TRACER_SET(*t->flag, enabled); - } - } - } else { - int found = 0; - for (t = tracers; t; t = t->next) { - if (0 == strcmp(name, t->flag->name)) { - TRACER_SET(*t->flag, enabled); - found = 1; - } - } - if (!found) { - gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name); - return 0; /* early return */ - } - } - return 1; -} diff --git a/src/core/lib/debug/trace.cc b/src/core/lib/debug/trace.cc new file mode 100644 index 0000000000..21b0d8c3a6 --- /dev/null +++ b/src/core/lib/debug/trace.cc @@ -0,0 +1,147 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/debug/trace.h" + +#include + +#include +#include +#include +#include "src/core/lib/support/env.h" + +int grpc_tracer_set_enabled(const char *name, int enabled); + +typedef struct tracer { + grpc_tracer_flag *flag; + struct tracer *next; +} tracer; +static tracer *tracers; + +#ifdef GRPC_THREADSAFE_TRACER +#define TRACER_SET(flag, on) gpr_atm_no_barrier_store(&(flag).value, (on)) +#else +#define TRACER_SET(flag, on) (flag).value = (on) +#endif + +void grpc_register_tracer(grpc_tracer_flag *flag) { + tracer *t = (tracer *)gpr_malloc(sizeof(*t)); + t->flag = flag; + t->next = tracers; + TRACER_SET(*flag, false); + tracers = t; +} + +static void add(const char *beg, const char *end, char ***ss, size_t *ns) { + size_t n = *ns; + size_t np = n + 1; + char *s; + size_t len; + GPR_ASSERT(end >= beg); + len = (size_t)(end - beg); + s = (char *)gpr_malloc(len + 1); + memcpy(s, beg, len); + s[len] = 0; + *ss = (char **)gpr_realloc(*ss, sizeof(char **) * np); + (*ss)[n] = s; + *ns = np; +} + +static void split(const char *s, char ***ss, size_t *ns) { + const char *c = strchr(s, ','); + if (c == NULL) { + add(s, s + strlen(s), ss, ns); + } else { + add(s, c, ss, ns); + split(c + 1, ss, ns); + } +} + +static void parse(const char *s) { + char **strings = NULL; + size_t nstrings = 0; + size_t i; + split(s, &strings, &nstrings); + + for (i = 0; i < nstrings; i++) { + if (strings[i][0] == '-') { + grpc_tracer_set_enabled(strings[i] + 1, 0); + } else { + grpc_tracer_set_enabled(strings[i], 1); + } + } + + for (i = 0; i < nstrings; i++) { + gpr_free(strings[i]); + } + gpr_free(strings); +} + +static void list_tracers() { + gpr_log(GPR_DEBUG, "available tracers:"); + tracer *t; + for (t = tracers; t; t = t->next) { + gpr_log(GPR_DEBUG, "\t%s", t->flag->name); + } +} + +void grpc_tracer_init(const char *env_var) { + char *e = gpr_getenv(env_var); + if (e != NULL) { + parse(e); + gpr_free(e); + } +} + +void grpc_tracer_shutdown(void) { + while (tracers) { + tracer *t = tracers; + tracers = t->next; + gpr_free(t); + } +} + +int grpc_tracer_set_enabled(const char *name, int enabled) { + tracer *t; + if (0 == strcmp(name, "all")) { + for (t = tracers; t; t = t->next) { + TRACER_SET(*t->flag, enabled); + } + } else if (0 == strcmp(name, "list_tracers")) { + list_tracers(); + } else if (0 == strcmp(name, "refcount")) { + for (t = tracers; t; t = t->next) { + if (strstr(t->flag->name, "refcount") != NULL) { + TRACER_SET(*t->flag, enabled); + } + } + } else { + int found = 0; + for (t = tracers; t; t = t->next) { + if (0 == strcmp(name, t->flag->name)) { + TRACER_SET(*t->flag, enabled); + found = 1; + } + } + if (!found) { + gpr_log(GPR_ERROR, "Unknown trace var: '%s'", name); + return 0; /* early return */ + } + } + return 1; +} diff --git a/src/core/lib/http/format_request.c b/src/core/lib/http/format_request.c deleted file mode 100644 index 88fb0ab0b6..0000000000 --- a/src/core/lib/http/format_request.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/http/format_request.h" - -#include -#include -#include - -#include -#include -#include -#include -#include "src/core/lib/support/string.h" - -static void fill_common_header(const grpc_httpcli_request *request, - gpr_strvec *buf, bool connection_close) { - size_t i; - gpr_strvec_add(buf, gpr_strdup(request->http.path)); - gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n")); - /* just in case some crazy server really expects HTTP/1.1 */ - gpr_strvec_add(buf, gpr_strdup("Host: ")); - gpr_strvec_add(buf, gpr_strdup(request->host)); - gpr_strvec_add(buf, gpr_strdup("\r\n")); - if (connection_close) - gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n")); - gpr_strvec_add(buf, - gpr_strdup("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n")); - /* user supplied headers */ - for (i = 0; i < request->http.hdr_count; i++) { - gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].key)); - gpr_strvec_add(buf, gpr_strdup(": ")); - gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].value)); - gpr_strvec_add(buf, gpr_strdup("\r\n")); - } -} - -grpc_slice grpc_httpcli_format_get_request( - const grpc_httpcli_request *request) { - gpr_strvec out; - char *flat; - size_t flat_len; - - gpr_strvec_init(&out); - gpr_strvec_add(&out, gpr_strdup("GET ")); - fill_common_header(request, &out, true); - gpr_strvec_add(&out, gpr_strdup("\r\n")); - - flat = gpr_strvec_flatten(&out, &flat_len); - gpr_strvec_destroy(&out); - - return grpc_slice_new(flat, flat_len, gpr_free); -} - -grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, - const char *body_bytes, - size_t body_size) { - gpr_strvec out; - char *tmp; - size_t out_len; - size_t i; - - gpr_strvec_init(&out); - - gpr_strvec_add(&out, gpr_strdup("POST ")); - fill_common_header(request, &out, true); - if (body_bytes) { - uint8_t has_content_type = 0; - for (i = 0; i < request->http.hdr_count; i++) { - if (strcmp(request->http.hdrs[i].key, "Content-Type") == 0) { - has_content_type = 1; - break; - } - } - if (!has_content_type) { - gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n")); - } - gpr_asprintf(&tmp, "Content-Length: %lu\r\n", (unsigned long)body_size); - gpr_strvec_add(&out, tmp); - } - gpr_strvec_add(&out, gpr_strdup("\r\n")); - tmp = gpr_strvec_flatten(&out, &out_len); - gpr_strvec_destroy(&out); - - if (body_bytes) { - tmp = (char *)gpr_realloc(tmp, out_len + body_size); - memcpy(tmp + out_len, body_bytes, body_size); - out_len += body_size; - } - - return grpc_slice_new(tmp, out_len, gpr_free); -} - -grpc_slice grpc_httpcli_format_connect_request( - const grpc_httpcli_request *request) { - gpr_strvec out; - gpr_strvec_init(&out); - gpr_strvec_add(&out, gpr_strdup("CONNECT ")); - fill_common_header(request, &out, false); - gpr_strvec_add(&out, gpr_strdup("\r\n")); - size_t flat_len; - char *flat = gpr_strvec_flatten(&out, &flat_len); - gpr_strvec_destroy(&out); - return grpc_slice_new(flat, flat_len, gpr_free); -} diff --git a/src/core/lib/http/format_request.cc b/src/core/lib/http/format_request.cc new file mode 100644 index 0000000000..88fb0ab0b6 --- /dev/null +++ b/src/core/lib/http/format_request.cc @@ -0,0 +1,120 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/http/format_request.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "src/core/lib/support/string.h" + +static void fill_common_header(const grpc_httpcli_request *request, + gpr_strvec *buf, bool connection_close) { + size_t i; + gpr_strvec_add(buf, gpr_strdup(request->http.path)); + gpr_strvec_add(buf, gpr_strdup(" HTTP/1.0\r\n")); + /* just in case some crazy server really expects HTTP/1.1 */ + gpr_strvec_add(buf, gpr_strdup("Host: ")); + gpr_strvec_add(buf, gpr_strdup(request->host)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); + if (connection_close) + gpr_strvec_add(buf, gpr_strdup("Connection: close\r\n")); + gpr_strvec_add(buf, + gpr_strdup("User-Agent: " GRPC_HTTPCLI_USER_AGENT "\r\n")); + /* user supplied headers */ + for (i = 0; i < request->http.hdr_count; i++) { + gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].key)); + gpr_strvec_add(buf, gpr_strdup(": ")); + gpr_strvec_add(buf, gpr_strdup(request->http.hdrs[i].value)); + gpr_strvec_add(buf, gpr_strdup("\r\n")); + } +} + +grpc_slice grpc_httpcli_format_get_request( + const grpc_httpcli_request *request) { + gpr_strvec out; + char *flat; + size_t flat_len; + + gpr_strvec_init(&out); + gpr_strvec_add(&out, gpr_strdup("GET ")); + fill_common_header(request, &out, true); + gpr_strvec_add(&out, gpr_strdup("\r\n")); + + flat = gpr_strvec_flatten(&out, &flat_len); + gpr_strvec_destroy(&out); + + return grpc_slice_new(flat, flat_len, gpr_free); +} + +grpc_slice grpc_httpcli_format_post_request(const grpc_httpcli_request *request, + const char *body_bytes, + size_t body_size) { + gpr_strvec out; + char *tmp; + size_t out_len; + size_t i; + + gpr_strvec_init(&out); + + gpr_strvec_add(&out, gpr_strdup("POST ")); + fill_common_header(request, &out, true); + if (body_bytes) { + uint8_t has_content_type = 0; + for (i = 0; i < request->http.hdr_count; i++) { + if (strcmp(request->http.hdrs[i].key, "Content-Type") == 0) { + has_content_type = 1; + break; + } + } + if (!has_content_type) { + gpr_strvec_add(&out, gpr_strdup("Content-Type: text/plain\r\n")); + } + gpr_asprintf(&tmp, "Content-Length: %lu\r\n", (unsigned long)body_size); + gpr_strvec_add(&out, tmp); + } + gpr_strvec_add(&out, gpr_strdup("\r\n")); + tmp = gpr_strvec_flatten(&out, &out_len); + gpr_strvec_destroy(&out); + + if (body_bytes) { + tmp = (char *)gpr_realloc(tmp, out_len + body_size); + memcpy(tmp + out_len, body_bytes, body_size); + out_len += body_size; + } + + return grpc_slice_new(tmp, out_len, gpr_free); +} + +grpc_slice grpc_httpcli_format_connect_request( + const grpc_httpcli_request *request) { + gpr_strvec out; + gpr_strvec_init(&out); + gpr_strvec_add(&out, gpr_strdup("CONNECT ")); + fill_common_header(request, &out, false); + gpr_strvec_add(&out, gpr_strdup("\r\n")); + size_t flat_len; + char *flat = gpr_strvec_flatten(&out, &flat_len); + gpr_strvec_destroy(&out); + return grpc_slice_new(flat, flat_len, gpr_free); +} diff --git a/src/core/lib/http/httpcli.c b/src/core/lib/http/httpcli.c deleted file mode 100644 index db995943a9..0000000000 --- a/src/core/lib/http/httpcli.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/http/httpcli.h" - -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/http/format_request.h" -#include "src/core/lib/http/parser.h" -#include "src/core/lib/iomgr/endpoint.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" - -typedef struct { - grpc_slice request_text; - grpc_http_parser parser; - grpc_resolved_addresses *addresses; - size_t next_address; - grpc_endpoint *ep; - char *host; - char *ssl_host_override; - gpr_timespec deadline; - int have_read_byte; - const grpc_httpcli_handshaker *handshaker; - grpc_closure *on_done; - grpc_httpcli_context *context; - grpc_polling_entity *pollent; - grpc_iomgr_object iomgr_obj; - grpc_slice_buffer incoming; - grpc_slice_buffer outgoing; - grpc_closure on_read; - grpc_closure done_write; - grpc_closure connected; - grpc_error *overall_error; - grpc_resource_quota *resource_quota; -} internal_request; - -static grpc_httpcli_get_override g_get_override = NULL; -static grpc_httpcli_post_override g_post_override = NULL; - -static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *endpoint, const char *host, - gpr_timespec deadline, - void (*on_done)(grpc_exec_ctx *exec_ctx, - void *arg, - grpc_endpoint *endpoint)) { - on_done(exec_ctx, arg, endpoint); -} - -const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http", - plaintext_handshake}; - -void grpc_httpcli_context_init(grpc_httpcli_context *context) { - context->pollset_set = grpc_pollset_set_create(); -} - -void grpc_httpcli_context_destroy(grpc_exec_ctx *exec_ctx, - grpc_httpcli_context *context) { - grpc_pollset_set_destroy(exec_ctx, context->pollset_set); -} - -static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, - grpc_error *due_to_error); - -static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, - grpc_error *error) { - grpc_polling_entity_del_from_pollset_set(exec_ctx, req->pollent, - req->context->pollset_set); - GRPC_CLOSURE_SCHED(exec_ctx, req->on_done, error); - grpc_http_parser_destroy(&req->parser); - if (req->addresses != NULL) { - grpc_resolved_addresses_destroy(req->addresses); - } - if (req->ep != NULL) { - grpc_endpoint_destroy(exec_ctx, req->ep); - } - grpc_slice_unref_internal(exec_ctx, req->request_text); - gpr_free(req->host); - gpr_free(req->ssl_host_override); - grpc_iomgr_unregister_object(&req->iomgr_obj); - grpc_slice_buffer_destroy_internal(exec_ctx, &req->incoming); - grpc_slice_buffer_destroy_internal(exec_ctx, &req->outgoing); - GRPC_ERROR_UNREF(req->overall_error); - grpc_resource_quota_unref_internal(exec_ctx, req->resource_quota); - gpr_free(req); -} - -static void append_error(internal_request *req, grpc_error *error) { - if (req->overall_error == GRPC_ERROR_NONE) { - req->overall_error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed HTTP/1 client request"); - } - grpc_resolved_address *addr = &req->addresses->addrs[req->next_address - 1]; - char *addr_text = grpc_sockaddr_to_uri(addr); - req->overall_error = grpc_error_add_child( - req->overall_error, - grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(addr_text))); - gpr_free(addr_text); -} - -static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) { - grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read); -} - -static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *error) { - internal_request *req = (internal_request *)user_data; - size_t i; - - for (i = 0; i < req->incoming.count; i++) { - if (GRPC_SLICE_LENGTH(req->incoming.slices[i])) { - req->have_read_byte = 1; - grpc_error *err = - grpc_http_parser_parse(&req->parser, req->incoming.slices[i], NULL); - if (err != GRPC_ERROR_NONE) { - finish(exec_ctx, req, err); - return; - } - } - } - - if (error == GRPC_ERROR_NONE) { - do_read(exec_ctx, req); - } else if (!req->have_read_byte) { - next_address(exec_ctx, req, GRPC_ERROR_REF(error)); - } else { - finish(exec_ctx, req, grpc_http_parser_eof(&req->parser)); - } -} - -static void on_written(grpc_exec_ctx *exec_ctx, internal_request *req) { - do_read(exec_ctx, req); -} - -static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - internal_request *req = (internal_request *)arg; - if (error == GRPC_ERROR_NONE) { - on_written(exec_ctx, req); - } else { - next_address(exec_ctx, req, GRPC_ERROR_REF(error)); - } -} - -static void start_write(grpc_exec_ctx *exec_ctx, internal_request *req) { - grpc_slice_ref_internal(req->request_text); - grpc_slice_buffer_add(&req->outgoing, req->request_text); - grpc_endpoint_write(exec_ctx, req->ep, &req->outgoing, &req->done_write); -} - -static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *ep) { - internal_request *req = (internal_request *)arg; - - if (!ep) { - next_address(exec_ctx, req, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Unexplained handshake failure")); - return; - } - - req->ep = ep; - start_write(exec_ctx, req); -} - -static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - internal_request *req = (internal_request *)arg; - - if (!req->ep) { - next_address(exec_ctx, req, GRPC_ERROR_REF(error)); - return; - } - req->handshaker->handshake( - exec_ctx, req, req->ep, - req->ssl_host_override ? req->ssl_host_override : req->host, - req->deadline, on_handshake_done); -} - -static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, - grpc_error *error) { - grpc_resolved_address *addr; - if (error != GRPC_ERROR_NONE) { - append_error(req, error); - } - if (req->next_address == req->addresses->naddrs) { - finish(exec_ctx, req, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed HTTP requests to all targets", &req->overall_error, 1)); - return; - } - addr = &req->addresses->addrs[req->next_address++]; - GRPC_CLOSURE_INIT(&req->connected, on_connected, req, - grpc_schedule_on_exec_ctx); - grpc_arg arg = grpc_channel_arg_pointer_create( - (char *)GRPC_ARG_RESOURCE_QUOTA, req->resource_quota, - grpc_resource_quota_arg_vtable()); - grpc_channel_args args = {1, &arg}; - grpc_tcp_client_connect(exec_ctx, &req->connected, &req->ep, - req->context->pollset_set, &args, addr, - req->deadline); -} - -static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - internal_request *req = (internal_request *)arg; - if (error != GRPC_ERROR_NONE) { - finish(exec_ctx, req, GRPC_ERROR_REF(error)); - return; - } - req->next_address = 0; - next_address(exec_ctx, req, GRPC_ERROR_NONE); -} - -static void internal_request_begin(grpc_exec_ctx *exec_ctx, - grpc_httpcli_context *context, - grpc_polling_entity *pollent, - grpc_resource_quota *resource_quota, - const grpc_httpcli_request *request, - gpr_timespec deadline, grpc_closure *on_done, - grpc_httpcli_response *response, - const char *name, grpc_slice request_text) { - internal_request *req = - (internal_request *)gpr_malloc(sizeof(internal_request)); - memset(req, 0, sizeof(*req)); - req->request_text = request_text; - grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response); - req->on_done = on_done; - req->deadline = deadline; - req->handshaker = - request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; - req->context = context; - req->pollent = pollent; - req->overall_error = GRPC_ERROR_NONE; - req->resource_quota = grpc_resource_quota_ref_internal(resource_quota); - GRPC_CLOSURE_INIT(&req->on_read, on_read, req, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&req->done_write, done_write, req, - grpc_schedule_on_exec_ctx); - grpc_slice_buffer_init(&req->incoming); - grpc_slice_buffer_init(&req->outgoing); - grpc_iomgr_register_object(&req->iomgr_obj, name); - req->host = gpr_strdup(request->host); - req->ssl_host_override = gpr_strdup(request->ssl_host_override); - - GPR_ASSERT(pollent); - grpc_polling_entity_add_to_pollset_set(exec_ctx, req->pollent, - req->context->pollset_set); - grpc_resolve_address( - exec_ctx, request->host, req->handshaker->default_port, - req->context->pollset_set, - GRPC_CLOSURE_CREATE(on_resolved, req, grpc_schedule_on_exec_ctx), - &req->addresses); -} - -void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, - grpc_polling_entity *pollent, - grpc_resource_quota *resource_quota, - const grpc_httpcli_request *request, - gpr_timespec deadline, grpc_closure *on_done, - grpc_httpcli_response *response) { - char *name; - if (g_get_override && - g_get_override(exec_ctx, request, deadline, on_done, response)) { - return; - } - gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path); - internal_request_begin(exec_ctx, context, pollent, resource_quota, request, - deadline, on_done, response, name, - grpc_httpcli_format_get_request(request)); - gpr_free(name); -} - -void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, - grpc_polling_entity *pollent, - grpc_resource_quota *resource_quota, - const grpc_httpcli_request *request, - const char *body_bytes, size_t body_size, - gpr_timespec deadline, grpc_closure *on_done, - grpc_httpcli_response *response) { - char *name; - if (g_post_override && - g_post_override(exec_ctx, request, body_bytes, body_size, deadline, - on_done, response)) { - return; - } - gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path); - internal_request_begin( - exec_ctx, context, pollent, resource_quota, request, deadline, on_done, - response, name, - grpc_httpcli_format_post_request(request, body_bytes, body_size)); - gpr_free(name); -} - -void grpc_httpcli_set_override(grpc_httpcli_get_override get, - grpc_httpcli_post_override post) { - g_get_override = get; - g_post_override = post; -} diff --git a/src/core/lib/http/httpcli.cc b/src/core/lib/http/httpcli.cc new file mode 100644 index 0000000000..db995943a9 --- /dev/null +++ b/src/core/lib/http/httpcli.cc @@ -0,0 +1,321 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/http/httpcli.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/http/format_request.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/endpoint.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" + +typedef struct { + grpc_slice request_text; + grpc_http_parser parser; + grpc_resolved_addresses *addresses; + size_t next_address; + grpc_endpoint *ep; + char *host; + char *ssl_host_override; + gpr_timespec deadline; + int have_read_byte; + const grpc_httpcli_handshaker *handshaker; + grpc_closure *on_done; + grpc_httpcli_context *context; + grpc_polling_entity *pollent; + grpc_iomgr_object iomgr_obj; + grpc_slice_buffer incoming; + grpc_slice_buffer outgoing; + grpc_closure on_read; + grpc_closure done_write; + grpc_closure connected; + grpc_error *overall_error; + grpc_resource_quota *resource_quota; +} internal_request; + +static grpc_httpcli_get_override g_get_override = NULL; +static grpc_httpcli_post_override g_post_override = NULL; + +static void plaintext_handshake(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *endpoint, const char *host, + gpr_timespec deadline, + void (*on_done)(grpc_exec_ctx *exec_ctx, + void *arg, + grpc_endpoint *endpoint)) { + on_done(exec_ctx, arg, endpoint); +} + +const grpc_httpcli_handshaker grpc_httpcli_plaintext = {"http", + plaintext_handshake}; + +void grpc_httpcli_context_init(grpc_httpcli_context *context) { + context->pollset_set = grpc_pollset_set_create(); +} + +void grpc_httpcli_context_destroy(grpc_exec_ctx *exec_ctx, + grpc_httpcli_context *context) { + grpc_pollset_set_destroy(exec_ctx, context->pollset_set); +} + +static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, + grpc_error *due_to_error); + +static void finish(grpc_exec_ctx *exec_ctx, internal_request *req, + grpc_error *error) { + grpc_polling_entity_del_from_pollset_set(exec_ctx, req->pollent, + req->context->pollset_set); + GRPC_CLOSURE_SCHED(exec_ctx, req->on_done, error); + grpc_http_parser_destroy(&req->parser); + if (req->addresses != NULL) { + grpc_resolved_addresses_destroy(req->addresses); + } + if (req->ep != NULL) { + grpc_endpoint_destroy(exec_ctx, req->ep); + } + grpc_slice_unref_internal(exec_ctx, req->request_text); + gpr_free(req->host); + gpr_free(req->ssl_host_override); + grpc_iomgr_unregister_object(&req->iomgr_obj); + grpc_slice_buffer_destroy_internal(exec_ctx, &req->incoming); + grpc_slice_buffer_destroy_internal(exec_ctx, &req->outgoing); + GRPC_ERROR_UNREF(req->overall_error); + grpc_resource_quota_unref_internal(exec_ctx, req->resource_quota); + gpr_free(req); +} + +static void append_error(internal_request *req, grpc_error *error) { + if (req->overall_error == GRPC_ERROR_NONE) { + req->overall_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed HTTP/1 client request"); + } + grpc_resolved_address *addr = &req->addresses->addrs[req->next_address - 1]; + char *addr_text = grpc_sockaddr_to_uri(addr); + req->overall_error = grpc_error_add_child( + req->overall_error, + grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(addr_text))); + gpr_free(addr_text); +} + +static void do_read(grpc_exec_ctx *exec_ctx, internal_request *req) { + grpc_endpoint_read(exec_ctx, req->ep, &req->incoming, &req->on_read); +} + +static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *error) { + internal_request *req = (internal_request *)user_data; + size_t i; + + for (i = 0; i < req->incoming.count; i++) { + if (GRPC_SLICE_LENGTH(req->incoming.slices[i])) { + req->have_read_byte = 1; + grpc_error *err = + grpc_http_parser_parse(&req->parser, req->incoming.slices[i], NULL); + if (err != GRPC_ERROR_NONE) { + finish(exec_ctx, req, err); + return; + } + } + } + + if (error == GRPC_ERROR_NONE) { + do_read(exec_ctx, req); + } else if (!req->have_read_byte) { + next_address(exec_ctx, req, GRPC_ERROR_REF(error)); + } else { + finish(exec_ctx, req, grpc_http_parser_eof(&req->parser)); + } +} + +static void on_written(grpc_exec_ctx *exec_ctx, internal_request *req) { + do_read(exec_ctx, req); +} + +static void done_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + internal_request *req = (internal_request *)arg; + if (error == GRPC_ERROR_NONE) { + on_written(exec_ctx, req); + } else { + next_address(exec_ctx, req, GRPC_ERROR_REF(error)); + } +} + +static void start_write(grpc_exec_ctx *exec_ctx, internal_request *req) { + grpc_slice_ref_internal(req->request_text); + grpc_slice_buffer_add(&req->outgoing, req->request_text); + grpc_endpoint_write(exec_ctx, req->ep, &req->outgoing, &req->done_write); +} + +static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *ep) { + internal_request *req = (internal_request *)arg; + + if (!ep) { + next_address(exec_ctx, req, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unexplained handshake failure")); + return; + } + + req->ep = ep; + start_write(exec_ctx, req); +} + +static void on_connected(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + internal_request *req = (internal_request *)arg; + + if (!req->ep) { + next_address(exec_ctx, req, GRPC_ERROR_REF(error)); + return; + } + req->handshaker->handshake( + exec_ctx, req, req->ep, + req->ssl_host_override ? req->ssl_host_override : req->host, + req->deadline, on_handshake_done); +} + +static void next_address(grpc_exec_ctx *exec_ctx, internal_request *req, + grpc_error *error) { + grpc_resolved_address *addr; + if (error != GRPC_ERROR_NONE) { + append_error(req, error); + } + if (req->next_address == req->addresses->naddrs) { + finish(exec_ctx, req, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed HTTP requests to all targets", &req->overall_error, 1)); + return; + } + addr = &req->addresses->addrs[req->next_address++]; + GRPC_CLOSURE_INIT(&req->connected, on_connected, req, + grpc_schedule_on_exec_ctx); + grpc_arg arg = grpc_channel_arg_pointer_create( + (char *)GRPC_ARG_RESOURCE_QUOTA, req->resource_quota, + grpc_resource_quota_arg_vtable()); + grpc_channel_args args = {1, &arg}; + grpc_tcp_client_connect(exec_ctx, &req->connected, &req->ep, + req->context->pollset_set, &args, addr, + req->deadline); +} + +static void on_resolved(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + internal_request *req = (internal_request *)arg; + if (error != GRPC_ERROR_NONE) { + finish(exec_ctx, req, GRPC_ERROR_REF(error)); + return; + } + req->next_address = 0; + next_address(exec_ctx, req, GRPC_ERROR_NONE); +} + +static void internal_request_begin(grpc_exec_ctx *exec_ctx, + grpc_httpcli_context *context, + grpc_polling_entity *pollent, + grpc_resource_quota *resource_quota, + const grpc_httpcli_request *request, + gpr_timespec deadline, grpc_closure *on_done, + grpc_httpcli_response *response, + const char *name, grpc_slice request_text) { + internal_request *req = + (internal_request *)gpr_malloc(sizeof(internal_request)); + memset(req, 0, sizeof(*req)); + req->request_text = request_text; + grpc_http_parser_init(&req->parser, GRPC_HTTP_RESPONSE, response); + req->on_done = on_done; + req->deadline = deadline; + req->handshaker = + request->handshaker ? request->handshaker : &grpc_httpcli_plaintext; + req->context = context; + req->pollent = pollent; + req->overall_error = GRPC_ERROR_NONE; + req->resource_quota = grpc_resource_quota_ref_internal(resource_quota); + GRPC_CLOSURE_INIT(&req->on_read, on_read, req, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&req->done_write, done_write, req, + grpc_schedule_on_exec_ctx); + grpc_slice_buffer_init(&req->incoming); + grpc_slice_buffer_init(&req->outgoing); + grpc_iomgr_register_object(&req->iomgr_obj, name); + req->host = gpr_strdup(request->host); + req->ssl_host_override = gpr_strdup(request->ssl_host_override); + + GPR_ASSERT(pollent); + grpc_polling_entity_add_to_pollset_set(exec_ctx, req->pollent, + req->context->pollset_set); + grpc_resolve_address( + exec_ctx, request->host, req->handshaker->default_port, + req->context->pollset_set, + GRPC_CLOSURE_CREATE(on_resolved, req, grpc_schedule_on_exec_ctx), + &req->addresses); +} + +void grpc_httpcli_get(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_polling_entity *pollent, + grpc_resource_quota *resource_quota, + const grpc_httpcli_request *request, + gpr_timespec deadline, grpc_closure *on_done, + grpc_httpcli_response *response) { + char *name; + if (g_get_override && + g_get_override(exec_ctx, request, deadline, on_done, response)) { + return; + } + gpr_asprintf(&name, "HTTP:GET:%s:%s", request->host, request->http.path); + internal_request_begin(exec_ctx, context, pollent, resource_quota, request, + deadline, on_done, response, name, + grpc_httpcli_format_get_request(request)); + gpr_free(name); +} + +void grpc_httpcli_post(grpc_exec_ctx *exec_ctx, grpc_httpcli_context *context, + grpc_polling_entity *pollent, + grpc_resource_quota *resource_quota, + const grpc_httpcli_request *request, + const char *body_bytes, size_t body_size, + gpr_timespec deadline, grpc_closure *on_done, + grpc_httpcli_response *response) { + char *name; + if (g_post_override && + g_post_override(exec_ctx, request, body_bytes, body_size, deadline, + on_done, response)) { + return; + } + gpr_asprintf(&name, "HTTP:POST:%s:%s", request->host, request->http.path); + internal_request_begin( + exec_ctx, context, pollent, resource_quota, request, deadline, on_done, + response, name, + grpc_httpcli_format_post_request(request, body_bytes, body_size)); + gpr_free(name); +} + +void grpc_httpcli_set_override(grpc_httpcli_get_override get, + grpc_httpcli_post_override post) { + g_get_override = get; + g_post_override = post; +} diff --git a/src/core/lib/http/httpcli_security_connector.c b/src/core/lib/http/httpcli_security_connector.c deleted file mode 100644 index 8a0f225ba2..0000000000 --- a/src/core/lib/http/httpcli_security_connector.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/http/httpcli.h" - -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker_registry.h" -#include "src/core/lib/security/transport/security_handshaker.h" -#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; - tsi_ssl_client_handshaker_factory *handshaker_factory; - char *secure_peer_name; -} grpc_httpcli_ssl_channel_security_connector; - -static void httpcli_ssl_destroy(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc) { - grpc_httpcli_ssl_channel_security_connector *c = - (grpc_httpcli_ssl_channel_security_connector *)sc; - if (c->handshaker_factory != NULL) { - tsi_ssl_client_handshaker_factory_unref(c->handshaker_factory); - c->handshaker_factory = NULL; - } - if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name); - gpr_free(sc); -} - -static void httpcli_ssl_add_handshakers(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, - grpc_handshake_manager *handshake_mgr) { - grpc_httpcli_ssl_channel_security_connector *c = - (grpc_httpcli_ssl_channel_security_connector *)sc; - tsi_handshaker *handshaker = NULL; - if (c->handshaker_factory != NULL) { - tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker( - c->handshaker_factory, c->secure_peer_name, &handshaker); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", - tsi_result_to_string(result)); - } - } - grpc_handshake_manager_add( - handshake_mgr, - grpc_security_handshaker_create( - exec_ctx, tsi_create_adapter_handshaker(handshaker), &sc->base)); -} - -static void httpcli_ssl_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - grpc_httpcli_ssl_channel_security_connector *c = - (grpc_httpcli_ssl_channel_security_connector *)sc; - grpc_error *error = GRPC_ERROR_NONE; - - /* Check the peer name. */ - if (c->secure_peer_name != NULL && - !tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) { - char *msg; - gpr_asprintf(&msg, "Peer name %s is not in peer certificate", - c->secure_peer_name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - } - GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); - tsi_peer_destruct(&peer); -} - -static grpc_security_connector_vtable httpcli_ssl_vtable = { - httpcli_ssl_destroy, httpcli_ssl_check_peer}; - -static grpc_security_status httpcli_ssl_channel_security_connector_create( - grpc_exec_ctx *exec_ctx, const char *pem_root_certs, - const char *secure_peer_name, grpc_channel_security_connector **sc) { - tsi_result result = TSI_OK; - grpc_httpcli_ssl_channel_security_connector *c; - - if (secure_peer_name != NULL && pem_root_certs == NULL) { - gpr_log(GPR_ERROR, - "Cannot assert a secure peer name without a trust root."); - return GRPC_SECURITY_ERROR; - } - - c = (grpc_httpcli_ssl_channel_security_connector *)gpr_zalloc( - sizeof(grpc_httpcli_ssl_channel_security_connector)); - - gpr_ref_init(&c->base.base.refcount, 1); - c->base.base.vtable = &httpcli_ssl_vtable; - if (secure_peer_name != NULL) { - c->secure_peer_name = gpr_strdup(secure_peer_name); - } - result = tsi_create_ssl_client_handshaker_factory( - NULL, pem_root_certs, NULL, NULL, 0, &c->handshaker_factory); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", - tsi_result_to_string(result)); - httpcli_ssl_destroy(exec_ctx, &c->base.base); - *sc = NULL; - return GRPC_SECURITY_ERROR; - } - c->base.add_handshakers = httpcli_ssl_add_handshakers; - *sc = &c->base; - return GRPC_SECURITY_OK; -} - -/* handshaker */ - -typedef struct { - void (*func)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint); - void *arg; - grpc_handshake_manager *handshake_mgr; -} on_done_closure; - -static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_handshaker_args *args = (grpc_handshaker_args *)arg; - on_done_closure *c = (on_done_closure *)args->user_data; - if (error != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(error); - gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg); - - c->func(exec_ctx, c->arg, NULL); - } else { - grpc_channel_args_destroy(exec_ctx, args->args); - grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer); - gpr_free(args->read_buffer); - c->func(exec_ctx, c->arg, args->endpoint); - } - grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr); - gpr_free(c); -} - -static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *tcp, const char *host, - gpr_timespec deadline, - void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, - grpc_endpoint *endpoint)) { - on_done_closure *c = (on_done_closure *)gpr_malloc(sizeof(*c)); - const char *pem_root_certs = grpc_get_default_ssl_roots(); - if (pem_root_certs == NULL) { - gpr_log(GPR_ERROR, "Could not get default pem root certs."); - on_done(exec_ctx, arg, NULL); - gpr_free(c); - return; - } - c->func = on_done; - c->arg = arg; - grpc_channel_security_connector *sc = NULL; - GPR_ASSERT(httpcli_ssl_channel_security_connector_create( - exec_ctx, pem_root_certs, host, &sc) == GRPC_SECURITY_OK); - grpc_arg channel_arg = grpc_security_connector_to_arg(&sc->base); - grpc_channel_args args = {1, &channel_arg}; - c->handshake_mgr = grpc_handshake_manager_create(); - grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, &args, c->handshake_mgr); - grpc_handshake_manager_do_handshake( - exec_ctx, c->handshake_mgr, tcp, NULL /* channel_args */, deadline, - NULL /* acceptor */, on_handshake_done, c /* user_data */); - GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &sc->base, "httpcli"); -} - -const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake}; diff --git a/src/core/lib/http/httpcli_security_connector.cc b/src/core/lib/http/httpcli_security_connector.cc new file mode 100644 index 0000000000..8a0f225ba2 --- /dev/null +++ b/src/core/lib/http/httpcli_security_connector.cc @@ -0,0 +1,186 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/http/httpcli.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#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; + tsi_ssl_client_handshaker_factory *handshaker_factory; + char *secure_peer_name; +} grpc_httpcli_ssl_channel_security_connector; + +static void httpcli_ssl_destroy(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc) { + grpc_httpcli_ssl_channel_security_connector *c = + (grpc_httpcli_ssl_channel_security_connector *)sc; + if (c->handshaker_factory != NULL) { + tsi_ssl_client_handshaker_factory_unref(c->handshaker_factory); + c->handshaker_factory = NULL; + } + if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name); + gpr_free(sc); +} + +static void httpcli_ssl_add_handshakers(grpc_exec_ctx *exec_ctx, + grpc_channel_security_connector *sc, + grpc_handshake_manager *handshake_mgr) { + grpc_httpcli_ssl_channel_security_connector *c = + (grpc_httpcli_ssl_channel_security_connector *)sc; + tsi_handshaker *handshaker = NULL; + if (c->handshaker_factory != NULL) { + tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker( + c->handshaker_factory, c->secure_peer_name, &handshaker); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + } + } + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + exec_ctx, tsi_create_adapter_handshaker(handshaker), &sc->base)); +} + +static void httpcli_ssl_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + grpc_httpcli_ssl_channel_security_connector *c = + (grpc_httpcli_ssl_channel_security_connector *)sc; + grpc_error *error = GRPC_ERROR_NONE; + + /* Check the peer name. */ + if (c->secure_peer_name != NULL && + !tsi_ssl_peer_matches_name(&peer, c->secure_peer_name)) { + char *msg; + gpr_asprintf(&msg, "Peer name %s is not in peer certificate", + c->secure_peer_name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } + GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); + tsi_peer_destruct(&peer); +} + +static grpc_security_connector_vtable httpcli_ssl_vtable = { + httpcli_ssl_destroy, httpcli_ssl_check_peer}; + +static grpc_security_status httpcli_ssl_channel_security_connector_create( + grpc_exec_ctx *exec_ctx, const char *pem_root_certs, + const char *secure_peer_name, grpc_channel_security_connector **sc) { + tsi_result result = TSI_OK; + grpc_httpcli_ssl_channel_security_connector *c; + + if (secure_peer_name != NULL && pem_root_certs == NULL) { + gpr_log(GPR_ERROR, + "Cannot assert a secure peer name without a trust root."); + return GRPC_SECURITY_ERROR; + } + + c = (grpc_httpcli_ssl_channel_security_connector *)gpr_zalloc( + sizeof(grpc_httpcli_ssl_channel_security_connector)); + + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &httpcli_ssl_vtable; + if (secure_peer_name != NULL) { + c->secure_peer_name = gpr_strdup(secure_peer_name); + } + result = tsi_create_ssl_client_handshaker_factory( + NULL, pem_root_certs, NULL, NULL, 0, &c->handshaker_factory); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + httpcli_ssl_destroy(exec_ctx, &c->base.base); + *sc = NULL; + return GRPC_SECURITY_ERROR; + } + c->base.add_handshakers = httpcli_ssl_add_handshakers; + *sc = &c->base; + return GRPC_SECURITY_OK; +} + +/* handshaker */ + +typedef struct { + void (*func)(grpc_exec_ctx *exec_ctx, void *arg, grpc_endpoint *endpoint); + void *arg; + grpc_handshake_manager *handshake_mgr; +} on_done_closure; + +static void on_handshake_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_handshaker_args *args = (grpc_handshaker_args *)arg; + on_done_closure *c = (on_done_closure *)args->user_data; + if (error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(error); + gpr_log(GPR_ERROR, "Secure transport setup failed: %s", msg); + + c->func(exec_ctx, c->arg, NULL); + } else { + grpc_channel_args_destroy(exec_ctx, args->args); + grpc_slice_buffer_destroy_internal(exec_ctx, args->read_buffer); + gpr_free(args->read_buffer); + c->func(exec_ctx, c->arg, args->endpoint); + } + grpc_handshake_manager_destroy(exec_ctx, c->handshake_mgr); + gpr_free(c); +} + +static void ssl_handshake(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *tcp, const char *host, + gpr_timespec deadline, + void (*on_done)(grpc_exec_ctx *exec_ctx, void *arg, + grpc_endpoint *endpoint)) { + on_done_closure *c = (on_done_closure *)gpr_malloc(sizeof(*c)); + const char *pem_root_certs = grpc_get_default_ssl_roots(); + if (pem_root_certs == NULL) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + on_done(exec_ctx, arg, NULL); + gpr_free(c); + return; + } + c->func = on_done; + c->arg = arg; + grpc_channel_security_connector *sc = NULL; + GPR_ASSERT(httpcli_ssl_channel_security_connector_create( + exec_ctx, pem_root_certs, host, &sc) == GRPC_SECURITY_OK); + grpc_arg channel_arg = grpc_security_connector_to_arg(&sc->base); + grpc_channel_args args = {1, &channel_arg}; + c->handshake_mgr = grpc_handshake_manager_create(); + grpc_handshakers_add(exec_ctx, HANDSHAKER_CLIENT, &args, c->handshake_mgr); + grpc_handshake_manager_do_handshake( + exec_ctx, c->handshake_mgr, tcp, NULL /* channel_args */, deadline, + NULL /* acceptor */, on_handshake_done, c /* user_data */); + GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &sc->base, "httpcli"); +} + +const grpc_httpcli_handshaker grpc_httpcli_ssl = {"https", ssl_handshake}; diff --git a/src/core/lib/http/parser.c b/src/core/lib/http/parser.c deleted file mode 100644 index 0950bd655e..0000000000 --- a/src/core/lib/http/parser.c +++ /dev/null @@ -1,365 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/http/parser.h" - -#include -#include - -#include -#include -#include - -grpc_tracer_flag grpc_http1_trace = GRPC_TRACER_INITIALIZER(false, "http1"); - -static char *buf2str(void *buffer, size_t length) { - char *out = (char *)gpr_malloc(length + 1); - memcpy(out, buffer, length); - out[length] = 0; - return out; -} - -static grpc_error *handle_response_line(grpc_http_parser *parser) { - uint8_t *beg = parser->cur_line; - uint8_t *cur = beg; - uint8_t *end = beg + parser->cur_line_length; - - if (cur == end || *cur++ != 'H') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'"); - if (cur == end || *cur++ != 'T') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); - if (cur == end || *cur++ != 'T') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); - if (cur == end || *cur++ != 'P') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'"); - if (cur == end || *cur++ != '/') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'"); - if (cur == end || *cur++ != '1') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '1'"); - if (cur == end || *cur++ != '.') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '.'"); - if (cur == end || *cur < '0' || *cur++ > '1') { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Expected HTTP/1.0 or HTTP/1.1"); - } - if (cur == end || *cur++ != ' ') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '"); - if (cur == end || *cur < '1' || *cur++ > '9') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); - if (cur == end || *cur < '0' || *cur++ > '9') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); - if (cur == end || *cur < '0' || *cur++ > '9') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); - parser->http.response->status = - (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); - if (cur == end || *cur++ != ' ') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '"); - - /* we don't really care about the status code message */ - - return GRPC_ERROR_NONE; -} - -static grpc_error *handle_request_line(grpc_http_parser *parser) { - uint8_t *beg = parser->cur_line; - uint8_t *cur = beg; - uint8_t *end = beg + parser->cur_line_length; - uint8_t vers_major = 0; - uint8_t vers_minor = 0; - - while (cur != end && *cur++ != ' ') - ; - if (cur == end) - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "No method on HTTP request line"); - parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1)); - - beg = cur; - while (cur != end && *cur++ != ' ') - ; - if (cur == end) - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No path on HTTP request line"); - parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1)); - - if (cur == end || *cur++ != 'H') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'"); - if (cur == end || *cur++ != 'T') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); - if (cur == end || *cur++ != 'T') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); - if (cur == end || *cur++ != 'P') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'"); - if (cur == end || *cur++ != '/') - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'"); - vers_major = (uint8_t)(*cur++ - '1' + 1); - ++cur; - if (cur == end) - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "End of line in HTTP version string"); - vers_minor = (uint8_t)(*cur++ - '1' + 1); - - if (vers_major == 1) { - if (vers_minor == 0) { - parser->http.request->version = GRPC_HTTP_HTTP10; - } else if (vers_minor == 1) { - parser->http.request->version = GRPC_HTTP_HTTP11; - } else { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); - } - } else if (vers_major == 2) { - if (vers_minor == 0) { - parser->http.request->version = GRPC_HTTP_HTTP20; - } else { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); - } - } else { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); - } - - return GRPC_ERROR_NONE; -} - -static grpc_error *handle_first_line(grpc_http_parser *parser) { - switch (parser->type) { - case GRPC_HTTP_REQUEST: - return handle_request_line(parser); - case GRPC_HTTP_RESPONSE: - return handle_response_line(parser); - } - GPR_UNREACHABLE_CODE( - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); -} - -static grpc_error *add_header(grpc_http_parser *parser) { - uint8_t *beg = parser->cur_line; - uint8_t *cur = beg; - uint8_t *end = beg + parser->cur_line_length; - size_t *hdr_count = NULL; - grpc_http_header **hdrs = NULL; - grpc_http_header hdr = {NULL, NULL}; - grpc_error *error = GRPC_ERROR_NONE; - - GPR_ASSERT(cur != end); - - if (*cur == ' ' || *cur == '\t') { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Continued header lines not supported yet"); - goto done; - } - - while (cur != end && *cur != ':') { - cur++; - } - if (cur == end) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Didn't find ':' in header string"); - goto done; - } - GPR_ASSERT(cur >= beg); - hdr.key = buf2str(beg, (size_t)(cur - beg)); - cur++; /* skip : */ - - while (cur != end && (*cur == ' ' || *cur == '\t')) { - cur++; - } - GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length); - hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length); - - switch (parser->type) { - case GRPC_HTTP_RESPONSE: - hdr_count = &parser->http.response->hdr_count; - hdrs = &parser->http.response->hdrs; - break; - case GRPC_HTTP_REQUEST: - hdr_count = &parser->http.request->hdr_count; - hdrs = &parser->http.request->hdrs; - break; - } - - if (*hdr_count == parser->hdr_capacity) { - parser->hdr_capacity = - GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2); - *hdrs = (grpc_http_header *)gpr_realloc( - *hdrs, parser->hdr_capacity * sizeof(**hdrs)); - } - (*hdrs)[(*hdr_count)++] = hdr; - -done: - if (error != GRPC_ERROR_NONE) { - gpr_free(hdr.key); - gpr_free(hdr.value); - } - return error; -} - -static grpc_error *finish_line(grpc_http_parser *parser, - bool *found_body_start) { - grpc_error *err; - switch (parser->state) { - case GRPC_HTTP_FIRST_LINE: - err = handle_first_line(parser); - if (err != GRPC_ERROR_NONE) return err; - parser->state = GRPC_HTTP_HEADERS; - break; - case GRPC_HTTP_HEADERS: - if (parser->cur_line_length == parser->cur_line_end_length) { - parser->state = GRPC_HTTP_BODY; - *found_body_start = true; - break; - } - err = add_header(parser); - if (err != GRPC_ERROR_NONE) { - return err; - } - break; - case GRPC_HTTP_BODY: - GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Should never reach here")); - } - - parser->cur_line_length = 0; - return GRPC_ERROR_NONE; -} - -static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) { - size_t *body_length = NULL; - char **body = NULL; - - if (parser->type == GRPC_HTTP_RESPONSE) { - body_length = &parser->http.response->body_length; - body = &parser->http.response->body; - } else if (parser->type == GRPC_HTTP_REQUEST) { - body_length = &parser->http.request->body_length; - body = &parser->http.request->body; - } else { - GPR_UNREACHABLE_CODE( - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); - } - - if (*body_length == parser->body_capacity) { - parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); - *body = (char *)gpr_realloc((void *)*body, parser->body_capacity); - } - (*body)[*body_length] = (char)byte; - (*body_length)++; - - return GRPC_ERROR_NONE; -} - -static bool check_line(grpc_http_parser *parser) { - if (parser->cur_line_length >= 2 && - parser->cur_line[parser->cur_line_length - 2] == '\r' && - parser->cur_line[parser->cur_line_length - 1] == '\n') { - return true; - } - - // HTTP request with \n\r line termiantors. - else if (parser->cur_line_length >= 2 && - parser->cur_line[parser->cur_line_length - 2] == '\n' && - parser->cur_line[parser->cur_line_length - 1] == '\r') { - return true; - } - - // HTTP request with only \n line terminators. - else if (parser->cur_line_length >= 1 && - parser->cur_line[parser->cur_line_length - 1] == '\n') { - parser->cur_line_end_length = 1; - return true; - } - - return false; -} - -static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte, - bool *found_body_start) { - switch (parser->state) { - case GRPC_HTTP_FIRST_LINE: - case GRPC_HTTP_HEADERS: - if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) { - if (GRPC_TRACER_ON(grpc_http1_trace)) - gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded", - GRPC_HTTP_PARSER_MAX_HEADER_LENGTH); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "HTTP header max line length exceeded"); - } - parser->cur_line[parser->cur_line_length] = byte; - parser->cur_line_length++; - if (check_line(parser)) { - return finish_line(parser, found_body_start); - } - return GRPC_ERROR_NONE; - case GRPC_HTTP_BODY: - return addbyte_body(parser, byte); - } - GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE); -} - -void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type, - void *request_or_response) { - memset(parser, 0, sizeof(*parser)); - parser->state = GRPC_HTTP_FIRST_LINE; - parser->type = type; - parser->http.request_or_response = request_or_response; - parser->cur_line_end_length = 2; -} - -void grpc_http_parser_destroy(grpc_http_parser *parser) {} - -void grpc_http_request_destroy(grpc_http_request *request) { - size_t i; - gpr_free(request->body); - for (i = 0; i < request->hdr_count; i++) { - gpr_free(request->hdrs[i].key); - gpr_free(request->hdrs[i].value); - } - gpr_free(request->hdrs); - gpr_free(request->method); - gpr_free(request->path); -} - -void grpc_http_response_destroy(grpc_http_response *response) { - size_t i; - gpr_free(response->body); - for (i = 0; i < response->hdr_count; i++) { - gpr_free(response->hdrs[i].key); - gpr_free(response->hdrs[i].value); - } - gpr_free(response->hdrs); -} - -grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, grpc_slice slice, - size_t *start_of_body) { - for (size_t i = 0; i < GRPC_SLICE_LENGTH(slice); i++) { - bool found_body_start = false; - grpc_error *err = - addbyte(parser, GRPC_SLICE_START_PTR(slice)[i], &found_body_start); - if (err != GRPC_ERROR_NONE) return err; - if (found_body_start && start_of_body != NULL) *start_of_body = i + 1; - } - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) { - if (parser->state != GRPC_HTTP_BODY) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Did not finish headers"); - } - return GRPC_ERROR_NONE; -} diff --git a/src/core/lib/http/parser.cc b/src/core/lib/http/parser.cc new file mode 100644 index 0000000000..0950bd655e --- /dev/null +++ b/src/core/lib/http/parser.cc @@ -0,0 +1,365 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/http/parser.h" + +#include +#include + +#include +#include +#include + +grpc_tracer_flag grpc_http1_trace = GRPC_TRACER_INITIALIZER(false, "http1"); + +static char *buf2str(void *buffer, size_t length) { + char *out = (char *)gpr_malloc(length + 1); + memcpy(out, buffer, length); + out[length] = 0; + return out; +} + +static grpc_error *handle_response_line(grpc_http_parser *parser) { + uint8_t *beg = parser->cur_line; + uint8_t *cur = beg; + uint8_t *end = beg + parser->cur_line_length; + + if (cur == end || *cur++ != 'H') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'P') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'"); + if (cur == end || *cur++ != '/') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'"); + if (cur == end || *cur++ != '1') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '1'"); + if (cur == end || *cur++ != '.') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '.'"); + if (cur == end || *cur < '0' || *cur++ > '1') { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected HTTP/1.0 or HTTP/1.1"); + } + if (cur == end || *cur++ != ' ') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '"); + if (cur == end || *cur < '1' || *cur++ > '9') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); + if (cur == end || *cur < '0' || *cur++ > '9') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected status code"); + parser->http.response->status = + (cur[-3] - '0') * 100 + (cur[-2] - '0') * 10 + (cur[-1] - '0'); + if (cur == end || *cur++ != ' ') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected ' '"); + + /* we don't really care about the status code message */ + + return GRPC_ERROR_NONE; +} + +static grpc_error *handle_request_line(grpc_http_parser *parser) { + uint8_t *beg = parser->cur_line; + uint8_t *cur = beg; + uint8_t *end = beg + parser->cur_line_length; + uint8_t vers_major = 0; + uint8_t vers_minor = 0; + + while (cur != end && *cur++ != ' ') + ; + if (cur == end) + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No method on HTTP request line"); + parser->http.request->method = buf2str(beg, (size_t)(cur - beg - 1)); + + beg = cur; + while (cur != end && *cur++ != ' ') + ; + if (cur == end) + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No path on HTTP request line"); + parser->http.request->path = buf2str(beg, (size_t)(cur - beg - 1)); + + if (cur == end || *cur++ != 'H') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'H'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'T') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'T'"); + if (cur == end || *cur++ != 'P') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected 'P'"); + if (cur == end || *cur++ != '/') + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Expected '/'"); + vers_major = (uint8_t)(*cur++ - '1' + 1); + ++cur; + if (cur == end) + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "End of line in HTTP version string"); + vers_minor = (uint8_t)(*cur++ - '1' + 1); + + if (vers_major == 1) { + if (vers_minor == 0) { + parser->http.request->version = GRPC_HTTP_HTTP10; + } else if (vers_minor == 1) { + parser->http.request->version = GRPC_HTTP_HTTP11; + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + } else if (vers_major == 2) { + if (vers_minor == 0) { + parser->http.request->version = GRPC_HTTP_HTTP20; + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected one of HTTP/1.0, HTTP/1.1, or HTTP/2.0"); + } + + return GRPC_ERROR_NONE; +} + +static grpc_error *handle_first_line(grpc_http_parser *parser) { + switch (parser->type) { + case GRPC_HTTP_REQUEST: + return handle_request_line(parser); + case GRPC_HTTP_RESPONSE: + return handle_response_line(parser); + } + GPR_UNREACHABLE_CODE( + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); +} + +static grpc_error *add_header(grpc_http_parser *parser) { + uint8_t *beg = parser->cur_line; + uint8_t *cur = beg; + uint8_t *end = beg + parser->cur_line_length; + size_t *hdr_count = NULL; + grpc_http_header **hdrs = NULL; + grpc_http_header hdr = {NULL, NULL}; + grpc_error *error = GRPC_ERROR_NONE; + + GPR_ASSERT(cur != end); + + if (*cur == ' ' || *cur == '\t') { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Continued header lines not supported yet"); + goto done; + } + + while (cur != end && *cur != ':') { + cur++; + } + if (cur == end) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Didn't find ':' in header string"); + goto done; + } + GPR_ASSERT(cur >= beg); + hdr.key = buf2str(beg, (size_t)(cur - beg)); + cur++; /* skip : */ + + while (cur != end && (*cur == ' ' || *cur == '\t')) { + cur++; + } + GPR_ASSERT((size_t)(end - cur) >= parser->cur_line_end_length); + hdr.value = buf2str(cur, (size_t)(end - cur) - parser->cur_line_end_length); + + switch (parser->type) { + case GRPC_HTTP_RESPONSE: + hdr_count = &parser->http.response->hdr_count; + hdrs = &parser->http.response->hdrs; + break; + case GRPC_HTTP_REQUEST: + hdr_count = &parser->http.request->hdr_count; + hdrs = &parser->http.request->hdrs; + break; + } + + if (*hdr_count == parser->hdr_capacity) { + parser->hdr_capacity = + GPR_MAX(parser->hdr_capacity + 1, parser->hdr_capacity * 3 / 2); + *hdrs = (grpc_http_header *)gpr_realloc( + *hdrs, parser->hdr_capacity * sizeof(**hdrs)); + } + (*hdrs)[(*hdr_count)++] = hdr; + +done: + if (error != GRPC_ERROR_NONE) { + gpr_free(hdr.key); + gpr_free(hdr.value); + } + return error; +} + +static grpc_error *finish_line(grpc_http_parser *parser, + bool *found_body_start) { + grpc_error *err; + switch (parser->state) { + case GRPC_HTTP_FIRST_LINE: + err = handle_first_line(parser); + if (err != GRPC_ERROR_NONE) return err; + parser->state = GRPC_HTTP_HEADERS; + break; + case GRPC_HTTP_HEADERS: + if (parser->cur_line_length == parser->cur_line_end_length) { + parser->state = GRPC_HTTP_BODY; + *found_body_start = true; + break; + } + err = add_header(parser); + if (err != GRPC_ERROR_NONE) { + return err; + } + break; + case GRPC_HTTP_BODY: + GPR_UNREACHABLE_CODE(return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Should never reach here")); + } + + parser->cur_line_length = 0; + return GRPC_ERROR_NONE; +} + +static grpc_error *addbyte_body(grpc_http_parser *parser, uint8_t byte) { + size_t *body_length = NULL; + char **body = NULL; + + if (parser->type == GRPC_HTTP_RESPONSE) { + body_length = &parser->http.response->body_length; + body = &parser->http.response->body; + } else if (parser->type == GRPC_HTTP_REQUEST) { + body_length = &parser->http.request->body_length; + body = &parser->http.request->body; + } else { + GPR_UNREACHABLE_CODE( + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Should never reach here")); + } + + if (*body_length == parser->body_capacity) { + parser->body_capacity = GPR_MAX(8, parser->body_capacity * 3 / 2); + *body = (char *)gpr_realloc((void *)*body, parser->body_capacity); + } + (*body)[*body_length] = (char)byte; + (*body_length)++; + + return GRPC_ERROR_NONE; +} + +static bool check_line(grpc_http_parser *parser) { + if (parser->cur_line_length >= 2 && + parser->cur_line[parser->cur_line_length - 2] == '\r' && + parser->cur_line[parser->cur_line_length - 1] == '\n') { + return true; + } + + // HTTP request with \n\r line termiantors. + else if (parser->cur_line_length >= 2 && + parser->cur_line[parser->cur_line_length - 2] == '\n' && + parser->cur_line[parser->cur_line_length - 1] == '\r') { + return true; + } + + // HTTP request with only \n line terminators. + else if (parser->cur_line_length >= 1 && + parser->cur_line[parser->cur_line_length - 1] == '\n') { + parser->cur_line_end_length = 1; + return true; + } + + return false; +} + +static grpc_error *addbyte(grpc_http_parser *parser, uint8_t byte, + bool *found_body_start) { + switch (parser->state) { + case GRPC_HTTP_FIRST_LINE: + case GRPC_HTTP_HEADERS: + if (parser->cur_line_length >= GRPC_HTTP_PARSER_MAX_HEADER_LENGTH) { + if (GRPC_TRACER_ON(grpc_http1_trace)) + gpr_log(GPR_ERROR, "HTTP header max line length (%d) exceeded", + GRPC_HTTP_PARSER_MAX_HEADER_LENGTH); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "HTTP header max line length exceeded"); + } + parser->cur_line[parser->cur_line_length] = byte; + parser->cur_line_length++; + if (check_line(parser)) { + return finish_line(parser, found_body_start); + } + return GRPC_ERROR_NONE; + case GRPC_HTTP_BODY: + return addbyte_body(parser, byte); + } + GPR_UNREACHABLE_CODE(return GRPC_ERROR_NONE); +} + +void grpc_http_parser_init(grpc_http_parser *parser, grpc_http_type type, + void *request_or_response) { + memset(parser, 0, sizeof(*parser)); + parser->state = GRPC_HTTP_FIRST_LINE; + parser->type = type; + parser->http.request_or_response = request_or_response; + parser->cur_line_end_length = 2; +} + +void grpc_http_parser_destroy(grpc_http_parser *parser) {} + +void grpc_http_request_destroy(grpc_http_request *request) { + size_t i; + gpr_free(request->body); + for (i = 0; i < request->hdr_count; i++) { + gpr_free(request->hdrs[i].key); + gpr_free(request->hdrs[i].value); + } + gpr_free(request->hdrs); + gpr_free(request->method); + gpr_free(request->path); +} + +void grpc_http_response_destroy(grpc_http_response *response) { + size_t i; + gpr_free(response->body); + for (i = 0; i < response->hdr_count; i++) { + gpr_free(response->hdrs[i].key); + gpr_free(response->hdrs[i].value); + } + gpr_free(response->hdrs); +} + +grpc_error *grpc_http_parser_parse(grpc_http_parser *parser, grpc_slice slice, + size_t *start_of_body) { + for (size_t i = 0; i < GRPC_SLICE_LENGTH(slice); i++) { + bool found_body_start = false; + grpc_error *err = + addbyte(parser, GRPC_SLICE_START_PTR(slice)[i], &found_body_start); + if (err != GRPC_ERROR_NONE) return err; + if (found_body_start && start_of_body != NULL) *start_of_body = i + 1; + } + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_http_parser_eof(grpc_http_parser *parser) { + if (parser->state != GRPC_HTTP_BODY) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Did not finish headers"); + } + return GRPC_ERROR_NONE; +} diff --git a/src/core/lib/iomgr/call_combiner.c b/src/core/lib/iomgr/call_combiner.c deleted file mode 100644 index 48d8eaec18..0000000000 --- a/src/core/lib/iomgr/call_combiner.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/call_combiner.h" - -#include - -grpc_tracer_flag grpc_call_combiner_trace = - GRPC_TRACER_INITIALIZER(false, "call_combiner"); - -static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) { - if (cancel_state & 1) { - return (grpc_error*)(cancel_state & ~(gpr_atm)1); - } - return GRPC_ERROR_NONE; -} - -static gpr_atm encode_cancel_state_error(grpc_error* error) { - return (gpr_atm)1 | (gpr_atm)error; -} - -void grpc_call_combiner_init(grpc_call_combiner* call_combiner) { - gpr_mpscq_init(&call_combiner->queue); -} - -void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner) { - gpr_mpscq_destroy(&call_combiner->queue); - GRPC_ERROR_UNREF(decode_cancel_state_error(call_combiner->cancel_state)); -} - -#ifndef NDEBUG -#define DEBUG_ARGS , const char *file, int line -#define DEBUG_FMT_STR "%s:%d: " -#define DEBUG_FMT_ARGS , file, line -#else -#define DEBUG_ARGS -#define DEBUG_FMT_STR -#define DEBUG_FMT_ARGS -#endif - -void grpc_call_combiner_start(grpc_exec_ctx* exec_ctx, - grpc_call_combiner* call_combiner, - grpc_closure* closure, - grpc_error* error DEBUG_ARGS, - const char* reason) { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, - "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR - "%s] error=%s", - call_combiner, closure DEBUG_FMT_ARGS, reason, - grpc_error_string(error)); - } - size_t prev_size = - (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1); - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " size: %" PRIdPTR " -> %" PRIdPTR, prev_size, - prev_size + 1); - } - if (prev_size == 0) { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " EXECUTING IMMEDIATELY"); - } - // Queue was empty, so execute this closure immediately. - GRPC_CLOSURE_SCHED(exec_ctx, closure, error); - } else { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_INFO, " QUEUING"); - } - // Queue was not empty, so add closure to queue. - closure->error_data.error = error; - gpr_mpscq_push(&call_combiner->queue, (gpr_mpscq_node*)closure); - } -} - -void grpc_call_combiner_stop(grpc_exec_ctx* exec_ctx, - grpc_call_combiner* call_combiner DEBUG_ARGS, - const char* reason) { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, - "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]", - call_combiner DEBUG_FMT_ARGS, reason); - } - size_t prev_size = - (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1); - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " size: %" PRIdPTR " -> %" PRIdPTR, prev_size, - prev_size - 1); - } - GPR_ASSERT(prev_size >= 1); - if (prev_size > 1) { - while (true) { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " checking queue"); - } - bool empty; - grpc_closure* closure = (grpc_closure*)gpr_mpscq_pop_and_check_end( - &call_combiner->queue, &empty); - if (closure == NULL) { - // This can happen either due to a race condition within the mpscq - // code or because of a race with grpc_call_combiner_start(). - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " queue returned no result; checking again"); - } - continue; - } - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " EXECUTING FROM QUEUE: closure=%p error=%s", - closure, grpc_error_string(closure->error_data.error)); - } - GRPC_CLOSURE_SCHED(exec_ctx, closure, closure->error_data.error); - break; - } - } else if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, " queue empty"); - } -} - -void grpc_call_combiner_set_notify_on_cancel(grpc_exec_ctx* exec_ctx, - grpc_call_combiner* call_combiner, - grpc_closure* closure) { - while (true) { - // Decode original state. - gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state); - grpc_error* original_error = decode_cancel_state_error(original_state); - // If error is set, invoke the cancellation closure immediately. - // Otherwise, store the new closure. - if (original_error != GRPC_ERROR_NONE) { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, - "call_combiner=%p: scheduling notify_on_cancel callback=%p " - "for pre-existing cancellation", - call_combiner, closure); - } - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_REF(original_error)); - break; - } else { - if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state, - (gpr_atm)closure)) { - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, "call_combiner=%p: setting notify_on_cancel=%p", - call_combiner, closure); - } - // If we replaced an earlier closure, invoke the original - // closure with GRPC_ERROR_NONE. This allows callers to clean - // up any resources they may be holding for the callback. - if (original_state != 0) { - closure = (grpc_closure*)original_state; - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, - "call_combiner=%p: scheduling old cancel callback=%p", - call_combiner, closure); - } - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); - } - break; - } - } - // cas failed, try again. - } -} - -void grpc_call_combiner_cancel(grpc_exec_ctx* exec_ctx, - grpc_call_combiner* call_combiner, - grpc_error* error) { - while (true) { - gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state); - grpc_error* original_error = decode_cancel_state_error(original_state); - if (original_error != GRPC_ERROR_NONE) { - GRPC_ERROR_UNREF(error); - break; - } - if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state, - encode_cancel_state_error(error))) { - if (original_state != 0) { - grpc_closure* notify_on_cancel = (grpc_closure*)original_state; - if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { - gpr_log(GPR_DEBUG, - "call_combiner=%p: scheduling notify_on_cancel callback=%p", - call_combiner, notify_on_cancel); - } - GRPC_CLOSURE_SCHED(exec_ctx, notify_on_cancel, GRPC_ERROR_REF(error)); - } - break; - } - // cas failed, try again. - } -} diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc new file mode 100644 index 0000000000..48d8eaec18 --- /dev/null +++ b/src/core/lib/iomgr/call_combiner.cc @@ -0,0 +1,202 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/call_combiner.h" + +#include + +grpc_tracer_flag grpc_call_combiner_trace = + GRPC_TRACER_INITIALIZER(false, "call_combiner"); + +static grpc_error* decode_cancel_state_error(gpr_atm cancel_state) { + if (cancel_state & 1) { + return (grpc_error*)(cancel_state & ~(gpr_atm)1); + } + return GRPC_ERROR_NONE; +} + +static gpr_atm encode_cancel_state_error(grpc_error* error) { + return (gpr_atm)1 | (gpr_atm)error; +} + +void grpc_call_combiner_init(grpc_call_combiner* call_combiner) { + gpr_mpscq_init(&call_combiner->queue); +} + +void grpc_call_combiner_destroy(grpc_call_combiner* call_combiner) { + gpr_mpscq_destroy(&call_combiner->queue); + GRPC_ERROR_UNREF(decode_cancel_state_error(call_combiner->cancel_state)); +} + +#ifndef NDEBUG +#define DEBUG_ARGS , const char *file, int line +#define DEBUG_FMT_STR "%s:%d: " +#define DEBUG_FMT_ARGS , file, line +#else +#define DEBUG_ARGS +#define DEBUG_FMT_STR +#define DEBUG_FMT_ARGS +#endif + +void grpc_call_combiner_start(grpc_exec_ctx* exec_ctx, + grpc_call_combiner* call_combiner, + grpc_closure* closure, + grpc_error* error DEBUG_ARGS, + const char* reason) { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, + "==> grpc_call_combiner_start() [%p] closure=%p [" DEBUG_FMT_STR + "%s] error=%s", + call_combiner, closure DEBUG_FMT_ARGS, reason, + grpc_error_string(error)); + } + size_t prev_size = + (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)1); + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " size: %" PRIdPTR " -> %" PRIdPTR, prev_size, + prev_size + 1); + } + if (prev_size == 0) { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " EXECUTING IMMEDIATELY"); + } + // Queue was empty, so execute this closure immediately. + GRPC_CLOSURE_SCHED(exec_ctx, closure, error); + } else { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_INFO, " QUEUING"); + } + // Queue was not empty, so add closure to queue. + closure->error_data.error = error; + gpr_mpscq_push(&call_combiner->queue, (gpr_mpscq_node*)closure); + } +} + +void grpc_call_combiner_stop(grpc_exec_ctx* exec_ctx, + grpc_call_combiner* call_combiner DEBUG_ARGS, + const char* reason) { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, + "==> grpc_call_combiner_stop() [%p] [" DEBUG_FMT_STR "%s]", + call_combiner DEBUG_FMT_ARGS, reason); + } + size_t prev_size = + (size_t)gpr_atm_full_fetch_add(&call_combiner->size, (gpr_atm)-1); + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " size: %" PRIdPTR " -> %" PRIdPTR, prev_size, + prev_size - 1); + } + GPR_ASSERT(prev_size >= 1); + if (prev_size > 1) { + while (true) { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " checking queue"); + } + bool empty; + grpc_closure* closure = (grpc_closure*)gpr_mpscq_pop_and_check_end( + &call_combiner->queue, &empty); + if (closure == NULL) { + // This can happen either due to a race condition within the mpscq + // code or because of a race with grpc_call_combiner_start(). + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " queue returned no result; checking again"); + } + continue; + } + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " EXECUTING FROM QUEUE: closure=%p error=%s", + closure, grpc_error_string(closure->error_data.error)); + } + GRPC_CLOSURE_SCHED(exec_ctx, closure, closure->error_data.error); + break; + } + } else if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, " queue empty"); + } +} + +void grpc_call_combiner_set_notify_on_cancel(grpc_exec_ctx* exec_ctx, + grpc_call_combiner* call_combiner, + grpc_closure* closure) { + while (true) { + // Decode original state. + gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state); + grpc_error* original_error = decode_cancel_state_error(original_state); + // If error is set, invoke the cancellation closure immediately. + // Otherwise, store the new closure. + if (original_error != GRPC_ERROR_NONE) { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, + "call_combiner=%p: scheduling notify_on_cancel callback=%p " + "for pre-existing cancellation", + call_combiner, closure); + } + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_REF(original_error)); + break; + } else { + if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state, + (gpr_atm)closure)) { + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, "call_combiner=%p: setting notify_on_cancel=%p", + call_combiner, closure); + } + // If we replaced an earlier closure, invoke the original + // closure with GRPC_ERROR_NONE. This allows callers to clean + // up any resources they may be holding for the callback. + if (original_state != 0) { + closure = (grpc_closure*)original_state; + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, + "call_combiner=%p: scheduling old cancel callback=%p", + call_combiner, closure); + } + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); + } + break; + } + } + // cas failed, try again. + } +} + +void grpc_call_combiner_cancel(grpc_exec_ctx* exec_ctx, + grpc_call_combiner* call_combiner, + grpc_error* error) { + while (true) { + gpr_atm original_state = gpr_atm_acq_load(&call_combiner->cancel_state); + grpc_error* original_error = decode_cancel_state_error(original_state); + if (original_error != GRPC_ERROR_NONE) { + GRPC_ERROR_UNREF(error); + break; + } + if (gpr_atm_full_cas(&call_combiner->cancel_state, original_state, + encode_cancel_state_error(error))) { + if (original_state != 0) { + grpc_closure* notify_on_cancel = (grpc_closure*)original_state; + if (GRPC_TRACER_ON(grpc_call_combiner_trace)) { + gpr_log(GPR_DEBUG, + "call_combiner=%p: scheduling notify_on_cancel callback=%p", + call_combiner, notify_on_cancel); + } + GRPC_CLOSURE_SCHED(exec_ctx, notify_on_cancel, GRPC_ERROR_REF(error)); + } + break; + } + // cas failed, try again. + } +} diff --git a/src/core/lib/iomgr/closure.c b/src/core/lib/iomgr/closure.c deleted file mode 100644 index 00edefc6ae..0000000000 --- a/src/core/lib/iomgr/closure.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/closure.h" - -#include -#include -#include - -#include "src/core/lib/profiling/timers.h" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_closure = GRPC_TRACER_INITIALIZER(false, "closure"); -#endif - -#ifndef NDEBUG -grpc_closure *grpc_closure_init(const char *file, int line, - grpc_closure *closure, grpc_iomgr_cb_func cb, - void *cb_arg, - grpc_closure_scheduler *scheduler) { -#else -grpc_closure *grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, - void *cb_arg, - grpc_closure_scheduler *scheduler) { -#endif - closure->cb = cb; - closure->cb_arg = cb_arg; - closure->scheduler = scheduler; -#ifndef NDEBUG - closure->scheduled = false; - closure->file_initiated = NULL; - closure->line_initiated = 0; - closure->run = false; - closure->file_created = file; - closure->line_created = line; -#endif - return closure; -} - -void grpc_closure_list_init(grpc_closure_list *closure_list) { - closure_list->head = closure_list->tail = NULL; -} - -bool grpc_closure_list_append(grpc_closure_list *closure_list, - grpc_closure *closure, grpc_error *error) { - if (closure == NULL) { - GRPC_ERROR_UNREF(error); - return false; - } - closure->error_data.error = error; - closure->next_data.next = NULL; - bool was_empty = (closure_list->head == NULL); - if (was_empty) { - closure_list->head = closure; - } else { - closure_list->tail->next_data.next = closure; - } - closure_list->tail = closure; - return was_empty; -} - -void grpc_closure_list_fail_all(grpc_closure_list *list, - grpc_error *forced_failure) { - for (grpc_closure *c = list->head; c != NULL; c = c->next_data.next) { - if (c->error_data.error == GRPC_ERROR_NONE) { - c->error_data.error = GRPC_ERROR_REF(forced_failure); - } - } - GRPC_ERROR_UNREF(forced_failure); -} - -bool grpc_closure_list_empty(grpc_closure_list closure_list) { - return closure_list.head == NULL; -} - -void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst) { - if (src->head == NULL) { - return; - } - if (dst->head == NULL) { - *dst = *src; - } else { - dst->tail->next_data.next = src->head; - dst->tail = src->tail; - } - src->head = src->tail = NULL; -} - -typedef struct { - grpc_iomgr_cb_func cb; - void *cb_arg; - grpc_closure wrapper; -} wrapped_closure; - -static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - wrapped_closure *wc = (wrapped_closure *)arg; - grpc_iomgr_cb_func cb = wc->cb; - void *cb_arg = wc->cb_arg; - gpr_free(wc); - cb(exec_ctx, cb_arg, error); -} - -#ifndef NDEBUG -grpc_closure *grpc_closure_create(const char *file, int line, - grpc_iomgr_cb_func cb, void *cb_arg, - grpc_closure_scheduler *scheduler) { -#else -grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg, - grpc_closure_scheduler *scheduler) { -#endif - wrapped_closure *wc = (wrapped_closure *)gpr_malloc(sizeof(*wc)); - wc->cb = cb; - wc->cb_arg = cb_arg; -#ifndef NDEBUG - grpc_closure_init(file, line, &wc->wrapper, closure_wrapper, wc, scheduler); -#else - grpc_closure_init(&wc->wrapper, closure_wrapper, wc, scheduler); -#endif - return &wc->wrapper; -} - -#ifndef NDEBUG -void grpc_closure_run(const char *file, int line, grpc_exec_ctx *exec_ctx, - grpc_closure *c, grpc_error *error) { -#else -void grpc_closure_run(grpc_exec_ctx *exec_ctx, grpc_closure *c, - grpc_error *error) { -#endif - GPR_TIMER_BEGIN("grpc_closure_run", 0); - if (c != NULL) { -#ifndef NDEBUG - c->file_initiated = file; - c->line_initiated = line; - c->run = true; -#endif - assert(c->cb); - c->scheduler->vtable->run(exec_ctx, c, error); - } else { - GRPC_ERROR_UNREF(error); - } - GPR_TIMER_END("grpc_closure_run", 0); -} - -#ifndef NDEBUG -void grpc_closure_sched(const char *file, int line, grpc_exec_ctx *exec_ctx, - grpc_closure *c, grpc_error *error) { -#else -void grpc_closure_sched(grpc_exec_ctx *exec_ctx, grpc_closure *c, - grpc_error *error) { -#endif - GPR_TIMER_BEGIN("grpc_closure_sched", 0); - if (c != NULL) { -#ifndef NDEBUG - if (c->scheduled) { - gpr_log(GPR_ERROR, - "Closure already scheduled. (closure: %p, created: [%s:%d], " - "previously scheduled at: [%s: %d] run?: %s", - c, c->file_created, c->line_created, c->file_initiated, - c->line_initiated, c->run ? "true" : "false"); - abort(); - } - c->scheduled = true; - c->file_initiated = file; - c->line_initiated = line; - c->run = false; -#endif - assert(c->cb); - c->scheduler->vtable->sched(exec_ctx, c, error); - } else { - GRPC_ERROR_UNREF(error); - } - GPR_TIMER_END("grpc_closure_sched", 0); -} - -#ifndef NDEBUG -void grpc_closure_list_sched(const char *file, int line, - grpc_exec_ctx *exec_ctx, grpc_closure_list *list) { -#else -void grpc_closure_list_sched(grpc_exec_ctx *exec_ctx, grpc_closure_list *list) { -#endif - grpc_closure *c = list->head; - while (c != NULL) { - grpc_closure *next = c->next_data.next; -#ifndef NDEBUG - if (c->scheduled) { - gpr_log(GPR_ERROR, - "Closure already scheduled. (closure: %p, created: [%s:%d], " - "previously scheduled at: [%s: %d] run?: %s", - c, c->file_created, c->line_created, c->file_initiated, - c->line_initiated, c->run ? "true" : "false"); - abort(); - } - c->scheduled = true; - c->file_initiated = file; - c->line_initiated = line; - c->run = false; -#endif - assert(c->cb); - c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error); - c = next; - } - list->head = list->tail = NULL; -} diff --git a/src/core/lib/iomgr/closure.cc b/src/core/lib/iomgr/closure.cc new file mode 100644 index 0000000000..00edefc6ae --- /dev/null +++ b/src/core/lib/iomgr/closure.cc @@ -0,0 +1,219 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/closure.h" + +#include +#include +#include + +#include "src/core/lib/profiling/timers.h" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_closure = GRPC_TRACER_INITIALIZER(false, "closure"); +#endif + +#ifndef NDEBUG +grpc_closure *grpc_closure_init(const char *file, int line, + grpc_closure *closure, grpc_iomgr_cb_func cb, + void *cb_arg, + grpc_closure_scheduler *scheduler) { +#else +grpc_closure *grpc_closure_init(grpc_closure *closure, grpc_iomgr_cb_func cb, + void *cb_arg, + grpc_closure_scheduler *scheduler) { +#endif + closure->cb = cb; + closure->cb_arg = cb_arg; + closure->scheduler = scheduler; +#ifndef NDEBUG + closure->scheduled = false; + closure->file_initiated = NULL; + closure->line_initiated = 0; + closure->run = false; + closure->file_created = file; + closure->line_created = line; +#endif + return closure; +} + +void grpc_closure_list_init(grpc_closure_list *closure_list) { + closure_list->head = closure_list->tail = NULL; +} + +bool grpc_closure_list_append(grpc_closure_list *closure_list, + grpc_closure *closure, grpc_error *error) { + if (closure == NULL) { + GRPC_ERROR_UNREF(error); + return false; + } + closure->error_data.error = error; + closure->next_data.next = NULL; + bool was_empty = (closure_list->head == NULL); + if (was_empty) { + closure_list->head = closure; + } else { + closure_list->tail->next_data.next = closure; + } + closure_list->tail = closure; + return was_empty; +} + +void grpc_closure_list_fail_all(grpc_closure_list *list, + grpc_error *forced_failure) { + for (grpc_closure *c = list->head; c != NULL; c = c->next_data.next) { + if (c->error_data.error == GRPC_ERROR_NONE) { + c->error_data.error = GRPC_ERROR_REF(forced_failure); + } + } + GRPC_ERROR_UNREF(forced_failure); +} + +bool grpc_closure_list_empty(grpc_closure_list closure_list) { + return closure_list.head == NULL; +} + +void grpc_closure_list_move(grpc_closure_list *src, grpc_closure_list *dst) { + if (src->head == NULL) { + return; + } + if (dst->head == NULL) { + *dst = *src; + } else { + dst->tail->next_data.next = src->head; + dst->tail = src->tail; + } + src->head = src->tail = NULL; +} + +typedef struct { + grpc_iomgr_cb_func cb; + void *cb_arg; + grpc_closure wrapper; +} wrapped_closure; + +static void closure_wrapper(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + wrapped_closure *wc = (wrapped_closure *)arg; + grpc_iomgr_cb_func cb = wc->cb; + void *cb_arg = wc->cb_arg; + gpr_free(wc); + cb(exec_ctx, cb_arg, error); +} + +#ifndef NDEBUG +grpc_closure *grpc_closure_create(const char *file, int line, + grpc_iomgr_cb_func cb, void *cb_arg, + grpc_closure_scheduler *scheduler) { +#else +grpc_closure *grpc_closure_create(grpc_iomgr_cb_func cb, void *cb_arg, + grpc_closure_scheduler *scheduler) { +#endif + wrapped_closure *wc = (wrapped_closure *)gpr_malloc(sizeof(*wc)); + wc->cb = cb; + wc->cb_arg = cb_arg; +#ifndef NDEBUG + grpc_closure_init(file, line, &wc->wrapper, closure_wrapper, wc, scheduler); +#else + grpc_closure_init(&wc->wrapper, closure_wrapper, wc, scheduler); +#endif + return &wc->wrapper; +} + +#ifndef NDEBUG +void grpc_closure_run(const char *file, int line, grpc_exec_ctx *exec_ctx, + grpc_closure *c, grpc_error *error) { +#else +void grpc_closure_run(grpc_exec_ctx *exec_ctx, grpc_closure *c, + grpc_error *error) { +#endif + GPR_TIMER_BEGIN("grpc_closure_run", 0); + if (c != NULL) { +#ifndef NDEBUG + c->file_initiated = file; + c->line_initiated = line; + c->run = true; +#endif + assert(c->cb); + c->scheduler->vtable->run(exec_ctx, c, error); + } else { + GRPC_ERROR_UNREF(error); + } + GPR_TIMER_END("grpc_closure_run", 0); +} + +#ifndef NDEBUG +void grpc_closure_sched(const char *file, int line, grpc_exec_ctx *exec_ctx, + grpc_closure *c, grpc_error *error) { +#else +void grpc_closure_sched(grpc_exec_ctx *exec_ctx, grpc_closure *c, + grpc_error *error) { +#endif + GPR_TIMER_BEGIN("grpc_closure_sched", 0); + if (c != NULL) { +#ifndef NDEBUG + if (c->scheduled) { + gpr_log(GPR_ERROR, + "Closure already scheduled. (closure: %p, created: [%s:%d], " + "previously scheduled at: [%s: %d] run?: %s", + c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated, c->run ? "true" : "false"); + abort(); + } + c->scheduled = true; + c->file_initiated = file; + c->line_initiated = line; + c->run = false; +#endif + assert(c->cb); + c->scheduler->vtable->sched(exec_ctx, c, error); + } else { + GRPC_ERROR_UNREF(error); + } + GPR_TIMER_END("grpc_closure_sched", 0); +} + +#ifndef NDEBUG +void grpc_closure_list_sched(const char *file, int line, + grpc_exec_ctx *exec_ctx, grpc_closure_list *list) { +#else +void grpc_closure_list_sched(grpc_exec_ctx *exec_ctx, grpc_closure_list *list) { +#endif + grpc_closure *c = list->head; + while (c != NULL) { + grpc_closure *next = c->next_data.next; +#ifndef NDEBUG + if (c->scheduled) { + gpr_log(GPR_ERROR, + "Closure already scheduled. (closure: %p, created: [%s:%d], " + "previously scheduled at: [%s: %d] run?: %s", + c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated, c->run ? "true" : "false"); + abort(); + } + c->scheduled = true; + c->file_initiated = file; + c->line_initiated = line; + c->run = false; +#endif + assert(c->cb); + c->scheduler->vtable->sched(exec_ctx, c, c->error_data.error); + c = next; + } + list->head = list->tail = NULL; +} diff --git a/src/core/lib/iomgr/combiner.c b/src/core/lib/iomgr/combiner.c deleted file mode 100644 index f899b25f10..0000000000 --- a/src/core/lib/iomgr/combiner.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/combiner.h" - -#include -#include - -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/profiling/timers.h" - -grpc_tracer_flag grpc_combiner_trace = - GRPC_TRACER_INITIALIZER(false, "combiner"); - -#define GRPC_COMBINER_TRACE(fn) \ - do { \ - if (GRPC_TRACER_ON(grpc_combiner_trace)) { \ - fn; \ - } \ - } while (0) - -#define STATE_UNORPHANED 1 -#define STATE_ELEM_COUNT_LOW_BIT 2 - -struct grpc_combiner { - grpc_combiner *next_combiner_on_this_exec_ctx; - grpc_closure_scheduler scheduler; - grpc_closure_scheduler finally_scheduler; - gpr_mpscq queue; - // either: - // a pointer to the initiating exec ctx if that is the only exec_ctx that has - // ever queued to this combiner, or NULL. If this is non-null, it's not - // dereferencable (since the initiating exec_ctx may have gone out of scope) - gpr_atm initiating_exec_ctx_or_null; - // state is: - // lower bit - zero if orphaned (STATE_UNORPHANED) - // other bits - number of items queued on the lock (STATE_ELEM_COUNT_LOW_BIT) - gpr_atm state; - bool time_to_execute_final_list; - grpc_closure_list final_list; - grpc_closure offload; - gpr_refcount refs; -}; - -static void combiner_exec(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_error *error); -static void combiner_finally_exec(grpc_exec_ctx *exec_ctx, - grpc_closure *closure, grpc_error *error); - -static const grpc_closure_scheduler_vtable scheduler = { - combiner_exec, combiner_exec, "combiner:immediately"}; -static const grpc_closure_scheduler_vtable finally_scheduler = { - combiner_finally_exec, combiner_finally_exec, "combiner:finally"}; - -static void offload(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error); - -grpc_combiner *grpc_combiner_create(void) { - grpc_combiner *lock = (grpc_combiner *)gpr_zalloc(sizeof(*lock)); - gpr_ref_init(&lock->refs, 1); - lock->scheduler.vtable = &scheduler; - lock->finally_scheduler.vtable = &finally_scheduler; - gpr_atm_no_barrier_store(&lock->state, STATE_UNORPHANED); - gpr_mpscq_init(&lock->queue); - grpc_closure_list_init(&lock->final_list); - GRPC_CLOSURE_INIT(&lock->offload, offload, lock, - grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); - GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p create", lock)); - return lock; -} - -static void really_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) { - GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p really_destroy", lock)); - GPR_ASSERT(gpr_atm_no_barrier_load(&lock->state) == 0); - gpr_mpscq_destroy(&lock->queue); - gpr_free(lock); -} - -static void start_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) { - gpr_atm old_state = gpr_atm_full_fetch_add(&lock->state, -STATE_UNORPHANED); - GRPC_COMBINER_TRACE(gpr_log( - GPR_DEBUG, "C:%p really_destroy old_state=%" PRIdPTR, lock, old_state)); - if (old_state == 1) { - really_destroy(exec_ctx, lock); - } -} - -#ifndef NDEBUG -#define GRPC_COMBINER_DEBUG_SPAM(op, delta) \ - if (GRPC_TRACER_ON(grpc_combiner_trace)) { \ - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, \ - "C:%p %s %" PRIdPTR " --> %" PRIdPTR " %s", lock, (op), \ - gpr_atm_no_barrier_load(&lock->refs.count), \ - gpr_atm_no_barrier_load(&lock->refs.count) + (delta), reason); \ - } -#else -#define GRPC_COMBINER_DEBUG_SPAM(op, delta) -#endif - -void grpc_combiner_unref(grpc_exec_ctx *exec_ctx, - grpc_combiner *lock GRPC_COMBINER_DEBUG_ARGS) { - GRPC_COMBINER_DEBUG_SPAM("UNREF", -1); - if (gpr_unref(&lock->refs)) { - start_destroy(exec_ctx, lock); - } -} - -grpc_combiner *grpc_combiner_ref(grpc_combiner *lock GRPC_COMBINER_DEBUG_ARGS) { - GRPC_COMBINER_DEBUG_SPAM(" REF", 1); - gpr_ref(&lock->refs); - return lock; -} - -static void push_last_on_exec_ctx(grpc_exec_ctx *exec_ctx, - grpc_combiner *lock) { - lock->next_combiner_on_this_exec_ctx = NULL; - if (exec_ctx->active_combiner == NULL) { - exec_ctx->active_combiner = exec_ctx->last_combiner = lock; - } else { - exec_ctx->last_combiner->next_combiner_on_this_exec_ctx = lock; - exec_ctx->last_combiner = lock; - } -} - -static void push_first_on_exec_ctx(grpc_exec_ctx *exec_ctx, - grpc_combiner *lock) { - lock->next_combiner_on_this_exec_ctx = exec_ctx->active_combiner; - exec_ctx->active_combiner = lock; - if (lock->next_combiner_on_this_exec_ctx == NULL) { - exec_ctx->last_combiner = lock; - } -} - -#define COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler_name) \ - ((grpc_combiner *)(((char *)((closure)->scheduler)) - \ - offsetof(grpc_combiner, scheduler_name))) - -static void combiner_exec(grpc_exec_ctx *exec_ctx, grpc_closure *cl, - grpc_error *error) { - GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS(exec_ctx); - GPR_TIMER_BEGIN("combiner.execute", 0); - grpc_combiner *lock = COMBINER_FROM_CLOSURE_SCHEDULER(cl, scheduler); - gpr_atm last = gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT); - GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, - "C:%p grpc_combiner_execute c=%p last=%" PRIdPTR, - lock, cl, last)); - if (last == 1) { - GRPC_STATS_INC_COMBINER_LOCKS_INITIATED(exec_ctx); - gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, - (gpr_atm)exec_ctx); - // first element on this list: add it to the list of combiner locks - // executing within this exec_ctx - push_last_on_exec_ctx(exec_ctx, lock); - } else { - // there may be a race with setting here: if that happens, we may delay - // offload for one or two actions, and that's fine - gpr_atm initiator = - gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null); - if (initiator != 0 && initiator != (gpr_atm)exec_ctx) { - gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, 0); - } - } - GPR_ASSERT(last & STATE_UNORPHANED); // ensure lock has not been destroyed - assert(cl->cb); - cl->error_data.error = error; - gpr_mpscq_push(&lock->queue, &cl->next_data.atm_next); - GPR_TIMER_END("combiner.execute", 0); -} - -static void move_next(grpc_exec_ctx *exec_ctx) { - exec_ctx->active_combiner = - exec_ctx->active_combiner->next_combiner_on_this_exec_ctx; - if (exec_ctx->active_combiner == NULL) { - exec_ctx->last_combiner = NULL; - } -} - -static void offload(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_combiner *lock = (grpc_combiner *)arg; - push_last_on_exec_ctx(exec_ctx, lock); -} - -static void queue_offload(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) { - GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED(exec_ctx); - move_next(exec_ctx); - GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p queue_offload", lock)); - GRPC_CLOSURE_SCHED(exec_ctx, &lock->offload, GRPC_ERROR_NONE); -} - -bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx) { - GPR_TIMER_BEGIN("combiner.continue_exec_ctx", 0); - grpc_combiner *lock = exec_ctx->active_combiner; - if (lock == NULL) { - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - return false; - } - - bool contended = - gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null) == 0; - - GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, - "C:%p grpc_combiner_continue_exec_ctx " - "contended=%d " - "exec_ctx_ready_to_finish=%d " - "time_to_execute_final_list=%d", - lock, contended, - grpc_exec_ctx_ready_to_finish(exec_ctx), - lock->time_to_execute_final_list)); - - if (contended && grpc_exec_ctx_ready_to_finish(exec_ctx) && - grpc_executor_is_threaded()) { - GPR_TIMER_MARK("offload_from_finished_exec_ctx", 0); - // this execution context wants to move on: schedule remaining work to be - // picked up on the executor - queue_offload(exec_ctx, lock); - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - return true; - } - - if (!lock->time_to_execute_final_list || - // peek to see if something new has shown up, and execute that with - // priority - (gpr_atm_acq_load(&lock->state) >> 1) > 1) { - gpr_mpscq_node *n = gpr_mpscq_pop(&lock->queue); - GRPC_COMBINER_TRACE( - gpr_log(GPR_DEBUG, "C:%p maybe_finish_one n=%p", lock, n)); - if (n == NULL) { - // queue is in an inconsistent state: use this as a cue that we should - // go off and do something else for a while (and come back later) - GPR_TIMER_MARK("delay_busy", 0); - queue_offload(exec_ctx, lock); - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - return true; - } - GPR_TIMER_BEGIN("combiner.exec1", 0); - grpc_closure *cl = (grpc_closure *)n; - grpc_error *cl_err = cl->error_data.error; -#ifndef NDEBUG - cl->scheduled = false; -#endif - cl->cb(exec_ctx, cl->cb_arg, cl_err); - GRPC_ERROR_UNREF(cl_err); - GPR_TIMER_END("combiner.exec1", 0); - } else { - grpc_closure *c = lock->final_list.head; - GPR_ASSERT(c != NULL); - grpc_closure_list_init(&lock->final_list); - int loops = 0; - while (c != NULL) { - GPR_TIMER_BEGIN("combiner.exec_1final", 0); - GRPC_COMBINER_TRACE( - gpr_log(GPR_DEBUG, "C:%p execute_final[%d] c=%p", lock, loops, c)); - grpc_closure *next = c->next_data.next; - grpc_error *error = c->error_data.error; -#ifndef NDEBUG - c->scheduled = false; -#endif - c->cb(exec_ctx, c->cb_arg, error); - GRPC_ERROR_UNREF(error); - c = next; - GPR_TIMER_END("combiner.exec_1final", 0); - } - } - - GPR_TIMER_MARK("unref", 0); - move_next(exec_ctx); - lock->time_to_execute_final_list = false; - gpr_atm old_state = - gpr_atm_full_fetch_add(&lock->state, -STATE_ELEM_COUNT_LOW_BIT); - GRPC_COMBINER_TRACE( - gpr_log(GPR_DEBUG, "C:%p finish old_state=%" PRIdPTR, lock, old_state)); -// Define a macro to ease readability of the following switch statement. -#define OLD_STATE_WAS(orphaned, elem_count) \ - (((orphaned) ? 0 : STATE_UNORPHANED) | \ - ((elem_count)*STATE_ELEM_COUNT_LOW_BIT)) - // Depending on what the previous state was, we need to perform different - // actions. - switch (old_state) { - default: - // we have multiple queued work items: just continue executing them - break; - case OLD_STATE_WAS(false, 2): - case OLD_STATE_WAS(true, 2): - // we're down to one queued item: if it's the final list we should do that - if (!grpc_closure_list_empty(lock->final_list)) { - lock->time_to_execute_final_list = true; - } - break; - case OLD_STATE_WAS(false, 1): - // had one count, one unorphaned --> unlocked unorphaned - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - return true; - case OLD_STATE_WAS(true, 1): - // and one count, one orphaned --> unlocked and orphaned - really_destroy(exec_ctx, lock); - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - return true; - case OLD_STATE_WAS(false, 0): - case OLD_STATE_WAS(true, 0): - // these values are illegal - representing an already unlocked or - // deleted lock - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - GPR_UNREACHABLE_CODE(return true); - } - push_first_on_exec_ctx(exec_ctx, lock); - GPR_TIMER_END("combiner.continue_exec_ctx", 0); - return true; -} - -static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure, - grpc_error *error); - -static void combiner_finally_exec(grpc_exec_ctx *exec_ctx, - grpc_closure *closure, grpc_error *error) { - GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS(exec_ctx); - grpc_combiner *lock = - COMBINER_FROM_CLOSURE_SCHEDULER(closure, finally_scheduler); - GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, - "C:%p grpc_combiner_execute_finally c=%p; ac=%p", - lock, closure, exec_ctx->active_combiner)); - GPR_TIMER_BEGIN("combiner.execute_finally", 0); - if (exec_ctx->active_combiner != lock) { - GPR_TIMER_MARK("slowpath", 0); - GRPC_CLOSURE_SCHED(exec_ctx, - GRPC_CLOSURE_CREATE(enqueue_finally, closure, - grpc_combiner_scheduler(lock)), - error); - GPR_TIMER_END("combiner.execute_finally", 0); - return; - } - - if (grpc_closure_list_empty(lock->final_list)) { - gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT); - } - grpc_closure_list_append(&lock->final_list, closure, error); - GPR_TIMER_END("combiner.execute_finally", 0); -} - -static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure, - grpc_error *error) { - combiner_finally_exec(exec_ctx, (grpc_closure *)closure, - GRPC_ERROR_REF(error)); -} - -grpc_closure_scheduler *grpc_combiner_scheduler(grpc_combiner *combiner) { - return &combiner->scheduler; -} - -grpc_closure_scheduler *grpc_combiner_finally_scheduler( - grpc_combiner *combiner) { - return &combiner->finally_scheduler; -} diff --git a/src/core/lib/iomgr/combiner.cc b/src/core/lib/iomgr/combiner.cc new file mode 100644 index 0000000000..f899b25f10 --- /dev/null +++ b/src/core/lib/iomgr/combiner.cc @@ -0,0 +1,370 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/combiner.h" + +#include +#include + +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/profiling/timers.h" + +grpc_tracer_flag grpc_combiner_trace = + GRPC_TRACER_INITIALIZER(false, "combiner"); + +#define GRPC_COMBINER_TRACE(fn) \ + do { \ + if (GRPC_TRACER_ON(grpc_combiner_trace)) { \ + fn; \ + } \ + } while (0) + +#define STATE_UNORPHANED 1 +#define STATE_ELEM_COUNT_LOW_BIT 2 + +struct grpc_combiner { + grpc_combiner *next_combiner_on_this_exec_ctx; + grpc_closure_scheduler scheduler; + grpc_closure_scheduler finally_scheduler; + gpr_mpscq queue; + // either: + // a pointer to the initiating exec ctx if that is the only exec_ctx that has + // ever queued to this combiner, or NULL. If this is non-null, it's not + // dereferencable (since the initiating exec_ctx may have gone out of scope) + gpr_atm initiating_exec_ctx_or_null; + // state is: + // lower bit - zero if orphaned (STATE_UNORPHANED) + // other bits - number of items queued on the lock (STATE_ELEM_COUNT_LOW_BIT) + gpr_atm state; + bool time_to_execute_final_list; + grpc_closure_list final_list; + grpc_closure offload; + gpr_refcount refs; +}; + +static void combiner_exec(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_error *error); +static void combiner_finally_exec(grpc_exec_ctx *exec_ctx, + grpc_closure *closure, grpc_error *error); + +static const grpc_closure_scheduler_vtable scheduler = { + combiner_exec, combiner_exec, "combiner:immediately"}; +static const grpc_closure_scheduler_vtable finally_scheduler = { + combiner_finally_exec, combiner_finally_exec, "combiner:finally"}; + +static void offload(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error); + +grpc_combiner *grpc_combiner_create(void) { + grpc_combiner *lock = (grpc_combiner *)gpr_zalloc(sizeof(*lock)); + gpr_ref_init(&lock->refs, 1); + lock->scheduler.vtable = &scheduler; + lock->finally_scheduler.vtable = &finally_scheduler; + gpr_atm_no_barrier_store(&lock->state, STATE_UNORPHANED); + gpr_mpscq_init(&lock->queue); + grpc_closure_list_init(&lock->final_list); + GRPC_CLOSURE_INIT(&lock->offload, offload, lock, + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); + GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p create", lock)); + return lock; +} + +static void really_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) { + GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p really_destroy", lock)); + GPR_ASSERT(gpr_atm_no_barrier_load(&lock->state) == 0); + gpr_mpscq_destroy(&lock->queue); + gpr_free(lock); +} + +static void start_destroy(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) { + gpr_atm old_state = gpr_atm_full_fetch_add(&lock->state, -STATE_UNORPHANED); + GRPC_COMBINER_TRACE(gpr_log( + GPR_DEBUG, "C:%p really_destroy old_state=%" PRIdPTR, lock, old_state)); + if (old_state == 1) { + really_destroy(exec_ctx, lock); + } +} + +#ifndef NDEBUG +#define GRPC_COMBINER_DEBUG_SPAM(op, delta) \ + if (GRPC_TRACER_ON(grpc_combiner_trace)) { \ + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, \ + "C:%p %s %" PRIdPTR " --> %" PRIdPTR " %s", lock, (op), \ + gpr_atm_no_barrier_load(&lock->refs.count), \ + gpr_atm_no_barrier_load(&lock->refs.count) + (delta), reason); \ + } +#else +#define GRPC_COMBINER_DEBUG_SPAM(op, delta) +#endif + +void grpc_combiner_unref(grpc_exec_ctx *exec_ctx, + grpc_combiner *lock GRPC_COMBINER_DEBUG_ARGS) { + GRPC_COMBINER_DEBUG_SPAM("UNREF", -1); + if (gpr_unref(&lock->refs)) { + start_destroy(exec_ctx, lock); + } +} + +grpc_combiner *grpc_combiner_ref(grpc_combiner *lock GRPC_COMBINER_DEBUG_ARGS) { + GRPC_COMBINER_DEBUG_SPAM(" REF", 1); + gpr_ref(&lock->refs); + return lock; +} + +static void push_last_on_exec_ctx(grpc_exec_ctx *exec_ctx, + grpc_combiner *lock) { + lock->next_combiner_on_this_exec_ctx = NULL; + if (exec_ctx->active_combiner == NULL) { + exec_ctx->active_combiner = exec_ctx->last_combiner = lock; + } else { + exec_ctx->last_combiner->next_combiner_on_this_exec_ctx = lock; + exec_ctx->last_combiner = lock; + } +} + +static void push_first_on_exec_ctx(grpc_exec_ctx *exec_ctx, + grpc_combiner *lock) { + lock->next_combiner_on_this_exec_ctx = exec_ctx->active_combiner; + exec_ctx->active_combiner = lock; + if (lock->next_combiner_on_this_exec_ctx == NULL) { + exec_ctx->last_combiner = lock; + } +} + +#define COMBINER_FROM_CLOSURE_SCHEDULER(closure, scheduler_name) \ + ((grpc_combiner *)(((char *)((closure)->scheduler)) - \ + offsetof(grpc_combiner, scheduler_name))) + +static void combiner_exec(grpc_exec_ctx *exec_ctx, grpc_closure *cl, + grpc_error *error) { + GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_ITEMS(exec_ctx); + GPR_TIMER_BEGIN("combiner.execute", 0); + grpc_combiner *lock = COMBINER_FROM_CLOSURE_SCHEDULER(cl, scheduler); + gpr_atm last = gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT); + GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, + "C:%p grpc_combiner_execute c=%p last=%" PRIdPTR, + lock, cl, last)); + if (last == 1) { + GRPC_STATS_INC_COMBINER_LOCKS_INITIATED(exec_ctx); + gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, + (gpr_atm)exec_ctx); + // first element on this list: add it to the list of combiner locks + // executing within this exec_ctx + push_last_on_exec_ctx(exec_ctx, lock); + } else { + // there may be a race with setting here: if that happens, we may delay + // offload for one or two actions, and that's fine + gpr_atm initiator = + gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null); + if (initiator != 0 && initiator != (gpr_atm)exec_ctx) { + gpr_atm_no_barrier_store(&lock->initiating_exec_ctx_or_null, 0); + } + } + GPR_ASSERT(last & STATE_UNORPHANED); // ensure lock has not been destroyed + assert(cl->cb); + cl->error_data.error = error; + gpr_mpscq_push(&lock->queue, &cl->next_data.atm_next); + GPR_TIMER_END("combiner.execute", 0); +} + +static void move_next(grpc_exec_ctx *exec_ctx) { + exec_ctx->active_combiner = + exec_ctx->active_combiner->next_combiner_on_this_exec_ctx; + if (exec_ctx->active_combiner == NULL) { + exec_ctx->last_combiner = NULL; + } +} + +static void offload(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_combiner *lock = (grpc_combiner *)arg; + push_last_on_exec_ctx(exec_ctx, lock); +} + +static void queue_offload(grpc_exec_ctx *exec_ctx, grpc_combiner *lock) { + GRPC_STATS_INC_COMBINER_LOCKS_OFFLOADED(exec_ctx); + move_next(exec_ctx); + GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, "C:%p queue_offload", lock)); + GRPC_CLOSURE_SCHED(exec_ctx, &lock->offload, GRPC_ERROR_NONE); +} + +bool grpc_combiner_continue_exec_ctx(grpc_exec_ctx *exec_ctx) { + GPR_TIMER_BEGIN("combiner.continue_exec_ctx", 0); + grpc_combiner *lock = exec_ctx->active_combiner; + if (lock == NULL) { + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + return false; + } + + bool contended = + gpr_atm_no_barrier_load(&lock->initiating_exec_ctx_or_null) == 0; + + GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, + "C:%p grpc_combiner_continue_exec_ctx " + "contended=%d " + "exec_ctx_ready_to_finish=%d " + "time_to_execute_final_list=%d", + lock, contended, + grpc_exec_ctx_ready_to_finish(exec_ctx), + lock->time_to_execute_final_list)); + + if (contended && grpc_exec_ctx_ready_to_finish(exec_ctx) && + grpc_executor_is_threaded()) { + GPR_TIMER_MARK("offload_from_finished_exec_ctx", 0); + // this execution context wants to move on: schedule remaining work to be + // picked up on the executor + queue_offload(exec_ctx, lock); + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + return true; + } + + if (!lock->time_to_execute_final_list || + // peek to see if something new has shown up, and execute that with + // priority + (gpr_atm_acq_load(&lock->state) >> 1) > 1) { + gpr_mpscq_node *n = gpr_mpscq_pop(&lock->queue); + GRPC_COMBINER_TRACE( + gpr_log(GPR_DEBUG, "C:%p maybe_finish_one n=%p", lock, n)); + if (n == NULL) { + // queue is in an inconsistent state: use this as a cue that we should + // go off and do something else for a while (and come back later) + GPR_TIMER_MARK("delay_busy", 0); + queue_offload(exec_ctx, lock); + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + return true; + } + GPR_TIMER_BEGIN("combiner.exec1", 0); + grpc_closure *cl = (grpc_closure *)n; + grpc_error *cl_err = cl->error_data.error; +#ifndef NDEBUG + cl->scheduled = false; +#endif + cl->cb(exec_ctx, cl->cb_arg, cl_err); + GRPC_ERROR_UNREF(cl_err); + GPR_TIMER_END("combiner.exec1", 0); + } else { + grpc_closure *c = lock->final_list.head; + GPR_ASSERT(c != NULL); + grpc_closure_list_init(&lock->final_list); + int loops = 0; + while (c != NULL) { + GPR_TIMER_BEGIN("combiner.exec_1final", 0); + GRPC_COMBINER_TRACE( + gpr_log(GPR_DEBUG, "C:%p execute_final[%d] c=%p", lock, loops, c)); + grpc_closure *next = c->next_data.next; + grpc_error *error = c->error_data.error; +#ifndef NDEBUG + c->scheduled = false; +#endif + c->cb(exec_ctx, c->cb_arg, error); + GRPC_ERROR_UNREF(error); + c = next; + GPR_TIMER_END("combiner.exec_1final", 0); + } + } + + GPR_TIMER_MARK("unref", 0); + move_next(exec_ctx); + lock->time_to_execute_final_list = false; + gpr_atm old_state = + gpr_atm_full_fetch_add(&lock->state, -STATE_ELEM_COUNT_LOW_BIT); + GRPC_COMBINER_TRACE( + gpr_log(GPR_DEBUG, "C:%p finish old_state=%" PRIdPTR, lock, old_state)); +// Define a macro to ease readability of the following switch statement. +#define OLD_STATE_WAS(orphaned, elem_count) \ + (((orphaned) ? 0 : STATE_UNORPHANED) | \ + ((elem_count)*STATE_ELEM_COUNT_LOW_BIT)) + // Depending on what the previous state was, we need to perform different + // actions. + switch (old_state) { + default: + // we have multiple queued work items: just continue executing them + break; + case OLD_STATE_WAS(false, 2): + case OLD_STATE_WAS(true, 2): + // we're down to one queued item: if it's the final list we should do that + if (!grpc_closure_list_empty(lock->final_list)) { + lock->time_to_execute_final_list = true; + } + break; + case OLD_STATE_WAS(false, 1): + // had one count, one unorphaned --> unlocked unorphaned + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + return true; + case OLD_STATE_WAS(true, 1): + // and one count, one orphaned --> unlocked and orphaned + really_destroy(exec_ctx, lock); + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + return true; + case OLD_STATE_WAS(false, 0): + case OLD_STATE_WAS(true, 0): + // these values are illegal - representing an already unlocked or + // deleted lock + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + GPR_UNREACHABLE_CODE(return true); + } + push_first_on_exec_ctx(exec_ctx, lock); + GPR_TIMER_END("combiner.continue_exec_ctx", 0); + return true; +} + +static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure, + grpc_error *error); + +static void combiner_finally_exec(grpc_exec_ctx *exec_ctx, + grpc_closure *closure, grpc_error *error) { + GRPC_STATS_INC_COMBINER_LOCKS_SCHEDULED_FINAL_ITEMS(exec_ctx); + grpc_combiner *lock = + COMBINER_FROM_CLOSURE_SCHEDULER(closure, finally_scheduler); + GRPC_COMBINER_TRACE(gpr_log(GPR_DEBUG, + "C:%p grpc_combiner_execute_finally c=%p; ac=%p", + lock, closure, exec_ctx->active_combiner)); + GPR_TIMER_BEGIN("combiner.execute_finally", 0); + if (exec_ctx->active_combiner != lock) { + GPR_TIMER_MARK("slowpath", 0); + GRPC_CLOSURE_SCHED(exec_ctx, + GRPC_CLOSURE_CREATE(enqueue_finally, closure, + grpc_combiner_scheduler(lock)), + error); + GPR_TIMER_END("combiner.execute_finally", 0); + return; + } + + if (grpc_closure_list_empty(lock->final_list)) { + gpr_atm_full_fetch_add(&lock->state, STATE_ELEM_COUNT_LOW_BIT); + } + grpc_closure_list_append(&lock->final_list, closure, error); + GPR_TIMER_END("combiner.execute_finally", 0); +} + +static void enqueue_finally(grpc_exec_ctx *exec_ctx, void *closure, + grpc_error *error) { + combiner_finally_exec(exec_ctx, (grpc_closure *)closure, + GRPC_ERROR_REF(error)); +} + +grpc_closure_scheduler *grpc_combiner_scheduler(grpc_combiner *combiner) { + return &combiner->scheduler; +} + +grpc_closure_scheduler *grpc_combiner_finally_scheduler( + grpc_combiner *combiner) { + return &combiner->finally_scheduler; +} diff --git a/src/core/lib/iomgr/endpoint.c b/src/core/lib/iomgr/endpoint.c deleted file mode 100644 index 37cce335ca..0000000000 --- a/src/core/lib/iomgr/endpoint.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/endpoint.h" - -void grpc_endpoint_read(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - grpc_slice_buffer* slices, grpc_closure* cb) { - ep->vtable->read(exec_ctx, ep, slices, cb); -} - -void grpc_endpoint_write(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - grpc_slice_buffer* slices, grpc_closure* cb) { - ep->vtable->write(exec_ctx, ep, slices, cb); -} - -void grpc_endpoint_add_to_pollset(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - grpc_pollset* pollset) { - ep->vtable->add_to_pollset(exec_ctx, ep, pollset); -} - -void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_endpoint* ep, - grpc_pollset_set* pollset_set) { - ep->vtable->add_to_pollset_set(exec_ctx, ep, pollset_set); -} - -void grpc_endpoint_shutdown(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, - grpc_error* why) { - ep->vtable->shutdown(exec_ctx, ep, why); -} - -void grpc_endpoint_destroy(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) { - ep->vtable->destroy(exec_ctx, ep); -} - -char* grpc_endpoint_get_peer(grpc_endpoint* ep) { - return ep->vtable->get_peer(ep); -} - -int grpc_endpoint_get_fd(grpc_endpoint* ep) { return ep->vtable->get_fd(ep); } - -grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* ep) { - return ep->vtable->get_resource_user(ep); -} diff --git a/src/core/lib/iomgr/endpoint.cc b/src/core/lib/iomgr/endpoint.cc new file mode 100644 index 0000000000..37cce335ca --- /dev/null +++ b/src/core/lib/iomgr/endpoint.cc @@ -0,0 +1,59 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/endpoint.h" + +void grpc_endpoint_read(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + grpc_slice_buffer* slices, grpc_closure* cb) { + ep->vtable->read(exec_ctx, ep, slices, cb); +} + +void grpc_endpoint_write(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + grpc_slice_buffer* slices, grpc_closure* cb) { + ep->vtable->write(exec_ctx, ep, slices, cb); +} + +void grpc_endpoint_add_to_pollset(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + grpc_pollset* pollset) { + ep->vtable->add_to_pollset(exec_ctx, ep, pollset); +} + +void grpc_endpoint_add_to_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_endpoint* ep, + grpc_pollset_set* pollset_set) { + ep->vtable->add_to_pollset_set(exec_ctx, ep, pollset_set); +} + +void grpc_endpoint_shutdown(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep, + grpc_error* why) { + ep->vtable->shutdown(exec_ctx, ep, why); +} + +void grpc_endpoint_destroy(grpc_exec_ctx* exec_ctx, grpc_endpoint* ep) { + ep->vtable->destroy(exec_ctx, ep); +} + +char* grpc_endpoint_get_peer(grpc_endpoint* ep) { + return ep->vtable->get_peer(ep); +} + +int grpc_endpoint_get_fd(grpc_endpoint* ep) { return ep->vtable->get_fd(ep); } + +grpc_resource_user* grpc_endpoint_get_resource_user(grpc_endpoint* ep) { + return ep->vtable->get_resource_user(ep); +} diff --git a/src/core/lib/iomgr/endpoint_pair_posix.c b/src/core/lib/iomgr/endpoint_pair_posix.c deleted file mode 100644 index 3ade2148ba..0000000000 --- a/src/core/lib/iomgr/endpoint_pair_posix.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/endpoint_pair.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include "src/core/lib/iomgr/tcp_posix.h" -#include "src/core/lib/support/string.h" - -static void create_sockets(int sv[2]) { - int flags; - grpc_create_socketpair_if_unix(sv); - flags = fcntl(sv[0], F_GETFL, 0); - GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); - flags = fcntl(sv[1], F_GETFL, 0); - GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); - GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == GRPC_ERROR_NONE); - GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE); -} - -grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, - grpc_channel_args *args) { - int sv[2]; - grpc_endpoint_pair p; - char *final_name; - create_sockets(sv); - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - gpr_asprintf(&final_name, "%s:client", name); - p.client = grpc_tcp_create(&exec_ctx, grpc_fd_create(sv[1], final_name), args, - "socketpair-server"); - gpr_free(final_name); - gpr_asprintf(&final_name, "%s:server", name); - p.server = grpc_tcp_create(&exec_ctx, grpc_fd_create(sv[0], final_name), args, - "socketpair-client"); - gpr_free(final_name); - - grpc_exec_ctx_finish(&exec_ctx); - return p; -} - -#endif diff --git a/src/core/lib/iomgr/endpoint_pair_posix.cc b/src/core/lib/iomgr/endpoint_pair_posix.cc new file mode 100644 index 0000000000..3ade2148ba --- /dev/null +++ b/src/core/lib/iomgr/endpoint_pair_posix.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/support/string.h" + +static void create_sockets(int sv[2]) { + int flags; + grpc_create_socketpair_if_unix(sv); + flags = fcntl(sv[0], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[0], F_SETFL, flags | O_NONBLOCK) == 0); + flags = fcntl(sv[1], F_GETFL, 0); + GPR_ASSERT(fcntl(sv[1], F_SETFL, flags | O_NONBLOCK) == 0); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[0]) == GRPC_ERROR_NONE); + GPR_ASSERT(grpc_set_socket_no_sigpipe_if_possible(sv[1]) == GRPC_ERROR_NONE); +} + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, + grpc_channel_args *args) { + int sv[2]; + grpc_endpoint_pair p; + char *final_name; + create_sockets(sv); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + gpr_asprintf(&final_name, "%s:client", name); + p.client = grpc_tcp_create(&exec_ctx, grpc_fd_create(sv[1], final_name), args, + "socketpair-server"); + gpr_free(final_name); + gpr_asprintf(&final_name, "%s:server", name); + p.server = grpc_tcp_create(&exec_ctx, grpc_fd_create(sv[0], final_name), args, + "socketpair-client"); + gpr_free(final_name); + + grpc_exec_ctx_finish(&exec_ctx); + return p; +} + +#endif diff --git a/src/core/lib/iomgr/endpoint_pair_uv.c b/src/core/lib/iomgr/endpoint_pair_uv.c deleted file mode 100644 index ff72fe0492..0000000000 --- a/src/core/lib/iomgr/endpoint_pair_uv.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include - -#include - -#include "src/core/lib/iomgr/endpoint_pair.h" - -grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, - grpc_channel_args *args) { - grpc_endpoint_pair endpoint_pair; - // TODO(mlumish): implement this properly under libuv - GPR_ASSERT(false && - "grpc_iomgr_create_endpoint_pair is not suppoted with libuv"); - return endpoint_pair; -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/endpoint_pair_uv.cc b/src/core/lib/iomgr/endpoint_pair_uv.cc new file mode 100644 index 0000000000..ff72fe0492 --- /dev/null +++ b/src/core/lib/iomgr/endpoint_pair_uv.cc @@ -0,0 +1,38 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include + +#include + +#include "src/core/lib/iomgr/endpoint_pair.h" + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair(const char *name, + grpc_channel_args *args) { + grpc_endpoint_pair endpoint_pair; + // TODO(mlumish): implement this properly under libuv + GPR_ASSERT(false && + "grpc_iomgr_create_endpoint_pair is not suppoted with libuv"); + return endpoint_pair; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/endpoint_pair_windows.c b/src/core/lib/iomgr/endpoint_pair_windows.c deleted file mode 100644 index 782fa2fd69..0000000000 --- a/src/core/lib/iomgr/endpoint_pair_windows.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET -#include "src/core/lib/iomgr/endpoint_pair.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" - -#include -#include -#include - -#include -#include "src/core/lib/iomgr/socket_windows.h" -#include "src/core/lib/iomgr/tcp_windows.h" - -static void create_sockets(SOCKET sv[2]) { - SOCKET svr_sock = INVALID_SOCKET; - SOCKET lst_sock = INVALID_SOCKET; - SOCKET cli_sock = INVALID_SOCKET; - SOCKADDR_IN addr; - int addr_len = sizeof(addr); - - lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, - WSA_FLAG_OVERLAPPED); - GPR_ASSERT(lst_sock != INVALID_SOCKET); - - memset(&addr, 0, sizeof(addr)); - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_family = AF_INET; - GPR_ASSERT(bind(lst_sock, (struct sockaddr *)&addr, sizeof(addr)) != - SOCKET_ERROR); - GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR); - GPR_ASSERT(getsockname(lst_sock, (struct sockaddr *)&addr, &addr_len) != - SOCKET_ERROR); - - cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, - WSA_FLAG_OVERLAPPED); - GPR_ASSERT(cli_sock != INVALID_SOCKET); - - GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr *)&addr, addr_len, NULL, - NULL, NULL, NULL) == 0); - svr_sock = accept(lst_sock, (struct sockaddr *)&addr, &addr_len); - GPR_ASSERT(svr_sock != INVALID_SOCKET); - - closesocket(lst_sock); - grpc_tcp_prepare_socket(cli_sock); - grpc_tcp_prepare_socket(svr_sock); - - sv[1] = cli_sock; - sv[0] = svr_sock; -} - -grpc_endpoint_pair grpc_iomgr_create_endpoint_pair( - const char *name, grpc_channel_args *channel_args) { - SOCKET sv[2]; - grpc_endpoint_pair p; - create_sockets(sv); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - p.client = grpc_tcp_create(&exec_ctx, - grpc_winsocket_create(sv[1], "endpoint:client"), - channel_args, "endpoint:server"); - p.server = grpc_tcp_create(&exec_ctx, - grpc_winsocket_create(sv[0], "endpoint:server"), - channel_args, "endpoint:client"); - grpc_exec_ctx_finish(&exec_ctx); - return p; -} - -#endif diff --git a/src/core/lib/iomgr/endpoint_pair_windows.cc b/src/core/lib/iomgr/endpoint_pair_windows.cc new file mode 100644 index 0000000000..782fa2fd69 --- /dev/null +++ b/src/core/lib/iomgr/endpoint_pair_windows.cc @@ -0,0 +1,86 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET +#include "src/core/lib/iomgr/endpoint_pair.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include +#include +#include + +#include +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_windows.h" + +static void create_sockets(SOCKET sv[2]) { + SOCKET svr_sock = INVALID_SOCKET; + SOCKET lst_sock = INVALID_SOCKET; + SOCKET cli_sock = INVALID_SOCKET; + SOCKADDR_IN addr; + int addr_len = sizeof(addr); + + lst_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + GPR_ASSERT(lst_sock != INVALID_SOCKET); + + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_family = AF_INET; + GPR_ASSERT(bind(lst_sock, (struct sockaddr *)&addr, sizeof(addr)) != + SOCKET_ERROR); + GPR_ASSERT(listen(lst_sock, SOMAXCONN) != SOCKET_ERROR); + GPR_ASSERT(getsockname(lst_sock, (struct sockaddr *)&addr, &addr_len) != + SOCKET_ERROR); + + cli_sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + GPR_ASSERT(cli_sock != INVALID_SOCKET); + + GPR_ASSERT(WSAConnect(cli_sock, (struct sockaddr *)&addr, addr_len, NULL, + NULL, NULL, NULL) == 0); + svr_sock = accept(lst_sock, (struct sockaddr *)&addr, &addr_len); + GPR_ASSERT(svr_sock != INVALID_SOCKET); + + closesocket(lst_sock); + grpc_tcp_prepare_socket(cli_sock); + grpc_tcp_prepare_socket(svr_sock); + + sv[1] = cli_sock; + sv[0] = svr_sock; +} + +grpc_endpoint_pair grpc_iomgr_create_endpoint_pair( + const char *name, grpc_channel_args *channel_args) { + SOCKET sv[2]; + grpc_endpoint_pair p; + create_sockets(sv); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + p.client = grpc_tcp_create(&exec_ctx, + grpc_winsocket_create(sv[1], "endpoint:client"), + channel_args, "endpoint:server"); + p.server = grpc_tcp_create(&exec_ctx, + grpc_winsocket_create(sv[0], "endpoint:server"), + channel_args, "endpoint:client"); + grpc_exec_ctx_finish(&exec_ctx); + return p; +} + +#endif diff --git a/src/core/lib/iomgr/error.c b/src/core/lib/iomgr/error.c deleted file mode 100644 index aa05501537..0000000000 --- a/src/core/lib/iomgr/error.c +++ /dev/null @@ -1,801 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/error.h" - -#include - -#include -#include -#include -#include -#include - -#ifdef GPR_WINDOWS -#include -#endif - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/error_internal.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_error_refcount = - GRPC_TRACER_INITIALIZER(false, "error_refcount"); -#endif - -static const char *error_int_name(grpc_error_ints key) { - switch (key) { - case GRPC_ERROR_INT_ERRNO: - return "errno"; - case GRPC_ERROR_INT_FILE_LINE: - return "file_line"; - case GRPC_ERROR_INT_STREAM_ID: - return "stream_id"; - case GRPC_ERROR_INT_GRPC_STATUS: - return "grpc_status"; - case GRPC_ERROR_INT_OFFSET: - return "offset"; - case GRPC_ERROR_INT_INDEX: - return "index"; - case GRPC_ERROR_INT_SIZE: - return "size"; - case GRPC_ERROR_INT_HTTP2_ERROR: - return "http2_error"; - case GRPC_ERROR_INT_TSI_CODE: - return "tsi_code"; - case GRPC_ERROR_INT_SECURITY_STATUS: - return "security_status"; - case GRPC_ERROR_INT_FD: - return "fd"; - case GRPC_ERROR_INT_WSA_ERROR: - return "wsa_error"; - case GRPC_ERROR_INT_HTTP_STATUS: - return "http_status"; - case GRPC_ERROR_INT_LIMIT: - return "limit"; - case GRPC_ERROR_INT_OCCURRED_DURING_WRITE: - return "occurred_during_write"; - case GRPC_ERROR_INT_MAX: - GPR_UNREACHABLE_CODE(return "unknown"); - } - GPR_UNREACHABLE_CODE(return "unknown"); -} - -static const char *error_str_name(grpc_error_strs key) { - switch (key) { - case GRPC_ERROR_STR_KEY: - return "key"; - case GRPC_ERROR_STR_VALUE: - return "value"; - case GRPC_ERROR_STR_DESCRIPTION: - return "description"; - case GRPC_ERROR_STR_OS_ERROR: - return "os_error"; - case GRPC_ERROR_STR_TARGET_ADDRESS: - return "target_address"; - case GRPC_ERROR_STR_SYSCALL: - return "syscall"; - case GRPC_ERROR_STR_FILE: - return "file"; - case GRPC_ERROR_STR_GRPC_MESSAGE: - return "grpc_message"; - case GRPC_ERROR_STR_RAW_BYTES: - return "raw_bytes"; - case GRPC_ERROR_STR_TSI_ERROR: - return "tsi_error"; - case GRPC_ERROR_STR_FILENAME: - return "filename"; - case GRPC_ERROR_STR_QUEUED_BUFFERS: - return "queued_buffers"; - case GRPC_ERROR_STR_MAX: - GPR_UNREACHABLE_CODE(return "unknown"); - } - GPR_UNREACHABLE_CODE(return "unknown"); -} - -static const char *error_time_name(grpc_error_times key) { - switch (key) { - case GRPC_ERROR_TIME_CREATED: - return "created"; - case GRPC_ERROR_TIME_MAX: - GPR_UNREACHABLE_CODE(return "unknown"); - } - GPR_UNREACHABLE_CODE(return "unknown"); -} - -bool grpc_error_is_special(grpc_error *err) { - return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM || - err == GRPC_ERROR_CANCELLED; -} - -#ifndef NDEBUG -grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line) { - if (grpc_error_is_special(err)) return err; - if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { - gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err, - gpr_atm_no_barrier_load(&err->atomics.refs.count), - gpr_atm_no_barrier_load(&err->atomics.refs.count) + 1, file, line); - } - gpr_ref(&err->atomics.refs); - return err; -} -#else -grpc_error *grpc_error_ref(grpc_error *err) { - if (grpc_error_is_special(err)) return err; - gpr_ref(&err->atomics.refs); - return err; -} -#endif - -static void unref_errs(grpc_error *err) { - uint8_t slot = err->first_err; - while (slot != UINT8_MAX) { - grpc_linked_error *lerr = (grpc_linked_error *)(err->arena + slot); - GRPC_ERROR_UNREF(lerr->err); - GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX - : lerr->next != UINT8_MAX); - slot = lerr->next; - } -} - -static void unref_slice(grpc_slice slice) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_unref_internal(&exec_ctx, slice); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void unref_strs(grpc_error *err) { - for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) { - uint8_t slot = err->strs[which]; - if (slot != UINT8_MAX) { - unref_slice(*(grpc_slice *)(err->arena + slot)); - } - } -} - -static void error_destroy(grpc_error *err) { - GPR_ASSERT(!grpc_error_is_special(err)); - unref_errs(err); - unref_strs(err); - gpr_free((void *)gpr_atm_acq_load(&err->atomics.error_string)); - gpr_free(err); -} - -#ifndef NDEBUG -void grpc_error_unref(grpc_error *err, const char *file, int line) { - if (grpc_error_is_special(err)) return; - if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { - gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err, - gpr_atm_no_barrier_load(&err->atomics.refs.count), - gpr_atm_no_barrier_load(&err->atomics.refs.count) - 1, file, line); - } - if (gpr_unref(&err->atomics.refs)) { - error_destroy(err); - } -} -#else -void grpc_error_unref(grpc_error *err) { - if (grpc_error_is_special(err)) return; - if (gpr_unref(&err->atomics.refs)) { - error_destroy(err); - } -} -#endif - -static uint8_t get_placement(grpc_error **err, size_t size) { - GPR_ASSERT(*err); - uint8_t slots = (uint8_t)(size / sizeof(intptr_t)); - if ((*err)->arena_size + slots > (*err)->arena_capacity) { - (*err)->arena_capacity = - (uint8_t)GPR_MIN(UINT8_MAX - 1, (3 * (*err)->arena_capacity / 2)); - if ((*err)->arena_size + slots > (*err)->arena_capacity) { - return UINT8_MAX; - } -#ifndef NDEBUG - grpc_error *orig = *err; -#endif - *err = (grpc_error *)gpr_realloc( - *err, sizeof(grpc_error) + (*err)->arena_capacity * sizeof(intptr_t)); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { - if (*err != orig) { - gpr_log(GPR_DEBUG, "realloc %p -> %p", orig, *err); - } - } -#endif - } - uint8_t placement = (*err)->arena_size; - (*err)->arena_size = (uint8_t)((*err)->arena_size + slots); - return placement; -} - -static void internal_set_int(grpc_error **err, grpc_error_ints which, - intptr_t value) { - uint8_t slot = (*err)->ints[which]; - if (slot == UINT8_MAX) { - slot = get_placement(err, sizeof(value)); - if (slot == UINT8_MAX) { - gpr_log(GPR_ERROR, "Error %p is full, dropping int {\"%s\":%" PRIiPTR "}", - *err, error_int_name(which), value); - return; - } - } - (*err)->ints[which] = slot; - (*err)->arena[slot] = value; -} - -static void internal_set_str(grpc_error **err, grpc_error_strs which, - grpc_slice value) { - uint8_t slot = (*err)->strs[which]; - if (slot == UINT8_MAX) { - slot = get_placement(err, sizeof(value)); - if (slot == UINT8_MAX) { - const char *str = grpc_slice_to_c_string(value); - gpr_log(GPR_ERROR, "Error %p is full, dropping string {\"%s\":\"%s\"}", - *err, error_str_name(which), str); - gpr_free((void *)str); - return; - } - } else { - unref_slice(*(grpc_slice *)((*err)->arena + slot)); - } - (*err)->strs[which] = slot; - memcpy((*err)->arena + slot, &value, sizeof(value)); -} - -static char *fmt_time(gpr_timespec tm); -static void internal_set_time(grpc_error **err, grpc_error_times which, - gpr_timespec value) { - uint8_t slot = (*err)->times[which]; - if (slot == UINT8_MAX) { - slot = get_placement(err, sizeof(value)); - if (slot == UINT8_MAX) { - const char *time_str = fmt_time(value); - gpr_log(GPR_ERROR, "Error %p is full, dropping \"%s\":\"%s\"}", *err, - error_time_name(which), time_str); - gpr_free((void *)time_str); - return; - } - } - (*err)->times[which] = slot; - memcpy((*err)->arena + slot, &value, sizeof(value)); -} - -static void internal_add_error(grpc_error **err, grpc_error *new_err) { - grpc_linked_error new_last = {new_err, UINT8_MAX}; - uint8_t slot = get_placement(err, sizeof(grpc_linked_error)); - if (slot == UINT8_MAX) { - gpr_log(GPR_ERROR, "Error %p is full, dropping error %p = %s", *err, - new_err, grpc_error_string(new_err)); - GRPC_ERROR_UNREF(new_err); - return; - } - if ((*err)->first_err == UINT8_MAX) { - GPR_ASSERT((*err)->last_err == UINT8_MAX); - (*err)->last_err = slot; - (*err)->first_err = slot; - } else { - GPR_ASSERT((*err)->last_err != UINT8_MAX); - grpc_linked_error *old_last = - (grpc_linked_error *)((*err)->arena + (*err)->last_err); - old_last->next = slot; - (*err)->last_err = slot; - } - memcpy((*err)->arena + slot, &new_last, sizeof(grpc_linked_error)); -} - -#define SLOTS_PER_INT (sizeof(intptr_t) / sizeof(intptr_t)) -#define SLOTS_PER_STR (sizeof(grpc_slice) / sizeof(intptr_t)) -#define SLOTS_PER_TIME (sizeof(gpr_timespec) / sizeof(intptr_t)) -#define SLOTS_PER_LINKED_ERROR (sizeof(grpc_linked_error) / sizeof(intptr_t)) - -// size of storing one int and two slices and a timespec. For line, desc, file, -// and time created -#define DEFAULT_ERROR_CAPACITY \ - (SLOTS_PER_INT + (SLOTS_PER_STR * 2) + SLOTS_PER_TIME) - -// It is very common to include and extra int and string in an error -#define SURPLUS_CAPACITY (2 * SLOTS_PER_INT + SLOTS_PER_TIME) - -grpc_error *grpc_error_create(const char *file, int line, grpc_slice desc, - grpc_error **referencing, - size_t num_referencing) { - GPR_TIMER_BEGIN("grpc_error_create", 0); - uint8_t initial_arena_capacity = (uint8_t)( - DEFAULT_ERROR_CAPACITY + - (uint8_t)(num_referencing * SLOTS_PER_LINKED_ERROR) + SURPLUS_CAPACITY); - grpc_error *err = (grpc_error *)gpr_malloc( - sizeof(*err) + initial_arena_capacity * sizeof(intptr_t)); - if (err == NULL) { // TODO(ctiller): make gpr_malloc return NULL - return GRPC_ERROR_OOM; - } -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { - gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line); - } -#endif - - err->arena_size = 0; - err->arena_capacity = initial_arena_capacity; - err->first_err = UINT8_MAX; - err->last_err = UINT8_MAX; - - memset(err->ints, UINT8_MAX, GRPC_ERROR_INT_MAX); - memset(err->strs, UINT8_MAX, GRPC_ERROR_STR_MAX); - memset(err->times, UINT8_MAX, GRPC_ERROR_TIME_MAX); - - internal_set_int(&err, GRPC_ERROR_INT_FILE_LINE, line); - internal_set_str(&err, GRPC_ERROR_STR_FILE, - grpc_slice_from_static_string(file)); - internal_set_str(&err, GRPC_ERROR_STR_DESCRIPTION, desc); - - for (size_t i = 0; i < num_referencing; ++i) { - if (referencing[i] == GRPC_ERROR_NONE) continue; - internal_add_error( - &err, - GRPC_ERROR_REF( - referencing[i])); // TODO(ncteisen), change ownership semantics - } - - internal_set_time(&err, GRPC_ERROR_TIME_CREATED, gpr_now(GPR_CLOCK_REALTIME)); - - gpr_atm_no_barrier_store(&err->atomics.error_string, 0); - gpr_ref_init(&err->atomics.refs, 1); - GPR_TIMER_END("grpc_error_create", 0); - return err; -} - -static void ref_strs(grpc_error *err) { - for (size_t i = 0; i < GRPC_ERROR_STR_MAX; ++i) { - uint8_t slot = err->strs[i]; - if (slot != UINT8_MAX) { - grpc_slice_ref_internal(*(grpc_slice *)(err->arena + slot)); - } - } -} - -static void ref_errs(grpc_error *err) { - uint8_t slot = err->first_err; - while (slot != UINT8_MAX) { - grpc_linked_error *lerr = (grpc_linked_error *)(err->arena + slot); - GRPC_ERROR_REF(lerr->err); - slot = lerr->next; - } -} - -static grpc_error *copy_error_and_unref(grpc_error *in) { - GPR_TIMER_BEGIN("copy_error_and_unref", 0); - grpc_error *out; - if (grpc_error_is_special(in)) { - out = GRPC_ERROR_CREATE_FROM_STATIC_STRING("unknown"); - if (in == GRPC_ERROR_NONE) { - internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, - grpc_slice_from_static_string("no error")); - internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK); - } else if (in == GRPC_ERROR_OOM) { - internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, - grpc_slice_from_static_string("oom")); - } else if (in == GRPC_ERROR_CANCELLED) { - internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, - grpc_slice_from_static_string("cancelled")); - internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED); - } - } else if (gpr_ref_is_unique(&in->atomics.refs)) { - out = in; - } else { - uint8_t new_arena_capacity = in->arena_capacity; - // the returned err will be added to, so we ensure this is room to avoid - // unneeded allocations. - if (in->arena_capacity - in->arena_size < (uint8_t)SLOTS_PER_STR) { - new_arena_capacity = (uint8_t)(3 * new_arena_capacity / 2); - } - out = (grpc_error *)gpr_malloc(sizeof(*in) + - new_arena_capacity * sizeof(intptr_t)); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { - gpr_log(GPR_DEBUG, "%p create copying %p", out, in); - } -#endif - // bulk memcpy of the rest of the struct. - size_t skip = sizeof(&out->atomics); - memcpy((void *)((uintptr_t)out + skip), (void *)((uintptr_t)in + skip), - sizeof(*in) + (in->arena_size * sizeof(intptr_t)) - skip); - // manually set the atomics and the new capacity - gpr_atm_no_barrier_store(&out->atomics.error_string, 0); - gpr_ref_init(&out->atomics.refs, 1); - out->arena_capacity = new_arena_capacity; - ref_strs(out); - ref_errs(out); - GRPC_ERROR_UNREF(in); - } - GPR_TIMER_END("copy_error_and_unref", 0); - return out; -} - -grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which, - intptr_t value) { - GPR_TIMER_BEGIN("grpc_error_set_int", 0); - grpc_error *new_err = copy_error_and_unref(src); - internal_set_int(&new_err, which, value); - GPR_TIMER_END("grpc_error_set_int", 0); - return new_err; -} - -typedef struct { - grpc_error *error; - grpc_status_code code; - const char *msg; -} special_error_status_map; -static special_error_status_map error_status_map[] = { - {GRPC_ERROR_NONE, GRPC_STATUS_OK, ""}, - {GRPC_ERROR_CANCELLED, GRPC_STATUS_CANCELLED, "Cancelled"}, - {GRPC_ERROR_OOM, GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"}, -}; - -bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) { - GPR_TIMER_BEGIN("grpc_error_get_int", 0); - if (grpc_error_is_special(err)) { - if (which == GRPC_ERROR_INT_GRPC_STATUS) { - for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { - if (error_status_map[i].error == err) { - if (p != NULL) *p = error_status_map[i].code; - GPR_TIMER_END("grpc_error_get_int", 0); - return true; - } - } - } - GPR_TIMER_END("grpc_error_get_int", 0); - return false; - } - uint8_t slot = err->ints[which]; - if (slot != UINT8_MAX) { - if (p != NULL) *p = err->arena[slot]; - GPR_TIMER_END("grpc_error_get_int", 0); - return true; - } - GPR_TIMER_END("grpc_error_get_int", 0); - return false; -} - -grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which, - grpc_slice str) { - GPR_TIMER_BEGIN("grpc_error_set_str", 0); - grpc_error *new_err = copy_error_and_unref(src); - internal_set_str(&new_err, which, str); - GPR_TIMER_END("grpc_error_set_str", 0); - return new_err; -} - -bool grpc_error_get_str(grpc_error *err, grpc_error_strs which, - grpc_slice *str) { - if (grpc_error_is_special(err)) { - if (which == GRPC_ERROR_STR_GRPC_MESSAGE) { - for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { - if (error_status_map[i].error == err) { - *str = grpc_slice_from_static_string(error_status_map[i].msg); - return true; - } - } - } - return false; - } - uint8_t slot = err->strs[which]; - if (slot != UINT8_MAX) { - *str = *(grpc_slice *)(err->arena + slot); - return true; - } else { - return false; - } -} - -grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) { - GPR_TIMER_BEGIN("grpc_error_add_child", 0); - grpc_error *new_err = copy_error_and_unref(src); - internal_add_error(&new_err, child); - GPR_TIMER_END("grpc_error_add_child", 0); - return new_err; -} - -static const char *no_error_string = "\"No Error\""; -static const char *oom_error_string = "\"Out of memory\""; -static const char *cancelled_error_string = "\"Cancelled\""; - -typedef struct { - char *key; - char *value; -} kv_pair; - -typedef struct { - kv_pair *kvs; - size_t num_kvs; - size_t cap_kvs; -} kv_pairs; - -static void append_chr(char c, char **s, size_t *sz, size_t *cap) { - if (*sz == *cap) { - *cap = GPR_MAX(8, 3 * *cap / 2); - *s = (char *)gpr_realloc(*s, *cap); - } - (*s)[(*sz)++] = c; -} - -static void append_str(const char *str, char **s, size_t *sz, size_t *cap) { - for (const char *c = str; *c; c++) { - append_chr(*c, s, sz, cap); - } -} - -static void append_esc_str(const uint8_t *str, size_t len, char **s, size_t *sz, - size_t *cap) { - static const char *hex = "0123456789abcdef"; - append_chr('"', s, sz, cap); - for (size_t i = 0; i < len; i++, str++) { - if (*str < 32 || *str >= 127) { - append_chr('\\', s, sz, cap); - switch (*str) { - case '\b': - append_chr('b', s, sz, cap); - break; - case '\f': - append_chr('f', s, sz, cap); - break; - case '\n': - append_chr('n', s, sz, cap); - break; - case '\r': - append_chr('r', s, sz, cap); - break; - case '\t': - append_chr('t', s, sz, cap); - break; - default: - append_chr('u', s, sz, cap); - append_chr('0', s, sz, cap); - append_chr('0', s, sz, cap); - append_chr(hex[*str >> 4], s, sz, cap); - append_chr(hex[*str & 0x0f], s, sz, cap); - break; - } - } else { - append_chr((char)*str, s, sz, cap); - } - } - append_chr('"', s, sz, cap); -} - -static void append_kv(kv_pairs *kvs, char *key, char *value) { - if (kvs->num_kvs == kvs->cap_kvs) { - kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4); - kvs->kvs = - (kv_pair *)gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs); - } - kvs->kvs[kvs->num_kvs].key = key; - kvs->kvs[kvs->num_kvs].value = value; - kvs->num_kvs++; -} - -static char *key_int(grpc_error_ints which) { - return gpr_strdup(error_int_name(which)); -} - -static char *fmt_int(intptr_t p) { - char *s; - gpr_asprintf(&s, "%" PRIdPTR, p); - return s; -} - -static void collect_ints_kvs(grpc_error *err, kv_pairs *kvs) { - for (size_t which = 0; which < GRPC_ERROR_INT_MAX; ++which) { - uint8_t slot = err->ints[which]; - if (slot != UINT8_MAX) { - append_kv(kvs, key_int((grpc_error_ints)which), - fmt_int(err->arena[slot])); - } - } -} - -static char *key_str(grpc_error_strs which) { - return gpr_strdup(error_str_name(which)); -} - -static char *fmt_str(grpc_slice slice) { - char *s = NULL; - size_t sz = 0; - size_t cap = 0; - append_esc_str((const uint8_t *)GRPC_SLICE_START_PTR(slice), - GRPC_SLICE_LENGTH(slice), &s, &sz, &cap); - append_chr(0, &s, &sz, &cap); - return s; -} - -static void collect_strs_kvs(grpc_error *err, kv_pairs *kvs) { - for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) { - uint8_t slot = err->strs[which]; - if (slot != UINT8_MAX) { - append_kv(kvs, key_str((grpc_error_strs)which), - fmt_str(*(grpc_slice *)(err->arena + slot))); - } - } -} - -static char *key_time(grpc_error_times which) { - return gpr_strdup(error_time_name(which)); -} - -static char *fmt_time(gpr_timespec tm) { - char *out; - const char *pfx = "!!"; - switch (tm.clock_type) { - case GPR_CLOCK_MONOTONIC: - pfx = "@monotonic:"; - break; - case GPR_CLOCK_REALTIME: - pfx = "@"; - break; - case GPR_CLOCK_PRECISE: - pfx = "@precise:"; - break; - case GPR_TIMESPAN: - pfx = ""; - break; - } - gpr_asprintf(&out, "\"%s%" PRId64 ".%09d\"", pfx, tm.tv_sec, tm.tv_nsec); - return out; -} - -static void collect_times_kvs(grpc_error *err, kv_pairs *kvs) { - for (size_t which = 0; which < GRPC_ERROR_TIME_MAX; ++which) { - uint8_t slot = err->times[which]; - if (slot != UINT8_MAX) { - append_kv(kvs, key_time((grpc_error_times)which), - fmt_time(*(gpr_timespec *)(err->arena + slot))); - } - } -} - -static void add_errs(grpc_error *err, char **s, size_t *sz, size_t *cap) { - uint8_t slot = err->first_err; - bool first = true; - while (slot != UINT8_MAX) { - grpc_linked_error *lerr = (grpc_linked_error *)(err->arena + slot); - if (!first) append_chr(',', s, sz, cap); - first = false; - const char *e = grpc_error_string(lerr->err); - append_str(e, s, sz, cap); - GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX - : lerr->next != UINT8_MAX); - slot = lerr->next; - } -} - -static char *errs_string(grpc_error *err) { - char *s = NULL; - size_t sz = 0; - size_t cap = 0; - append_chr('[', &s, &sz, &cap); - add_errs(err, &s, &sz, &cap); - append_chr(']', &s, &sz, &cap); - append_chr(0, &s, &sz, &cap); - return s; -} - -static int cmp_kvs(const void *a, const void *b) { - const kv_pair *ka = (const kv_pair *)a; - const kv_pair *kb = (const kv_pair *)b; - return strcmp(ka->key, kb->key); -} - -static char *finish_kvs(kv_pairs *kvs) { - char *s = NULL; - size_t sz = 0; - size_t cap = 0; - - append_chr('{', &s, &sz, &cap); - for (size_t i = 0; i < kvs->num_kvs; i++) { - if (i != 0) append_chr(',', &s, &sz, &cap); - append_esc_str((const uint8_t *)kvs->kvs[i].key, strlen(kvs->kvs[i].key), - &s, &sz, &cap); - gpr_free(kvs->kvs[i].key); - append_chr(':', &s, &sz, &cap); - append_str(kvs->kvs[i].value, &s, &sz, &cap); - gpr_free(kvs->kvs[i].value); - } - append_chr('}', &s, &sz, &cap); - append_chr(0, &s, &sz, &cap); - - gpr_free(kvs->kvs); - return s; -} - -const char *grpc_error_string(grpc_error *err) { - GPR_TIMER_BEGIN("grpc_error_string", 0); - if (err == GRPC_ERROR_NONE) return no_error_string; - if (err == GRPC_ERROR_OOM) return oom_error_string; - if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string; - - void *p = (void *)gpr_atm_acq_load(&err->atomics.error_string); - if (p != NULL) { - GPR_TIMER_END("grpc_error_string", 0); - return (const char *)p; - } - - kv_pairs kvs; - memset(&kvs, 0, sizeof(kvs)); - - collect_ints_kvs(err, &kvs); - collect_strs_kvs(err, &kvs); - collect_times_kvs(err, &kvs); - if (err->first_err != UINT8_MAX) { - append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err)); - } - - qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs); - - char *out = finish_kvs(&kvs); - - if (!gpr_atm_rel_cas(&err->atomics.error_string, 0, (gpr_atm)out)) { - gpr_free(out); - out = (char *)gpr_atm_no_barrier_load(&err->atomics.error_string); - } - - GPR_TIMER_END("grpc_error_string", 0); - return out; -} - -grpc_error *grpc_os_error(const char *file, int line, int err, - const char *call_name) { - return grpc_error_set_str( - grpc_error_set_str( - grpc_error_set_int( - grpc_error_create(file, line, - grpc_slice_from_static_string("OS Error"), NULL, - 0), - GRPC_ERROR_INT_ERRNO, err), - GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(strerror(err))), - GRPC_ERROR_STR_SYSCALL, grpc_slice_from_copied_string(call_name)); -} - -#ifdef GPR_WINDOWS -grpc_error *grpc_wsa_error(const char *file, int line, int err, - const char *call_name) { - char *utf8_message = gpr_format_message(err); - grpc_error *error = grpc_error_set_str( - grpc_error_set_str( - grpc_error_set_int( - grpc_error_create(file, line, - grpc_slice_from_static_string("OS Error"), NULL, - 0), - GRPC_ERROR_INT_WSA_ERROR, err), - GRPC_ERROR_STR_OS_ERROR, grpc_slice_from_copied_string(utf8_message)), - GRPC_ERROR_STR_SYSCALL, grpc_slice_from_static_string(call_name)); - gpr_free(utf8_message); - return error; -} -#endif - -bool grpc_log_if_error(const char *what, grpc_error *error, const char *file, - int line) { - if (error == GRPC_ERROR_NONE) return true; - const char *msg = grpc_error_string(error); - gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg); - GRPC_ERROR_UNREF(error); - return false; -} diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc new file mode 100644 index 0000000000..aa05501537 --- /dev/null +++ b/src/core/lib/iomgr/error.cc @@ -0,0 +1,801 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/error.h" + +#include + +#include +#include +#include +#include +#include + +#ifdef GPR_WINDOWS +#include +#endif + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/error_internal.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_error_refcount = + GRPC_TRACER_INITIALIZER(false, "error_refcount"); +#endif + +static const char *error_int_name(grpc_error_ints key) { + switch (key) { + case GRPC_ERROR_INT_ERRNO: + return "errno"; + case GRPC_ERROR_INT_FILE_LINE: + return "file_line"; + case GRPC_ERROR_INT_STREAM_ID: + return "stream_id"; + case GRPC_ERROR_INT_GRPC_STATUS: + return "grpc_status"; + case GRPC_ERROR_INT_OFFSET: + return "offset"; + case GRPC_ERROR_INT_INDEX: + return "index"; + case GRPC_ERROR_INT_SIZE: + return "size"; + case GRPC_ERROR_INT_HTTP2_ERROR: + return "http2_error"; + case GRPC_ERROR_INT_TSI_CODE: + return "tsi_code"; + case GRPC_ERROR_INT_SECURITY_STATUS: + return "security_status"; + case GRPC_ERROR_INT_FD: + return "fd"; + case GRPC_ERROR_INT_WSA_ERROR: + return "wsa_error"; + case GRPC_ERROR_INT_HTTP_STATUS: + return "http_status"; + case GRPC_ERROR_INT_LIMIT: + return "limit"; + case GRPC_ERROR_INT_OCCURRED_DURING_WRITE: + return "occurred_during_write"; + case GRPC_ERROR_INT_MAX: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static const char *error_str_name(grpc_error_strs key) { + switch (key) { + case GRPC_ERROR_STR_KEY: + return "key"; + case GRPC_ERROR_STR_VALUE: + return "value"; + case GRPC_ERROR_STR_DESCRIPTION: + return "description"; + case GRPC_ERROR_STR_OS_ERROR: + return "os_error"; + case GRPC_ERROR_STR_TARGET_ADDRESS: + return "target_address"; + case GRPC_ERROR_STR_SYSCALL: + return "syscall"; + case GRPC_ERROR_STR_FILE: + return "file"; + case GRPC_ERROR_STR_GRPC_MESSAGE: + return "grpc_message"; + case GRPC_ERROR_STR_RAW_BYTES: + return "raw_bytes"; + case GRPC_ERROR_STR_TSI_ERROR: + return "tsi_error"; + case GRPC_ERROR_STR_FILENAME: + return "filename"; + case GRPC_ERROR_STR_QUEUED_BUFFERS: + return "queued_buffers"; + case GRPC_ERROR_STR_MAX: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +static const char *error_time_name(grpc_error_times key) { + switch (key) { + case GRPC_ERROR_TIME_CREATED: + return "created"; + case GRPC_ERROR_TIME_MAX: + GPR_UNREACHABLE_CODE(return "unknown"); + } + GPR_UNREACHABLE_CODE(return "unknown"); +} + +bool grpc_error_is_special(grpc_error *err) { + return err == GRPC_ERROR_NONE || err == GRPC_ERROR_OOM || + err == GRPC_ERROR_CANCELLED; +} + +#ifndef NDEBUG +grpc_error *grpc_error_ref(grpc_error *err, const char *file, int line) { + if (grpc_error_is_special(err)) return err; + if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { + gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err, + gpr_atm_no_barrier_load(&err->atomics.refs.count), + gpr_atm_no_barrier_load(&err->atomics.refs.count) + 1, file, line); + } + gpr_ref(&err->atomics.refs); + return err; +} +#else +grpc_error *grpc_error_ref(grpc_error *err) { + if (grpc_error_is_special(err)) return err; + gpr_ref(&err->atomics.refs); + return err; +} +#endif + +static void unref_errs(grpc_error *err) { + uint8_t slot = err->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error *lerr = (grpc_linked_error *)(err->arena + slot); + GRPC_ERROR_UNREF(lerr->err); + GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX + : lerr->next != UINT8_MAX); + slot = lerr->next; + } +} + +static void unref_slice(grpc_slice slice) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_unref_internal(&exec_ctx, slice); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void unref_strs(grpc_error *err) { + for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) { + uint8_t slot = err->strs[which]; + if (slot != UINT8_MAX) { + unref_slice(*(grpc_slice *)(err->arena + slot)); + } + } +} + +static void error_destroy(grpc_error *err) { + GPR_ASSERT(!grpc_error_is_special(err)); + unref_errs(err); + unref_strs(err); + gpr_free((void *)gpr_atm_acq_load(&err->atomics.error_string)); + gpr_free(err); +} + +#ifndef NDEBUG +void grpc_error_unref(grpc_error *err, const char *file, int line) { + if (grpc_error_is_special(err)) return; + if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { + gpr_log(GPR_DEBUG, "%p: %" PRIdPTR " -> %" PRIdPTR " [%s:%d]", err, + gpr_atm_no_barrier_load(&err->atomics.refs.count), + gpr_atm_no_barrier_load(&err->atomics.refs.count) - 1, file, line); + } + if (gpr_unref(&err->atomics.refs)) { + error_destroy(err); + } +} +#else +void grpc_error_unref(grpc_error *err) { + if (grpc_error_is_special(err)) return; + if (gpr_unref(&err->atomics.refs)) { + error_destroy(err); + } +} +#endif + +static uint8_t get_placement(grpc_error **err, size_t size) { + GPR_ASSERT(*err); + uint8_t slots = (uint8_t)(size / sizeof(intptr_t)); + if ((*err)->arena_size + slots > (*err)->arena_capacity) { + (*err)->arena_capacity = + (uint8_t)GPR_MIN(UINT8_MAX - 1, (3 * (*err)->arena_capacity / 2)); + if ((*err)->arena_size + slots > (*err)->arena_capacity) { + return UINT8_MAX; + } +#ifndef NDEBUG + grpc_error *orig = *err; +#endif + *err = (grpc_error *)gpr_realloc( + *err, sizeof(grpc_error) + (*err)->arena_capacity * sizeof(intptr_t)); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { + if (*err != orig) { + gpr_log(GPR_DEBUG, "realloc %p -> %p", orig, *err); + } + } +#endif + } + uint8_t placement = (*err)->arena_size; + (*err)->arena_size = (uint8_t)((*err)->arena_size + slots); + return placement; +} + +static void internal_set_int(grpc_error **err, grpc_error_ints which, + intptr_t value) { + uint8_t slot = (*err)->ints[which]; + if (slot == UINT8_MAX) { + slot = get_placement(err, sizeof(value)); + if (slot == UINT8_MAX) { + gpr_log(GPR_ERROR, "Error %p is full, dropping int {\"%s\":%" PRIiPTR "}", + *err, error_int_name(which), value); + return; + } + } + (*err)->ints[which] = slot; + (*err)->arena[slot] = value; +} + +static void internal_set_str(grpc_error **err, grpc_error_strs which, + grpc_slice value) { + uint8_t slot = (*err)->strs[which]; + if (slot == UINT8_MAX) { + slot = get_placement(err, sizeof(value)); + if (slot == UINT8_MAX) { + const char *str = grpc_slice_to_c_string(value); + gpr_log(GPR_ERROR, "Error %p is full, dropping string {\"%s\":\"%s\"}", + *err, error_str_name(which), str); + gpr_free((void *)str); + return; + } + } else { + unref_slice(*(grpc_slice *)((*err)->arena + slot)); + } + (*err)->strs[which] = slot; + memcpy((*err)->arena + slot, &value, sizeof(value)); +} + +static char *fmt_time(gpr_timespec tm); +static void internal_set_time(grpc_error **err, grpc_error_times which, + gpr_timespec value) { + uint8_t slot = (*err)->times[which]; + if (slot == UINT8_MAX) { + slot = get_placement(err, sizeof(value)); + if (slot == UINT8_MAX) { + const char *time_str = fmt_time(value); + gpr_log(GPR_ERROR, "Error %p is full, dropping \"%s\":\"%s\"}", *err, + error_time_name(which), time_str); + gpr_free((void *)time_str); + return; + } + } + (*err)->times[which] = slot; + memcpy((*err)->arena + slot, &value, sizeof(value)); +} + +static void internal_add_error(grpc_error **err, grpc_error *new_err) { + grpc_linked_error new_last = {new_err, UINT8_MAX}; + uint8_t slot = get_placement(err, sizeof(grpc_linked_error)); + if (slot == UINT8_MAX) { + gpr_log(GPR_ERROR, "Error %p is full, dropping error %p = %s", *err, + new_err, grpc_error_string(new_err)); + GRPC_ERROR_UNREF(new_err); + return; + } + if ((*err)->first_err == UINT8_MAX) { + GPR_ASSERT((*err)->last_err == UINT8_MAX); + (*err)->last_err = slot; + (*err)->first_err = slot; + } else { + GPR_ASSERT((*err)->last_err != UINT8_MAX); + grpc_linked_error *old_last = + (grpc_linked_error *)((*err)->arena + (*err)->last_err); + old_last->next = slot; + (*err)->last_err = slot; + } + memcpy((*err)->arena + slot, &new_last, sizeof(grpc_linked_error)); +} + +#define SLOTS_PER_INT (sizeof(intptr_t) / sizeof(intptr_t)) +#define SLOTS_PER_STR (sizeof(grpc_slice) / sizeof(intptr_t)) +#define SLOTS_PER_TIME (sizeof(gpr_timespec) / sizeof(intptr_t)) +#define SLOTS_PER_LINKED_ERROR (sizeof(grpc_linked_error) / sizeof(intptr_t)) + +// size of storing one int and two slices and a timespec. For line, desc, file, +// and time created +#define DEFAULT_ERROR_CAPACITY \ + (SLOTS_PER_INT + (SLOTS_PER_STR * 2) + SLOTS_PER_TIME) + +// It is very common to include and extra int and string in an error +#define SURPLUS_CAPACITY (2 * SLOTS_PER_INT + SLOTS_PER_TIME) + +grpc_error *grpc_error_create(const char *file, int line, grpc_slice desc, + grpc_error **referencing, + size_t num_referencing) { + GPR_TIMER_BEGIN("grpc_error_create", 0); + uint8_t initial_arena_capacity = (uint8_t)( + DEFAULT_ERROR_CAPACITY + + (uint8_t)(num_referencing * SLOTS_PER_LINKED_ERROR) + SURPLUS_CAPACITY); + grpc_error *err = (grpc_error *)gpr_malloc( + sizeof(*err) + initial_arena_capacity * sizeof(intptr_t)); + if (err == NULL) { // TODO(ctiller): make gpr_malloc return NULL + return GRPC_ERROR_OOM; + } +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { + gpr_log(GPR_DEBUG, "%p create [%s:%d]", err, file, line); + } +#endif + + err->arena_size = 0; + err->arena_capacity = initial_arena_capacity; + err->first_err = UINT8_MAX; + err->last_err = UINT8_MAX; + + memset(err->ints, UINT8_MAX, GRPC_ERROR_INT_MAX); + memset(err->strs, UINT8_MAX, GRPC_ERROR_STR_MAX); + memset(err->times, UINT8_MAX, GRPC_ERROR_TIME_MAX); + + internal_set_int(&err, GRPC_ERROR_INT_FILE_LINE, line); + internal_set_str(&err, GRPC_ERROR_STR_FILE, + grpc_slice_from_static_string(file)); + internal_set_str(&err, GRPC_ERROR_STR_DESCRIPTION, desc); + + for (size_t i = 0; i < num_referencing; ++i) { + if (referencing[i] == GRPC_ERROR_NONE) continue; + internal_add_error( + &err, + GRPC_ERROR_REF( + referencing[i])); // TODO(ncteisen), change ownership semantics + } + + internal_set_time(&err, GRPC_ERROR_TIME_CREATED, gpr_now(GPR_CLOCK_REALTIME)); + + gpr_atm_no_barrier_store(&err->atomics.error_string, 0); + gpr_ref_init(&err->atomics.refs, 1); + GPR_TIMER_END("grpc_error_create", 0); + return err; +} + +static void ref_strs(grpc_error *err) { + for (size_t i = 0; i < GRPC_ERROR_STR_MAX; ++i) { + uint8_t slot = err->strs[i]; + if (slot != UINT8_MAX) { + grpc_slice_ref_internal(*(grpc_slice *)(err->arena + slot)); + } + } +} + +static void ref_errs(grpc_error *err) { + uint8_t slot = err->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error *lerr = (grpc_linked_error *)(err->arena + slot); + GRPC_ERROR_REF(lerr->err); + slot = lerr->next; + } +} + +static grpc_error *copy_error_and_unref(grpc_error *in) { + GPR_TIMER_BEGIN("copy_error_and_unref", 0); + grpc_error *out; + if (grpc_error_is_special(in)) { + out = GRPC_ERROR_CREATE_FROM_STATIC_STRING("unknown"); + if (in == GRPC_ERROR_NONE) { + internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_static_string("no error")); + internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK); + } else if (in == GRPC_ERROR_OOM) { + internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_static_string("oom")); + } else if (in == GRPC_ERROR_CANCELLED) { + internal_set_str(&out, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_static_string("cancelled")); + internal_set_int(&out, GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_CANCELLED); + } + } else if (gpr_ref_is_unique(&in->atomics.refs)) { + out = in; + } else { + uint8_t new_arena_capacity = in->arena_capacity; + // the returned err will be added to, so we ensure this is room to avoid + // unneeded allocations. + if (in->arena_capacity - in->arena_size < (uint8_t)SLOTS_PER_STR) { + new_arena_capacity = (uint8_t)(3 * new_arena_capacity / 2); + } + out = (grpc_error *)gpr_malloc(sizeof(*in) + + new_arena_capacity * sizeof(intptr_t)); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_error_refcount)) { + gpr_log(GPR_DEBUG, "%p create copying %p", out, in); + } +#endif + // bulk memcpy of the rest of the struct. + size_t skip = sizeof(&out->atomics); + memcpy((void *)((uintptr_t)out + skip), (void *)((uintptr_t)in + skip), + sizeof(*in) + (in->arena_size * sizeof(intptr_t)) - skip); + // manually set the atomics and the new capacity + gpr_atm_no_barrier_store(&out->atomics.error_string, 0); + gpr_ref_init(&out->atomics.refs, 1); + out->arena_capacity = new_arena_capacity; + ref_strs(out); + ref_errs(out); + GRPC_ERROR_UNREF(in); + } + GPR_TIMER_END("copy_error_and_unref", 0); + return out; +} + +grpc_error *grpc_error_set_int(grpc_error *src, grpc_error_ints which, + intptr_t value) { + GPR_TIMER_BEGIN("grpc_error_set_int", 0); + grpc_error *new_err = copy_error_and_unref(src); + internal_set_int(&new_err, which, value); + GPR_TIMER_END("grpc_error_set_int", 0); + return new_err; +} + +typedef struct { + grpc_error *error; + grpc_status_code code; + const char *msg; +} special_error_status_map; +static special_error_status_map error_status_map[] = { + {GRPC_ERROR_NONE, GRPC_STATUS_OK, ""}, + {GRPC_ERROR_CANCELLED, GRPC_STATUS_CANCELLED, "Cancelled"}, + {GRPC_ERROR_OOM, GRPC_STATUS_RESOURCE_EXHAUSTED, "Out of memory"}, +}; + +bool grpc_error_get_int(grpc_error *err, grpc_error_ints which, intptr_t *p) { + GPR_TIMER_BEGIN("grpc_error_get_int", 0); + if (grpc_error_is_special(err)) { + if (which == GRPC_ERROR_INT_GRPC_STATUS) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { + if (error_status_map[i].error == err) { + if (p != NULL) *p = error_status_map[i].code; + GPR_TIMER_END("grpc_error_get_int", 0); + return true; + } + } + } + GPR_TIMER_END("grpc_error_get_int", 0); + return false; + } + uint8_t slot = err->ints[which]; + if (slot != UINT8_MAX) { + if (p != NULL) *p = err->arena[slot]; + GPR_TIMER_END("grpc_error_get_int", 0); + return true; + } + GPR_TIMER_END("grpc_error_get_int", 0); + return false; +} + +grpc_error *grpc_error_set_str(grpc_error *src, grpc_error_strs which, + grpc_slice str) { + GPR_TIMER_BEGIN("grpc_error_set_str", 0); + grpc_error *new_err = copy_error_and_unref(src); + internal_set_str(&new_err, which, str); + GPR_TIMER_END("grpc_error_set_str", 0); + return new_err; +} + +bool grpc_error_get_str(grpc_error *err, grpc_error_strs which, + grpc_slice *str) { + if (grpc_error_is_special(err)) { + if (which == GRPC_ERROR_STR_GRPC_MESSAGE) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(error_status_map); i++) { + if (error_status_map[i].error == err) { + *str = grpc_slice_from_static_string(error_status_map[i].msg); + return true; + } + } + } + return false; + } + uint8_t slot = err->strs[which]; + if (slot != UINT8_MAX) { + *str = *(grpc_slice *)(err->arena + slot); + return true; + } else { + return false; + } +} + +grpc_error *grpc_error_add_child(grpc_error *src, grpc_error *child) { + GPR_TIMER_BEGIN("grpc_error_add_child", 0); + grpc_error *new_err = copy_error_and_unref(src); + internal_add_error(&new_err, child); + GPR_TIMER_END("grpc_error_add_child", 0); + return new_err; +} + +static const char *no_error_string = "\"No Error\""; +static const char *oom_error_string = "\"Out of memory\""; +static const char *cancelled_error_string = "\"Cancelled\""; + +typedef struct { + char *key; + char *value; +} kv_pair; + +typedef struct { + kv_pair *kvs; + size_t num_kvs; + size_t cap_kvs; +} kv_pairs; + +static void append_chr(char c, char **s, size_t *sz, size_t *cap) { + if (*sz == *cap) { + *cap = GPR_MAX(8, 3 * *cap / 2); + *s = (char *)gpr_realloc(*s, *cap); + } + (*s)[(*sz)++] = c; +} + +static void append_str(const char *str, char **s, size_t *sz, size_t *cap) { + for (const char *c = str; *c; c++) { + append_chr(*c, s, sz, cap); + } +} + +static void append_esc_str(const uint8_t *str, size_t len, char **s, size_t *sz, + size_t *cap) { + static const char *hex = "0123456789abcdef"; + append_chr('"', s, sz, cap); + for (size_t i = 0; i < len; i++, str++) { + if (*str < 32 || *str >= 127) { + append_chr('\\', s, sz, cap); + switch (*str) { + case '\b': + append_chr('b', s, sz, cap); + break; + case '\f': + append_chr('f', s, sz, cap); + break; + case '\n': + append_chr('n', s, sz, cap); + break; + case '\r': + append_chr('r', s, sz, cap); + break; + case '\t': + append_chr('t', s, sz, cap); + break; + default: + append_chr('u', s, sz, cap); + append_chr('0', s, sz, cap); + append_chr('0', s, sz, cap); + append_chr(hex[*str >> 4], s, sz, cap); + append_chr(hex[*str & 0x0f], s, sz, cap); + break; + } + } else { + append_chr((char)*str, s, sz, cap); + } + } + append_chr('"', s, sz, cap); +} + +static void append_kv(kv_pairs *kvs, char *key, char *value) { + if (kvs->num_kvs == kvs->cap_kvs) { + kvs->cap_kvs = GPR_MAX(3 * kvs->cap_kvs / 2, 4); + kvs->kvs = + (kv_pair *)gpr_realloc(kvs->kvs, sizeof(*kvs->kvs) * kvs->cap_kvs); + } + kvs->kvs[kvs->num_kvs].key = key; + kvs->kvs[kvs->num_kvs].value = value; + kvs->num_kvs++; +} + +static char *key_int(grpc_error_ints which) { + return gpr_strdup(error_int_name(which)); +} + +static char *fmt_int(intptr_t p) { + char *s; + gpr_asprintf(&s, "%" PRIdPTR, p); + return s; +} + +static void collect_ints_kvs(grpc_error *err, kv_pairs *kvs) { + for (size_t which = 0; which < GRPC_ERROR_INT_MAX; ++which) { + uint8_t slot = err->ints[which]; + if (slot != UINT8_MAX) { + append_kv(kvs, key_int((grpc_error_ints)which), + fmt_int(err->arena[slot])); + } + } +} + +static char *key_str(grpc_error_strs which) { + return gpr_strdup(error_str_name(which)); +} + +static char *fmt_str(grpc_slice slice) { + char *s = NULL; + size_t sz = 0; + size_t cap = 0; + append_esc_str((const uint8_t *)GRPC_SLICE_START_PTR(slice), + GRPC_SLICE_LENGTH(slice), &s, &sz, &cap); + append_chr(0, &s, &sz, &cap); + return s; +} + +static void collect_strs_kvs(grpc_error *err, kv_pairs *kvs) { + for (size_t which = 0; which < GRPC_ERROR_STR_MAX; ++which) { + uint8_t slot = err->strs[which]; + if (slot != UINT8_MAX) { + append_kv(kvs, key_str((grpc_error_strs)which), + fmt_str(*(grpc_slice *)(err->arena + slot))); + } + } +} + +static char *key_time(grpc_error_times which) { + return gpr_strdup(error_time_name(which)); +} + +static char *fmt_time(gpr_timespec tm) { + char *out; + const char *pfx = "!!"; + switch (tm.clock_type) { + case GPR_CLOCK_MONOTONIC: + pfx = "@monotonic:"; + break; + case GPR_CLOCK_REALTIME: + pfx = "@"; + break; + case GPR_CLOCK_PRECISE: + pfx = "@precise:"; + break; + case GPR_TIMESPAN: + pfx = ""; + break; + } + gpr_asprintf(&out, "\"%s%" PRId64 ".%09d\"", pfx, tm.tv_sec, tm.tv_nsec); + return out; +} + +static void collect_times_kvs(grpc_error *err, kv_pairs *kvs) { + for (size_t which = 0; which < GRPC_ERROR_TIME_MAX; ++which) { + uint8_t slot = err->times[which]; + if (slot != UINT8_MAX) { + append_kv(kvs, key_time((grpc_error_times)which), + fmt_time(*(gpr_timespec *)(err->arena + slot))); + } + } +} + +static void add_errs(grpc_error *err, char **s, size_t *sz, size_t *cap) { + uint8_t slot = err->first_err; + bool first = true; + while (slot != UINT8_MAX) { + grpc_linked_error *lerr = (grpc_linked_error *)(err->arena + slot); + if (!first) append_chr(',', s, sz, cap); + first = false; + const char *e = grpc_error_string(lerr->err); + append_str(e, s, sz, cap); + GPR_ASSERT(err->last_err == slot ? lerr->next == UINT8_MAX + : lerr->next != UINT8_MAX); + slot = lerr->next; + } +} + +static char *errs_string(grpc_error *err) { + char *s = NULL; + size_t sz = 0; + size_t cap = 0; + append_chr('[', &s, &sz, &cap); + add_errs(err, &s, &sz, &cap); + append_chr(']', &s, &sz, &cap); + append_chr(0, &s, &sz, &cap); + return s; +} + +static int cmp_kvs(const void *a, const void *b) { + const kv_pair *ka = (const kv_pair *)a; + const kv_pair *kb = (const kv_pair *)b; + return strcmp(ka->key, kb->key); +} + +static char *finish_kvs(kv_pairs *kvs) { + char *s = NULL; + size_t sz = 0; + size_t cap = 0; + + append_chr('{', &s, &sz, &cap); + for (size_t i = 0; i < kvs->num_kvs; i++) { + if (i != 0) append_chr(',', &s, &sz, &cap); + append_esc_str((const uint8_t *)kvs->kvs[i].key, strlen(kvs->kvs[i].key), + &s, &sz, &cap); + gpr_free(kvs->kvs[i].key); + append_chr(':', &s, &sz, &cap); + append_str(kvs->kvs[i].value, &s, &sz, &cap); + gpr_free(kvs->kvs[i].value); + } + append_chr('}', &s, &sz, &cap); + append_chr(0, &s, &sz, &cap); + + gpr_free(kvs->kvs); + return s; +} + +const char *grpc_error_string(grpc_error *err) { + GPR_TIMER_BEGIN("grpc_error_string", 0); + if (err == GRPC_ERROR_NONE) return no_error_string; + if (err == GRPC_ERROR_OOM) return oom_error_string; + if (err == GRPC_ERROR_CANCELLED) return cancelled_error_string; + + void *p = (void *)gpr_atm_acq_load(&err->atomics.error_string); + if (p != NULL) { + GPR_TIMER_END("grpc_error_string", 0); + return (const char *)p; + } + + kv_pairs kvs; + memset(&kvs, 0, sizeof(kvs)); + + collect_ints_kvs(err, &kvs); + collect_strs_kvs(err, &kvs); + collect_times_kvs(err, &kvs); + if (err->first_err != UINT8_MAX) { + append_kv(&kvs, gpr_strdup("referenced_errors"), errs_string(err)); + } + + qsort(kvs.kvs, kvs.num_kvs, sizeof(kv_pair), cmp_kvs); + + char *out = finish_kvs(&kvs); + + if (!gpr_atm_rel_cas(&err->atomics.error_string, 0, (gpr_atm)out)) { + gpr_free(out); + out = (char *)gpr_atm_no_barrier_load(&err->atomics.error_string); + } + + GPR_TIMER_END("grpc_error_string", 0); + return out; +} + +grpc_error *grpc_os_error(const char *file, int line, int err, + const char *call_name) { + return grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_int( + grpc_error_create(file, line, + grpc_slice_from_static_string("OS Error"), NULL, + 0), + GRPC_ERROR_INT_ERRNO, err), + GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(strerror(err))), + GRPC_ERROR_STR_SYSCALL, grpc_slice_from_copied_string(call_name)); +} + +#ifdef GPR_WINDOWS +grpc_error *grpc_wsa_error(const char *file, int line, int err, + const char *call_name) { + char *utf8_message = gpr_format_message(err); + grpc_error *error = grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_int( + grpc_error_create(file, line, + grpc_slice_from_static_string("OS Error"), NULL, + 0), + GRPC_ERROR_INT_WSA_ERROR, err), + GRPC_ERROR_STR_OS_ERROR, grpc_slice_from_copied_string(utf8_message)), + GRPC_ERROR_STR_SYSCALL, grpc_slice_from_static_string(call_name)); + gpr_free(utf8_message); + return error; +} +#endif + +bool grpc_log_if_error(const char *what, grpc_error *error, const char *file, + int line) { + if (error == GRPC_ERROR_NONE) return true; + const char *msg = grpc_error_string(error); + gpr_log(file, line, GPR_LOG_SEVERITY_ERROR, "%s: %s", what, msg); + GRPC_ERROR_UNREF(error); + return false; +} diff --git a/src/core/lib/iomgr/ev_epoll1_linux.c b/src/core/lib/iomgr/ev_epoll1_linux.c deleted file mode 100644 index 3ac12ab56f..0000000000 --- a/src/core/lib/iomgr/ev_epoll1_linux.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GRPC_LINUX_EPOLL - -#include "src/core/lib/iomgr/ev_epoll1_linux.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/lockfree_event.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/string.h" - -static grpc_wakeup_fd global_wakeup_fd; - -/******************************************************************************* - * Singleton epoll set related fields - */ - -#define MAX_EPOLL_EVENTS 100 -#define MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION 1 - -/* NOTE ON SYNCHRONIZATION: - * - Fields in this struct are only modified by the designated poller. Hence - * there is no need for any locks to protect the struct. - * - num_events and cursor fields have to be of atomic type to provide memory - * visibility guarantees only. i.e In case of multiple pollers, the designated - * polling thread keeps changing; the thread that wrote these values may be - * different from the thread reading the values - */ -typedef struct epoll_set { - int epfd; - - /* The epoll_events after the last call to epoll_wait() */ - struct epoll_event events[MAX_EPOLL_EVENTS]; - - /* The number of epoll_events after the last call to epoll_wait() */ - gpr_atm num_events; - - /* Index of the first event in epoll_events that has to be processed. This - * field is only valid if num_events > 0 */ - gpr_atm cursor; -} epoll_set; - -/* The global singleton epoll set */ -static epoll_set g_epoll_set; - -/* Must be called *only* once */ -static bool epoll_set_init() { - g_epoll_set.epfd = epoll_create1(EPOLL_CLOEXEC); - if (g_epoll_set.epfd < 0) { - gpr_log(GPR_ERROR, "epoll unavailable"); - return false; - } - - gpr_log(GPR_INFO, "grpc epoll fd: %d", g_epoll_set.epfd); - gpr_atm_no_barrier_store(&g_epoll_set.num_events, 0); - gpr_atm_no_barrier_store(&g_epoll_set.cursor, 0); - return true; -} - -/* epoll_set_init() MUST be called before calling this. */ -static void epoll_set_shutdown() { - if (g_epoll_set.epfd >= 0) { - close(g_epoll_set.epfd); - g_epoll_set.epfd = -1; - } -} - -/******************************************************************************* - * Fd Declarations - */ - -struct grpc_fd { - int fd; - - gpr_atm read_closure; - gpr_atm write_closure; - - struct grpc_fd *freelist_next; - - /* The pollset that last noticed that the fd is readable. The actual type - * stored in this is (grpc_pollset *) */ - gpr_atm read_notifier_pollset; - - grpc_iomgr_object iomgr_object; -}; - -static void fd_global_init(void); -static void fd_global_shutdown(void); - -/******************************************************************************* - * Pollset Declarations - */ - -typedef enum { UNKICKED, KICKED, DESIGNATED_POLLER } kick_state; - -static const char *kick_state_string(kick_state st) { - switch (st) { - case UNKICKED: - return "UNKICKED"; - case KICKED: - return "KICKED"; - case DESIGNATED_POLLER: - return "DESIGNATED_POLLER"; - } - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -struct grpc_pollset_worker { - kick_state state; - int kick_state_mutator; // which line of code last changed kick state - bool initialized_cv; - grpc_pollset_worker *next; - grpc_pollset_worker *prev; - gpr_cv cv; - grpc_closure_list schedule_on_end_work; -}; - -#define SET_KICK_STATE(worker, kick_state) \ - do { \ - (worker)->state = (kick_state); \ - (worker)->kick_state_mutator = __LINE__; \ - } while (false) - -#define MAX_NEIGHBORHOODS 1024 - -typedef struct pollset_neighborhood { - gpr_mu mu; - grpc_pollset *active_root; - char pad[GPR_CACHELINE_SIZE]; -} pollset_neighborhood; - -struct grpc_pollset { - gpr_mu mu; - pollset_neighborhood *neighborhood; - bool reassigning_neighborhood; - grpc_pollset_worker *root_worker; - bool kicked_without_poller; - - /* Set to true if the pollset is observed to have no workers available to - poll */ - bool seen_inactive; - bool shutting_down; /* Is the pollset shutting down ? */ - grpc_closure *shutdown_closure; /* Called after after shutdown is complete */ - - /* Number of workers who are *about-to* attach themselves to the pollset - * worker list */ - int begin_refs; - - grpc_pollset *next; - grpc_pollset *prev; -}; - -/******************************************************************************* - * Pollset-set Declarations - */ - -struct grpc_pollset_set { - char unused; -}; - -/******************************************************************************* - * Common helpers - */ - -static bool append_error(grpc_error **composite, grpc_error *error, - const char *desc) { - if (error == GRPC_ERROR_NONE) return true; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); - } - *composite = grpc_error_add_child(*composite, error); - return false; -} - -/******************************************************************************* - * Fd Definitions - */ - -/* We need to keep a freelist not because of any concerns of malloc performance - * but instead so that implementations with multiple threads in (for example) - * epoll_wait deal with the race between pollset removal and incoming poll - * notifications. - * - * The problem is that the poller ultimately holds a reference to this - * object, so it is very difficult to know when is safe to free it, at least - * without some expensive synchronization. - * - * If we keep the object freelisted, in the worst case losing this race just - * becomes a spurious read notification on a reused fd. - */ - -/* The alarm system needs to be able to wakeup 'some poller' sometimes - * (specifically when a new alarm needs to be triggered earlier than the next - * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a - * case occurs. */ - -static grpc_fd *fd_freelist = NULL; -static gpr_mu fd_freelist_mu; - -static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } - -static void fd_global_shutdown(void) { - gpr_mu_lock(&fd_freelist_mu); - gpr_mu_unlock(&fd_freelist_mu); - while (fd_freelist != NULL) { - grpc_fd *fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - gpr_free(fd); - } - gpr_mu_destroy(&fd_freelist_mu); -} - -static grpc_fd *fd_create(int fd, const char *name) { - grpc_fd *new_fd = NULL; - - gpr_mu_lock(&fd_freelist_mu); - if (fd_freelist != NULL) { - new_fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - } - gpr_mu_unlock(&fd_freelist_mu); - - if (new_fd == NULL) { - new_fd = (grpc_fd *)gpr_malloc(sizeof(grpc_fd)); - } - - new_fd->fd = fd; - grpc_lfev_init(&new_fd->read_closure); - grpc_lfev_init(&new_fd->write_closure); - gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); - - new_fd->freelist_next = NULL; - - char *fd_name; - gpr_asprintf(&fd_name, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); - } -#endif - gpr_free(fd_name); - - struct epoll_event ev; - ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); - ev.data.ptr = new_fd; - if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, fd, &ev) != 0) { - gpr_log(GPR_ERROR, "epoll_ctl failed: %s", strerror(errno)); - } - - return new_fd; -} - -static int fd_wrapped_fd(grpc_fd *fd) { return fd->fd; } - -/* if 'releasing_fd' is true, it means that we are going to detach the internal - * fd from grpc_fd structure (i.e which means we should not be calling - * shutdown() syscall on that fd) */ -static void fd_shutdown_internal(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_error *why, bool releasing_fd) { - if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, - GRPC_ERROR_REF(why))) { - if (!releasing_fd) { - shutdown(fd->fd, SHUT_RDWR); - } - grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); - } - GRPC_ERROR_UNREF(why); -} - -/* Might be called multiple times */ -static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - fd_shutdown_internal(exec_ctx, fd, why, false); -} - -static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *on_done, int *release_fd, - bool already_closed, const char *reason) { - grpc_error *error = GRPC_ERROR_NONE; - bool is_release_fd = (release_fd != NULL); - - if (!grpc_lfev_is_shutdown(&fd->read_closure)) { - fd_shutdown_internal(exec_ctx, fd, - GRPC_ERROR_CREATE_FROM_COPIED_STRING(reason), - is_release_fd); - } - - /* If release_fd is not NULL, we should be relinquishing control of the file - descriptor fd->fd (but we still own the grpc_fd structure). */ - if (is_release_fd) { - *release_fd = fd->fd; - } else if (!already_closed) { - close(fd->fd); - } - - GRPC_CLOSURE_SCHED(exec_ctx, on_done, GRPC_ERROR_REF(error)); - - grpc_iomgr_unregister_object(&fd->iomgr_object); - grpc_lfev_destroy(&fd->read_closure); - grpc_lfev_destroy(&fd->write_closure); - - gpr_mu_lock(&fd_freelist_mu); - fd->freelist_next = fd_freelist; - fd_freelist = fd; - gpr_mu_unlock(&fd_freelist_mu); -} - -static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); - return (grpc_pollset *)notifier; -} - -static bool fd_is_shutdown(grpc_fd *fd) { - return grpc_lfev_is_shutdown(&fd->read_closure); -} - -static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); -} - -static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); -} - -static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_pollset *notifier) { - grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); - /* Use release store to match with acquire load in fd_get_read_notifier */ - gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); -} - -static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); -} - -/******************************************************************************* - * Pollset Definitions - */ - -GPR_TLS_DECL(g_current_thread_pollset); -GPR_TLS_DECL(g_current_thread_worker); - -/* The designated poller */ -static gpr_atm g_active_poller; - -static pollset_neighborhood *g_neighborhoods; -static size_t g_num_neighborhoods; - -/* Return true if first in list */ -static bool worker_insert(grpc_pollset *pollset, grpc_pollset_worker *worker) { - if (pollset->root_worker == NULL) { - pollset->root_worker = worker; - worker->next = worker->prev = worker; - return true; - } else { - worker->next = pollset->root_worker; - worker->prev = worker->next->prev; - worker->next->prev = worker; - worker->prev->next = worker; - return false; - } -} - -/* Return true if last in list */ -typedef enum { EMPTIED, NEW_ROOT, REMOVED } worker_remove_result; - -static worker_remove_result worker_remove(grpc_pollset *pollset, - grpc_pollset_worker *worker) { - if (worker == pollset->root_worker) { - if (worker == worker->next) { - pollset->root_worker = NULL; - return EMPTIED; - } else { - pollset->root_worker = worker->next; - worker->prev->next = worker->next; - worker->next->prev = worker->prev; - return NEW_ROOT; - } - } else { - worker->prev->next = worker->next; - worker->next->prev = worker->prev; - return REMOVED; - } -} - -static size_t choose_neighborhood(void) { - return (size_t)gpr_cpu_current_cpu() % g_num_neighborhoods; -} - -static grpc_error *pollset_global_init(void) { - gpr_tls_init(&g_current_thread_pollset); - gpr_tls_init(&g_current_thread_worker); - gpr_atm_no_barrier_store(&g_active_poller, 0); - global_wakeup_fd.read_fd = -1; - grpc_error *err = grpc_wakeup_fd_init(&global_wakeup_fd); - if (err != GRPC_ERROR_NONE) return err; - struct epoll_event ev; - ev.events = (uint32_t)(EPOLLIN | EPOLLET); - ev.data.ptr = &global_wakeup_fd; - if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, global_wakeup_fd.read_fd, - &ev) != 0) { - return GRPC_OS_ERROR(errno, "epoll_ctl"); - } - g_num_neighborhoods = GPR_CLAMP(gpr_cpu_num_cores(), 1, MAX_NEIGHBORHOODS); - g_neighborhoods = (pollset_neighborhood *)gpr_zalloc( - sizeof(*g_neighborhoods) * g_num_neighborhoods); - for (size_t i = 0; i < g_num_neighborhoods; i++) { - gpr_mu_init(&g_neighborhoods[i].mu); - } - return GRPC_ERROR_NONE; -} - -static void pollset_global_shutdown(void) { - gpr_tls_destroy(&g_current_thread_pollset); - gpr_tls_destroy(&g_current_thread_worker); - if (global_wakeup_fd.read_fd != -1) grpc_wakeup_fd_destroy(&global_wakeup_fd); - for (size_t i = 0; i < g_num_neighborhoods; i++) { - gpr_mu_destroy(&g_neighborhoods[i].mu); - } - gpr_free(g_neighborhoods); -} - -static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - gpr_mu_init(&pollset->mu); - *mu = &pollset->mu; - pollset->neighborhood = &g_neighborhoods[choose_neighborhood()]; - pollset->reassigning_neighborhood = false; - pollset->root_worker = NULL; - pollset->kicked_without_poller = false; - pollset->seen_inactive = true; - pollset->shutting_down = false; - pollset->shutdown_closure = NULL; - pollset->begin_refs = 0; - pollset->next = pollset->prev = NULL; -} - -static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - gpr_mu_lock(&pollset->mu); - if (!pollset->seen_inactive) { - pollset_neighborhood *neighborhood = pollset->neighborhood; - gpr_mu_unlock(&pollset->mu); - retry_lock_neighborhood: - gpr_mu_lock(&neighborhood->mu); - gpr_mu_lock(&pollset->mu); - if (!pollset->seen_inactive) { - if (pollset->neighborhood != neighborhood) { - gpr_mu_unlock(&neighborhood->mu); - neighborhood = pollset->neighborhood; - gpr_mu_unlock(&pollset->mu); - goto retry_lock_neighborhood; - } - pollset->prev->next = pollset->next; - pollset->next->prev = pollset->prev; - if (pollset == pollset->neighborhood->active_root) { - pollset->neighborhood->active_root = - pollset->next == pollset ? NULL : pollset->next; - } - } - gpr_mu_unlock(&pollset->neighborhood->mu); - } - gpr_mu_unlock(&pollset->mu); - gpr_mu_destroy(&pollset->mu); -} - -static grpc_error *pollset_kick_all(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - GPR_TIMER_BEGIN("pollset_kick_all", 0); - grpc_error *error = GRPC_ERROR_NONE; - if (pollset->root_worker != NULL) { - grpc_pollset_worker *worker = pollset->root_worker; - do { - GRPC_STATS_INC_POLLSET_KICK(exec_ctx); - switch (worker->state) { - case KICKED: - GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); - break; - case UNKICKED: - SET_KICK_STATE(worker, KICKED); - if (worker->initialized_cv) { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); - gpr_cv_signal(&worker->cv); - } - break; - case DESIGNATED_POLLER: - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); - SET_KICK_STATE(worker, KICKED); - append_error(&error, grpc_wakeup_fd_wakeup(&global_wakeup_fd), - "pollset_kick_all"); - break; - } - - worker = worker->next; - } while (worker != pollset->root_worker); - } - // TODO: sreek. Check if we need to set 'kicked_without_poller' to true here - // in the else case - GPR_TIMER_END("pollset_kick_all", 0); - return error; -} - -static void pollset_maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - if (pollset->shutdown_closure != NULL && pollset->root_worker == NULL && - pollset->begin_refs == 0) { - GPR_TIMER_MARK("pollset_finish_shutdown", 0); - GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_closure, GRPC_ERROR_NONE); - pollset->shutdown_closure = NULL; - } -} - -static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_TIMER_BEGIN("pollset_shutdown", 0); - GPR_ASSERT(pollset->shutdown_closure == NULL); - GPR_ASSERT(!pollset->shutting_down); - pollset->shutdown_closure = closure; - pollset->shutting_down = true; - GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(exec_ctx, pollset)); - pollset_maybe_finish_shutdown(exec_ctx, pollset); - GPR_TIMER_END("pollset_shutdown", 0); -} - -static int poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return -1; - } - - if (gpr_time_cmp(deadline, now) <= 0) { - return 0; - } - - static const gpr_timespec round_up = { - 0, /* tv_sec */ - GPR_NS_PER_MS - 1, /* tv_nsec */ - GPR_TIMESPAN /* clock_type */ - }; - timeout = gpr_time_sub(deadline, now); - int millis = gpr_time_to_millis(gpr_time_add(timeout, round_up)); - return millis >= 1 ? millis : 1; -} - -/* Process the epoll events found by do_epoll_wait() function. - - g_epoll_set.cursor points to the index of the first event to be processed - - This function then processes up-to MAX_EPOLL_EVENTS_PER_ITERATION and - updates the g_epoll_set.cursor - - NOTE ON SYNCRHONIZATION: Similar to do_epoll_wait(), this function is only - called by g_active_poller thread. So there is no need for synchronization - when accessing fields in g_epoll_set */ -static grpc_error *process_epoll_events(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - static const char *err_desc = "process_events"; - grpc_error *error = GRPC_ERROR_NONE; - - GPR_TIMER_BEGIN("process_epoll_events", 0); - long num_events = gpr_atm_acq_load(&g_epoll_set.num_events); - long cursor = gpr_atm_acq_load(&g_epoll_set.cursor); - for (int idx = 0; - (idx < MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION) && cursor != num_events; - idx++) { - long c = cursor++; - struct epoll_event *ev = &g_epoll_set.events[c]; - void *data_ptr = ev->data.ptr; - - if (data_ptr == &global_wakeup_fd) { - append_error(&error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd), - err_desc); - } else { - grpc_fd *fd = (grpc_fd *)(data_ptr); - bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0; - bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0; - bool write_ev = (ev->events & EPOLLOUT) != 0; - - if (read_ev || cancel) { - fd_become_readable(exec_ctx, fd, pollset); - } - - if (write_ev || cancel) { - fd_become_writable(exec_ctx, fd); - } - } - } - gpr_atm_rel_store(&g_epoll_set.cursor, cursor); - GPR_TIMER_END("process_epoll_events", 0); - return error; -} - -/* Do epoll_wait and store the events in g_epoll_set.events field. This does not - "process" any of the events yet; that is done in process_epoll_events(). - *See process_epoll_events() function for more details. - - NOTE ON SYNCHRONIZATION: At any point of time, only the g_active_poller - (i.e the designated poller thread) will be calling this function. So there is - no need for any synchronization when accesing fields in g_epoll_set */ -static grpc_error *do_epoll_wait(grpc_exec_ctx *exec_ctx, grpc_pollset *ps, - gpr_timespec now, gpr_timespec deadline) { - GPR_TIMER_BEGIN("do_epoll_wait", 0); - - int r; - int timeout = poll_deadline_to_millis_timeout(deadline, now); - if (timeout != 0) { - GRPC_SCHEDULING_START_BLOCKING_REGION; - } - do { - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - r = epoll_wait(g_epoll_set.epfd, g_epoll_set.events, MAX_EPOLL_EVENTS, - timeout); - } while (r < 0 && errno == EINTR); - if (timeout != 0) { - GRPC_SCHEDULING_END_BLOCKING_REGION; - } - - if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait"); - - GRPC_STATS_INC_POLL_EVENTS_RETURNED(exec_ctx, r); - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "ps: %p poll got %d events", ps, r); - } - - gpr_atm_rel_store(&g_epoll_set.num_events, r); - gpr_atm_rel_store(&g_epoll_set.cursor, 0); - - GPR_TIMER_END("do_epoll_wait", 0); - return GRPC_ERROR_NONE; -} - -static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker, - grpc_pollset_worker **worker_hdl, gpr_timespec *now, - gpr_timespec deadline) { - GPR_TIMER_BEGIN("begin_worker", 0); - if (worker_hdl != NULL) *worker_hdl = worker; - worker->initialized_cv = false; - SET_KICK_STATE(worker, UNKICKED); - worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; - pollset->begin_refs++; - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, "PS:%p BEGIN_STARTS:%p", pollset, worker); - } - - if (pollset->seen_inactive) { - // pollset has been observed to be inactive, we need to move back to the - // active list - bool is_reassigning = false; - if (!pollset->reassigning_neighborhood) { - is_reassigning = true; - pollset->reassigning_neighborhood = true; - pollset->neighborhood = &g_neighborhoods[choose_neighborhood()]; - } - pollset_neighborhood *neighborhood = pollset->neighborhood; - gpr_mu_unlock(&pollset->mu); - // pollset unlocked: state may change (even worker->kick_state) - retry_lock_neighborhood: - gpr_mu_lock(&neighborhood->mu); - gpr_mu_lock(&pollset->mu); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d", - pollset, worker, kick_state_string(worker->state), - is_reassigning); - } - if (pollset->seen_inactive) { - if (neighborhood != pollset->neighborhood) { - gpr_mu_unlock(&neighborhood->mu); - neighborhood = pollset->neighborhood; - gpr_mu_unlock(&pollset->mu); - goto retry_lock_neighborhood; - } - - /* In the brief time we released the pollset locks above, the worker MAY - have been kicked. In this case, the worker should get out of this - pollset ASAP and hence this should neither add the pollset to - neighborhood nor mark the pollset as active. - - On a side note, the only way a worker's kick state could have changed - at this point is if it were "kicked specifically". Since the worker has - not added itself to the pollset yet (by calling worker_insert()), it is - not visible in the "kick any" path yet */ - if (worker->state == UNKICKED) { - pollset->seen_inactive = false; - if (neighborhood->active_root == NULL) { - neighborhood->active_root = pollset->next = pollset->prev = pollset; - /* Make this the designated poller if there isn't one already */ - if (worker->state == UNKICKED && - gpr_atm_no_barrier_cas(&g_active_poller, 0, (gpr_atm)worker)) { - SET_KICK_STATE(worker, DESIGNATED_POLLER); - } - } else { - pollset->next = neighborhood->active_root; - pollset->prev = pollset->next->prev; - pollset->next->prev = pollset->prev->next = pollset; - } - } - } - if (is_reassigning) { - GPR_ASSERT(pollset->reassigning_neighborhood); - pollset->reassigning_neighborhood = false; - } - gpr_mu_unlock(&neighborhood->mu); - } - - worker_insert(pollset, worker); - pollset->begin_refs--; - if (worker->state == UNKICKED && !pollset->kicked_without_poller) { - GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker); - worker->initialized_cv = true; - gpr_cv_init(&worker->cv); - while (worker->state == UNKICKED && !pollset->shutting_down) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d", - pollset, worker, kick_state_string(worker->state), - pollset->shutting_down); - } - - if (gpr_cv_wait(&worker->cv, &pollset->mu, deadline) && - worker->state == UNKICKED) { - /* If gpr_cv_wait returns true (i.e a timeout), pretend that the worker - received a kick */ - SET_KICK_STATE(worker, KICKED); - } - } - *now = gpr_now(now->clock_type); - } - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, - "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d " - "kicked_without_poller: %d", - pollset, worker, kick_state_string(worker->state), - pollset->shutting_down, pollset->kicked_without_poller); - } - - /* We release pollset lock in this function at a couple of places: - * 1. Briefly when assigning pollset to a neighborhood - * 2. When doing gpr_cv_wait() - * It is possible that 'kicked_without_poller' was set to true during (1) and - * 'shutting_down' is set to true during (1) or (2). If either of them is - * true, this worker cannot do polling */ - /* TODO(sreek): Perhaps there is a better way to handle kicked_without_poller - * case; especially when the worker is the DESIGNATED_POLLER */ - - if (pollset->kicked_without_poller) { - pollset->kicked_without_poller = false; - GPR_TIMER_END("begin_worker", 0); - return false; - } - - GPR_TIMER_END("begin_worker", 0); - return worker->state == DESIGNATED_POLLER && !pollset->shutting_down; -} - -static bool check_neighborhood_for_available_poller( - grpc_exec_ctx *exec_ctx, pollset_neighborhood *neighborhood) { - GPR_TIMER_BEGIN("check_neighborhood_for_available_poller", 0); - bool found_worker = false; - do { - grpc_pollset *inspect = neighborhood->active_root; - if (inspect == NULL) { - break; - } - gpr_mu_lock(&inspect->mu); - GPR_ASSERT(!inspect->seen_inactive); - grpc_pollset_worker *inspect_worker = inspect->root_worker; - if (inspect_worker != NULL) { - do { - switch (inspect_worker->state) { - case UNKICKED: - if (gpr_atm_no_barrier_cas(&g_active_poller, 0, - (gpr_atm)inspect_worker)) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, " .. choose next poller to be %p", - inspect_worker); - } - SET_KICK_STATE(inspect_worker, DESIGNATED_POLLER); - if (inspect_worker->initialized_cv) { - GPR_TIMER_MARK("signal worker", 0); - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); - gpr_cv_signal(&inspect_worker->cv); - } - } else { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, " .. beaten to choose next poller"); - } - } - // even if we didn't win the cas, there's a worker, we can stop - found_worker = true; - break; - case KICKED: - break; - case DESIGNATED_POLLER: - found_worker = true; // ok, so someone else found the worker, but - // we'll accept that - break; - } - inspect_worker = inspect_worker->next; - } while (!found_worker && inspect_worker != inspect->root_worker); - } - if (!found_worker) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, " .. mark pollset %p inactive", inspect); - } - inspect->seen_inactive = true; - if (inspect == neighborhood->active_root) { - neighborhood->active_root = - inspect->next == inspect ? NULL : inspect->next; - } - inspect->next->prev = inspect->prev; - inspect->prev->next = inspect->next; - inspect->next = inspect->prev = NULL; - } - gpr_mu_unlock(&inspect->mu); - } while (!found_worker); - GPR_TIMER_END("check_neighborhood_for_available_poller", 0); - return found_worker; -} - -static void end_worker(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *worker, - grpc_pollset_worker **worker_hdl) { - GPR_TIMER_BEGIN("end_worker", 0); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p END_WORKER:%p", pollset, worker); - } - if (worker_hdl != NULL) *worker_hdl = NULL; - /* Make sure we appear kicked */ - SET_KICK_STATE(worker, KICKED); - grpc_closure_list_move(&worker->schedule_on_end_work, - &exec_ctx->closure_list); - if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) { - if (worker->next != worker && worker->next->state == UNKICKED) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, " .. choose next poller to be peer %p", worker); - } - GPR_ASSERT(worker->next->initialized_cv); - gpr_atm_no_barrier_store(&g_active_poller, (gpr_atm)worker->next); - SET_KICK_STATE(worker->next, DESIGNATED_POLLER); - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); - gpr_cv_signal(&worker->next->cv); - if (grpc_exec_ctx_has_work(exec_ctx)) { - gpr_mu_unlock(&pollset->mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - } - } else { - gpr_atm_no_barrier_store(&g_active_poller, 0); - size_t poller_neighborhood_idx = - (size_t)(pollset->neighborhood - g_neighborhoods); - gpr_mu_unlock(&pollset->mu); - bool found_worker = false; - bool scan_state[MAX_NEIGHBORHOODS]; - for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) { - pollset_neighborhood *neighborhood = - &g_neighborhoods[(poller_neighborhood_idx + i) % - g_num_neighborhoods]; - if (gpr_mu_trylock(&neighborhood->mu)) { - found_worker = - check_neighborhood_for_available_poller(exec_ctx, neighborhood); - gpr_mu_unlock(&neighborhood->mu); - scan_state[i] = true; - } else { - scan_state[i] = false; - } - } - for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) { - if (scan_state[i]) continue; - pollset_neighborhood *neighborhood = - &g_neighborhoods[(poller_neighborhood_idx + i) % - g_num_neighborhoods]; - gpr_mu_lock(&neighborhood->mu); - found_worker = - check_neighborhood_for_available_poller(exec_ctx, neighborhood); - gpr_mu_unlock(&neighborhood->mu); - } - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - } - } else if (grpc_exec_ctx_has_work(exec_ctx)) { - gpr_mu_unlock(&pollset->mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - } - if (worker->initialized_cv) { - gpr_cv_destroy(&worker->cv); - } - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, " .. remove worker"); - } - if (EMPTIED == worker_remove(pollset, worker)) { - pollset_maybe_finish_shutdown(exec_ctx, pollset); - } - GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker); - GPR_TIMER_END("end_worker", 0); -} - -/* pollset->po.mu lock must be held by the caller before calling this. - The function pollset_work() may temporarily release the lock (pollset->po.mu) - during the course of its execution but it will always re-acquire the lock and - ensure that it is held by the time the function returns */ -static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *ps, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - grpc_pollset_worker worker; - grpc_error *error = GRPC_ERROR_NONE; - static const char *err_desc = "pollset_work"; - GPR_TIMER_BEGIN("pollset_work", 0); - if (ps->kicked_without_poller) { - ps->kicked_without_poller = false; - GPR_TIMER_END("pollset_work", 0); - return GRPC_ERROR_NONE; - } - - if (begin_worker(ps, &worker, worker_hdl, &now, deadline)) { - gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps); - gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); - GPR_ASSERT(!ps->shutting_down); - GPR_ASSERT(!ps->seen_inactive); - - gpr_mu_unlock(&ps->mu); /* unlock */ - /* This is the designated polling thread at this point and should ideally do - polling. However, if there are unprocessed events left from a previous - call to do_epoll_wait(), skip calling epoll_wait() in this iteration and - process the pending epoll events. - - The reason for decoupling do_epoll_wait and process_epoll_events is to - better distrubute the work (i.e handling epoll events) across multiple - threads - - process_epoll_events() returns very quickly: It just queues the work on - exec_ctx but does not execute it (the actual exectution or more - accurately grpc_exec_ctx_flush() happens in end_worker() AFTER selecting - a designated poller). So we are not waiting long periods without a - designated poller */ - if (gpr_atm_acq_load(&g_epoll_set.cursor) == - gpr_atm_acq_load(&g_epoll_set.num_events)) { - append_error(&error, do_epoll_wait(exec_ctx, ps, now, deadline), - err_desc); - } - append_error(&error, process_epoll_events(exec_ctx, ps), err_desc); - - gpr_mu_lock(&ps->mu); /* lock */ - - gpr_tls_set(&g_current_thread_worker, 0); - } else { - gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps); - } - end_worker(exec_ctx, ps, &worker, worker_hdl); - - gpr_tls_set(&g_current_thread_pollset, 0); - GPR_TIMER_END("pollset_work", 0); - return error; -} - -static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *specific_worker) { - GPR_TIMER_BEGIN("pollset_kick", 0); - GRPC_STATS_INC_POLLSET_KICK(exec_ctx); - grpc_error *ret_err = GRPC_ERROR_NONE; - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_strvec log; - gpr_strvec_init(&log); - char *tmp; - gpr_asprintf( - &tmp, "PS:%p KICK:%p curps=%p curworker=%p root=%p", pollset, - specific_worker, (void *)gpr_tls_get(&g_current_thread_pollset), - (void *)gpr_tls_get(&g_current_thread_worker), pollset->root_worker); - gpr_strvec_add(&log, tmp); - if (pollset->root_worker != NULL) { - gpr_asprintf(&tmp, " {kick_state=%s next=%p {kick_state=%s}}", - kick_state_string(pollset->root_worker->state), - pollset->root_worker->next, - kick_state_string(pollset->root_worker->next->state)); - gpr_strvec_add(&log, tmp); - } - if (specific_worker != NULL) { - gpr_asprintf(&tmp, " worker_kick_state=%s", - kick_state_string(specific_worker->state)); - gpr_strvec_add(&log, tmp); - } - tmp = gpr_strvec_flatten(&log, NULL); - gpr_strvec_destroy(&log); - gpr_log(GPR_ERROR, "%s", tmp); - gpr_free(tmp); - } - - if (specific_worker == NULL) { - if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { - grpc_pollset_worker *root_worker = pollset->root_worker; - if (root_worker == NULL) { - GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(exec_ctx); - pollset->kicked_without_poller = true; - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kicked_without_poller"); - } - goto done; - } - grpc_pollset_worker *next_worker = root_worker->next; - if (root_worker->state == KICKED) { - GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. already kicked %p", root_worker); - } - SET_KICK_STATE(root_worker, KICKED); - goto done; - } else if (next_worker->state == KICKED) { - GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. already kicked %p", next_worker); - } - SET_KICK_STATE(next_worker, KICKED); - goto done; - } else if (root_worker == - next_worker && // only try and wake up a poller if - // there is no next worker - root_worker == (grpc_pollset_worker *)gpr_atm_no_barrier_load( - &g_active_poller)) { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kicked %p", root_worker); - } - SET_KICK_STATE(root_worker, KICKED); - ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); - goto done; - } else if (next_worker->state == UNKICKED) { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kicked %p", next_worker); - } - GPR_ASSERT(next_worker->initialized_cv); - SET_KICK_STATE(next_worker, KICKED); - gpr_cv_signal(&next_worker->cv); - goto done; - } else if (next_worker->state == DESIGNATED_POLLER) { - if (root_worker->state != DESIGNATED_POLLER) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log( - GPR_ERROR, - " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)", - root_worker, root_worker->initialized_cv, next_worker); - } - SET_KICK_STATE(root_worker, KICKED); - if (root_worker->initialized_cv) { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); - gpr_cv_signal(&root_worker->cv); - } - goto done; - } else { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. non-root poller %p (root=%p)", next_worker, - root_worker); - } - SET_KICK_STATE(next_worker, KICKED); - ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); - goto done; - } - } else { - GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); - GPR_ASSERT(next_worker->state == KICKED); - SET_KICK_STATE(next_worker, KICKED); - goto done; - } - } else { - GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kicked while waking up"); - } - goto done; - } - - GPR_UNREACHABLE_CODE(goto done); - } - - if (specific_worker->state == KICKED) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. specific worker already kicked"); - } - goto done; - } else if (gpr_tls_get(&g_current_thread_worker) == - (intptr_t)specific_worker) { - GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. mark %p kicked", specific_worker); - } - SET_KICK_STATE(specific_worker, KICKED); - goto done; - } else if (specific_worker == - (grpc_pollset_worker *)gpr_atm_no_barrier_load(&g_active_poller)) { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kick active poller"); - } - SET_KICK_STATE(specific_worker, KICKED); - ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); - goto done; - } else if (specific_worker->initialized_cv) { - GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kick waiting worker"); - } - SET_KICK_STATE(specific_worker, KICKED); - gpr_cv_signal(&specific_worker->cv); - goto done; - } else { - GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, " .. kick non-waiting worker"); - } - SET_KICK_STATE(specific_worker, KICKED); - goto done; - } -done: - GPR_TIMER_END("pollset_kick", 0); - return ret_err; -} - -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) {} - -/******************************************************************************* - * Pollset-set Definitions - */ - -static grpc_pollset_set *pollset_set_create(void) { - return (grpc_pollset_set *)((intptr_t)0xdeafbeef); -} - -static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss) {} - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) {} - -static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) {} - -static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) {} - -static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) {} - -static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) {} - -static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) {} - -/******************************************************************************* - * Event engine binding - */ - -static void shutdown_engine(void) { - fd_global_shutdown(); - pollset_global_shutdown(); - epoll_set_shutdown(); -} - -static const grpc_event_engine_vtable vtable = { - sizeof(grpc_pollset), - - fd_create, - fd_wrapped_fd, - fd_orphan, - fd_shutdown, - fd_notify_on_read, - fd_notify_on_write, - fd_is_shutdown, - fd_get_read_notifier_pollset, - - pollset_init, - pollset_shutdown, - pollset_destroy, - pollset_work, - pollset_kick, - pollset_add_fd, - - pollset_set_create, - pollset_set_destroy, - pollset_set_add_pollset, - pollset_set_del_pollset, - pollset_set_add_pollset_set, - pollset_set_del_pollset_set, - pollset_set_add_fd, - pollset_set_del_fd, - - shutdown_engine, -}; - -/* It is possible that GLIBC has epoll but the underlying kernel doesn't. - * Create epoll_fd (epoll_set_init() takes care of that) to make sure epoll - * support is available */ -const grpc_event_engine_vtable *grpc_init_epoll1_linux(bool explicit_request) { - if (!grpc_has_wakeup_fd()) { - return NULL; - } - - if (!epoll_set_init()) { - return NULL; - } - - fd_global_init(); - - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - fd_global_shutdown(); - epoll_set_shutdown(); - return NULL; - } - - return &vtable; -} - -#else /* defined(GRPC_LINUX_EPOLL) */ -#if defined(GRPC_POSIX_SOCKET) -#include "src/core/lib/iomgr/ev_posix.h" -/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return - * NULL */ -const grpc_event_engine_vtable *grpc_init_epoll1_linux(bool explicit_request) { - return NULL; -} -#endif /* defined(GRPC_POSIX_SOCKET) */ -#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc new file mode 100644 index 0000000000..3ac12ab56f --- /dev/null +++ b/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -0,0 +1,1267 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +/* This polling engine is only relevant on linux kernels supporting epoll() */ +#ifdef GRPC_LINUX_EPOLL + +#include "src/core/lib/iomgr/ev_epoll1_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/lockfree_event.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/string.h" + +static grpc_wakeup_fd global_wakeup_fd; + +/******************************************************************************* + * Singleton epoll set related fields + */ + +#define MAX_EPOLL_EVENTS 100 +#define MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION 1 + +/* NOTE ON SYNCHRONIZATION: + * - Fields in this struct are only modified by the designated poller. Hence + * there is no need for any locks to protect the struct. + * - num_events and cursor fields have to be of atomic type to provide memory + * visibility guarantees only. i.e In case of multiple pollers, the designated + * polling thread keeps changing; the thread that wrote these values may be + * different from the thread reading the values + */ +typedef struct epoll_set { + int epfd; + + /* The epoll_events after the last call to epoll_wait() */ + struct epoll_event events[MAX_EPOLL_EVENTS]; + + /* The number of epoll_events after the last call to epoll_wait() */ + gpr_atm num_events; + + /* Index of the first event in epoll_events that has to be processed. This + * field is only valid if num_events > 0 */ + gpr_atm cursor; +} epoll_set; + +/* The global singleton epoll set */ +static epoll_set g_epoll_set; + +/* Must be called *only* once */ +static bool epoll_set_init() { + g_epoll_set.epfd = epoll_create1(EPOLL_CLOEXEC); + if (g_epoll_set.epfd < 0) { + gpr_log(GPR_ERROR, "epoll unavailable"); + return false; + } + + gpr_log(GPR_INFO, "grpc epoll fd: %d", g_epoll_set.epfd); + gpr_atm_no_barrier_store(&g_epoll_set.num_events, 0); + gpr_atm_no_barrier_store(&g_epoll_set.cursor, 0); + return true; +} + +/* epoll_set_init() MUST be called before calling this. */ +static void epoll_set_shutdown() { + if (g_epoll_set.epfd >= 0) { + close(g_epoll_set.epfd); + g_epoll_set.epfd = -1; + } +} + +/******************************************************************************* + * Fd Declarations + */ + +struct grpc_fd { + int fd; + + gpr_atm read_closure; + gpr_atm write_closure; + + struct grpc_fd *freelist_next; + + /* The pollset that last noticed that the fd is readable. The actual type + * stored in this is (grpc_pollset *) */ + gpr_atm read_notifier_pollset; + + grpc_iomgr_object iomgr_object; +}; + +static void fd_global_init(void); +static void fd_global_shutdown(void); + +/******************************************************************************* + * Pollset Declarations + */ + +typedef enum { UNKICKED, KICKED, DESIGNATED_POLLER } kick_state; + +static const char *kick_state_string(kick_state st) { + switch (st) { + case UNKICKED: + return "UNKICKED"; + case KICKED: + return "KICKED"; + case DESIGNATED_POLLER: + return "DESIGNATED_POLLER"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +struct grpc_pollset_worker { + kick_state state; + int kick_state_mutator; // which line of code last changed kick state + bool initialized_cv; + grpc_pollset_worker *next; + grpc_pollset_worker *prev; + gpr_cv cv; + grpc_closure_list schedule_on_end_work; +}; + +#define SET_KICK_STATE(worker, kick_state) \ + do { \ + (worker)->state = (kick_state); \ + (worker)->kick_state_mutator = __LINE__; \ + } while (false) + +#define MAX_NEIGHBORHOODS 1024 + +typedef struct pollset_neighborhood { + gpr_mu mu; + grpc_pollset *active_root; + char pad[GPR_CACHELINE_SIZE]; +} pollset_neighborhood; + +struct grpc_pollset { + gpr_mu mu; + pollset_neighborhood *neighborhood; + bool reassigning_neighborhood; + grpc_pollset_worker *root_worker; + bool kicked_without_poller; + + /* Set to true if the pollset is observed to have no workers available to + poll */ + bool seen_inactive; + bool shutting_down; /* Is the pollset shutting down ? */ + grpc_closure *shutdown_closure; /* Called after after shutdown is complete */ + + /* Number of workers who are *about-to* attach themselves to the pollset + * worker list */ + int begin_refs; + + grpc_pollset *next; + grpc_pollset *prev; +}; + +/******************************************************************************* + * Pollset-set Declarations + */ + +struct grpc_pollset_set { + char unused; +}; + +/******************************************************************************* + * Common helpers + */ + +static bool append_error(grpc_error **composite, grpc_error *error, + const char *desc) { + if (error == GRPC_ERROR_NONE) return true; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + } + *composite = grpc_error_add_child(*composite, error); + return false; +} + +/******************************************************************************* + * Fd Definitions + */ + +/* We need to keep a freelist not because of any concerns of malloc performance + * but instead so that implementations with multiple threads in (for example) + * epoll_wait deal with the race between pollset removal and incoming poll + * notifications. + * + * The problem is that the poller ultimately holds a reference to this + * object, so it is very difficult to know when is safe to free it, at least + * without some expensive synchronization. + * + * If we keep the object freelisted, in the worst case losing this race just + * becomes a spurious read notification on a reused fd. + */ + +/* The alarm system needs to be able to wakeup 'some poller' sometimes + * (specifically when a new alarm needs to be triggered earlier than the next + * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a + * case occurs. */ + +static grpc_fd *fd_freelist = NULL; +static gpr_mu fd_freelist_mu; + +static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } + +static void fd_global_shutdown(void) { + gpr_mu_lock(&fd_freelist_mu); + gpr_mu_unlock(&fd_freelist_mu); + while (fd_freelist != NULL) { + grpc_fd *fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + gpr_free(fd); + } + gpr_mu_destroy(&fd_freelist_mu); +} + +static grpc_fd *fd_create(int fd, const char *name) { + grpc_fd *new_fd = NULL; + + gpr_mu_lock(&fd_freelist_mu); + if (fd_freelist != NULL) { + new_fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + } + gpr_mu_unlock(&fd_freelist_mu); + + if (new_fd == NULL) { + new_fd = (grpc_fd *)gpr_malloc(sizeof(grpc_fd)); + } + + new_fd->fd = fd; + grpc_lfev_init(&new_fd->read_closure); + grpc_lfev_init(&new_fd->write_closure); + gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); + + new_fd->freelist_next = NULL; + + char *fd_name; + gpr_asprintf(&fd_name, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); + } +#endif + gpr_free(fd_name); + + struct epoll_event ev; + ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); + ev.data.ptr = new_fd; + if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, fd, &ev) != 0) { + gpr_log(GPR_ERROR, "epoll_ctl failed: %s", strerror(errno)); + } + + return new_fd; +} + +static int fd_wrapped_fd(grpc_fd *fd) { return fd->fd; } + +/* if 'releasing_fd' is true, it means that we are going to detach the internal + * fd from grpc_fd structure (i.e which means we should not be calling + * shutdown() syscall on that fd) */ +static void fd_shutdown_internal(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_error *why, bool releasing_fd) { + if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, + GRPC_ERROR_REF(why))) { + if (!releasing_fd) { + shutdown(fd->fd, SHUT_RDWR); + } + grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); + } + GRPC_ERROR_UNREF(why); +} + +/* Might be called multiple times */ +static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { + fd_shutdown_internal(exec_ctx, fd, why, false); +} + +static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *on_done, int *release_fd, + bool already_closed, const char *reason) { + grpc_error *error = GRPC_ERROR_NONE; + bool is_release_fd = (release_fd != NULL); + + if (!grpc_lfev_is_shutdown(&fd->read_closure)) { + fd_shutdown_internal(exec_ctx, fd, + GRPC_ERROR_CREATE_FROM_COPIED_STRING(reason), + is_release_fd); + } + + /* If release_fd is not NULL, we should be relinquishing control of the file + descriptor fd->fd (but we still own the grpc_fd structure). */ + if (is_release_fd) { + *release_fd = fd->fd; + } else if (!already_closed) { + close(fd->fd); + } + + GRPC_CLOSURE_SCHED(exec_ctx, on_done, GRPC_ERROR_REF(error)); + + grpc_iomgr_unregister_object(&fd->iomgr_object); + grpc_lfev_destroy(&fd->read_closure); + grpc_lfev_destroy(&fd->write_closure); + + gpr_mu_lock(&fd_freelist_mu); + fd->freelist_next = fd_freelist; + fd_freelist = fd; + gpr_mu_unlock(&fd_freelist_mu); +} + +static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, + grpc_fd *fd) { + gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); + return (grpc_pollset *)notifier; +} + +static bool fd_is_shutdown(grpc_fd *fd) { + return grpc_lfev_is_shutdown(&fd->read_closure); +} + +static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); +} + +static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); +} + +static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_pollset *notifier) { + grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); + /* Use release store to match with acquire load in fd_get_read_notifier */ + gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); +} + +static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); +} + +/******************************************************************************* + * Pollset Definitions + */ + +GPR_TLS_DECL(g_current_thread_pollset); +GPR_TLS_DECL(g_current_thread_worker); + +/* The designated poller */ +static gpr_atm g_active_poller; + +static pollset_neighborhood *g_neighborhoods; +static size_t g_num_neighborhoods; + +/* Return true if first in list */ +static bool worker_insert(grpc_pollset *pollset, grpc_pollset_worker *worker) { + if (pollset->root_worker == NULL) { + pollset->root_worker = worker; + worker->next = worker->prev = worker; + return true; + } else { + worker->next = pollset->root_worker; + worker->prev = worker->next->prev; + worker->next->prev = worker; + worker->prev->next = worker; + return false; + } +} + +/* Return true if last in list */ +typedef enum { EMPTIED, NEW_ROOT, REMOVED } worker_remove_result; + +static worker_remove_result worker_remove(grpc_pollset *pollset, + grpc_pollset_worker *worker) { + if (worker == pollset->root_worker) { + if (worker == worker->next) { + pollset->root_worker = NULL; + return EMPTIED; + } else { + pollset->root_worker = worker->next; + worker->prev->next = worker->next; + worker->next->prev = worker->prev; + return NEW_ROOT; + } + } else { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; + return REMOVED; + } +} + +static size_t choose_neighborhood(void) { + return (size_t)gpr_cpu_current_cpu() % g_num_neighborhoods; +} + +static grpc_error *pollset_global_init(void) { + gpr_tls_init(&g_current_thread_pollset); + gpr_tls_init(&g_current_thread_worker); + gpr_atm_no_barrier_store(&g_active_poller, 0); + global_wakeup_fd.read_fd = -1; + grpc_error *err = grpc_wakeup_fd_init(&global_wakeup_fd); + if (err != GRPC_ERROR_NONE) return err; + struct epoll_event ev; + ev.events = (uint32_t)(EPOLLIN | EPOLLET); + ev.data.ptr = &global_wakeup_fd; + if (epoll_ctl(g_epoll_set.epfd, EPOLL_CTL_ADD, global_wakeup_fd.read_fd, + &ev) != 0) { + return GRPC_OS_ERROR(errno, "epoll_ctl"); + } + g_num_neighborhoods = GPR_CLAMP(gpr_cpu_num_cores(), 1, MAX_NEIGHBORHOODS); + g_neighborhoods = (pollset_neighborhood *)gpr_zalloc( + sizeof(*g_neighborhoods) * g_num_neighborhoods); + for (size_t i = 0; i < g_num_neighborhoods; i++) { + gpr_mu_init(&g_neighborhoods[i].mu); + } + return GRPC_ERROR_NONE; +} + +static void pollset_global_shutdown(void) { + gpr_tls_destroy(&g_current_thread_pollset); + gpr_tls_destroy(&g_current_thread_worker); + if (global_wakeup_fd.read_fd != -1) grpc_wakeup_fd_destroy(&global_wakeup_fd); + for (size_t i = 0; i < g_num_neighborhoods; i++) { + gpr_mu_destroy(&g_neighborhoods[i].mu); + } + gpr_free(g_neighborhoods); +} + +static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; + pollset->neighborhood = &g_neighborhoods[choose_neighborhood()]; + pollset->reassigning_neighborhood = false; + pollset->root_worker = NULL; + pollset->kicked_without_poller = false; + pollset->seen_inactive = true; + pollset->shutting_down = false; + pollset->shutdown_closure = NULL; + pollset->begin_refs = 0; + pollset->next = pollset->prev = NULL; +} + +static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + gpr_mu_lock(&pollset->mu); + if (!pollset->seen_inactive) { + pollset_neighborhood *neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + retry_lock_neighborhood: + gpr_mu_lock(&neighborhood->mu); + gpr_mu_lock(&pollset->mu); + if (!pollset->seen_inactive) { + if (pollset->neighborhood != neighborhood) { + gpr_mu_unlock(&neighborhood->mu); + neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + goto retry_lock_neighborhood; + } + pollset->prev->next = pollset->next; + pollset->next->prev = pollset->prev; + if (pollset == pollset->neighborhood->active_root) { + pollset->neighborhood->active_root = + pollset->next == pollset ? NULL : pollset->next; + } + } + gpr_mu_unlock(&pollset->neighborhood->mu); + } + gpr_mu_unlock(&pollset->mu); + gpr_mu_destroy(&pollset->mu); +} + +static grpc_error *pollset_kick_all(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset) { + GPR_TIMER_BEGIN("pollset_kick_all", 0); + grpc_error *error = GRPC_ERROR_NONE; + if (pollset->root_worker != NULL) { + grpc_pollset_worker *worker = pollset->root_worker; + do { + GRPC_STATS_INC_POLLSET_KICK(exec_ctx); + switch (worker->state) { + case KICKED: + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); + break; + case UNKICKED: + SET_KICK_STATE(worker, KICKED); + if (worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); + gpr_cv_signal(&worker->cv); + } + break; + case DESIGNATED_POLLER: + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); + SET_KICK_STATE(worker, KICKED); + append_error(&error, grpc_wakeup_fd_wakeup(&global_wakeup_fd), + "pollset_kick_all"); + break; + } + + worker = worker->next; + } while (worker != pollset->root_worker); + } + // TODO: sreek. Check if we need to set 'kicked_without_poller' to true here + // in the else case + GPR_TIMER_END("pollset_kick_all", 0); + return error; +} + +static void pollset_maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset) { + if (pollset->shutdown_closure != NULL && pollset->root_worker == NULL && + pollset->begin_refs == 0) { + GPR_TIMER_MARK("pollset_finish_shutdown", 0); + GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_closure, GRPC_ERROR_NONE); + pollset->shutdown_closure = NULL; + } +} + +static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + GPR_TIMER_BEGIN("pollset_shutdown", 0); + GPR_ASSERT(pollset->shutdown_closure == NULL); + GPR_ASSERT(!pollset->shutting_down); + pollset->shutdown_closure = closure; + pollset->shutting_down = true; + GRPC_LOG_IF_ERROR("pollset_shutdown", pollset_kick_all(exec_ctx, pollset)); + pollset_maybe_finish_shutdown(exec_ctx, pollset); + GPR_TIMER_END("pollset_shutdown", 0); +} + +static int poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return -1; + } + + if (gpr_time_cmp(deadline, now) <= 0) { + return 0; + } + + static const gpr_timespec round_up = { + 0, /* tv_sec */ + GPR_NS_PER_MS - 1, /* tv_nsec */ + GPR_TIMESPAN /* clock_type */ + }; + timeout = gpr_time_sub(deadline, now); + int millis = gpr_time_to_millis(gpr_time_add(timeout, round_up)); + return millis >= 1 ? millis : 1; +} + +/* Process the epoll events found by do_epoll_wait() function. + - g_epoll_set.cursor points to the index of the first event to be processed + - This function then processes up-to MAX_EPOLL_EVENTS_PER_ITERATION and + updates the g_epoll_set.cursor + + NOTE ON SYNCRHONIZATION: Similar to do_epoll_wait(), this function is only + called by g_active_poller thread. So there is no need for synchronization + when accessing fields in g_epoll_set */ +static grpc_error *process_epoll_events(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset) { + static const char *err_desc = "process_events"; + grpc_error *error = GRPC_ERROR_NONE; + + GPR_TIMER_BEGIN("process_epoll_events", 0); + long num_events = gpr_atm_acq_load(&g_epoll_set.num_events); + long cursor = gpr_atm_acq_load(&g_epoll_set.cursor); + for (int idx = 0; + (idx < MAX_EPOLL_EVENTS_HANDLED_PER_ITERATION) && cursor != num_events; + idx++) { + long c = cursor++; + struct epoll_event *ev = &g_epoll_set.events[c]; + void *data_ptr = ev->data.ptr; + + if (data_ptr == &global_wakeup_fd) { + append_error(&error, grpc_wakeup_fd_consume_wakeup(&global_wakeup_fd), + err_desc); + } else { + grpc_fd *fd = (grpc_fd *)(data_ptr); + bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0; + bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0; + bool write_ev = (ev->events & EPOLLOUT) != 0; + + if (read_ev || cancel) { + fd_become_readable(exec_ctx, fd, pollset); + } + + if (write_ev || cancel) { + fd_become_writable(exec_ctx, fd); + } + } + } + gpr_atm_rel_store(&g_epoll_set.cursor, cursor); + GPR_TIMER_END("process_epoll_events", 0); + return error; +} + +/* Do epoll_wait and store the events in g_epoll_set.events field. This does not + "process" any of the events yet; that is done in process_epoll_events(). + *See process_epoll_events() function for more details. + + NOTE ON SYNCHRONIZATION: At any point of time, only the g_active_poller + (i.e the designated poller thread) will be calling this function. So there is + no need for any synchronization when accesing fields in g_epoll_set */ +static grpc_error *do_epoll_wait(grpc_exec_ctx *exec_ctx, grpc_pollset *ps, + gpr_timespec now, gpr_timespec deadline) { + GPR_TIMER_BEGIN("do_epoll_wait", 0); + + int r; + int timeout = poll_deadline_to_millis_timeout(deadline, now); + if (timeout != 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + } + do { + GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); + r = epoll_wait(g_epoll_set.epfd, g_epoll_set.events, MAX_EPOLL_EVENTS, + timeout); + } while (r < 0 && errno == EINTR); + if (timeout != 0) { + GRPC_SCHEDULING_END_BLOCKING_REGION; + } + + if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait"); + + GRPC_STATS_INC_POLL_EVENTS_RETURNED(exec_ctx, r); + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "ps: %p poll got %d events", ps, r); + } + + gpr_atm_rel_store(&g_epoll_set.num_events, r); + gpr_atm_rel_store(&g_epoll_set.cursor, 0); + + GPR_TIMER_END("do_epoll_wait", 0); + return GRPC_ERROR_NONE; +} + +static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker, + grpc_pollset_worker **worker_hdl, gpr_timespec *now, + gpr_timespec deadline) { + GPR_TIMER_BEGIN("begin_worker", 0); + if (worker_hdl != NULL) *worker_hdl = worker; + worker->initialized_cv = false; + SET_KICK_STATE(worker, UNKICKED); + worker->schedule_on_end_work = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; + pollset->begin_refs++; + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, "PS:%p BEGIN_STARTS:%p", pollset, worker); + } + + if (pollset->seen_inactive) { + // pollset has been observed to be inactive, we need to move back to the + // active list + bool is_reassigning = false; + if (!pollset->reassigning_neighborhood) { + is_reassigning = true; + pollset->reassigning_neighborhood = true; + pollset->neighborhood = &g_neighborhoods[choose_neighborhood()]; + } + pollset_neighborhood *neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + // pollset unlocked: state may change (even worker->kick_state) + retry_lock_neighborhood: + gpr_mu_lock(&neighborhood->mu); + gpr_mu_lock(&pollset->mu); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, "PS:%p BEGIN_REORG:%p kick_state=%s is_reassigning=%d", + pollset, worker, kick_state_string(worker->state), + is_reassigning); + } + if (pollset->seen_inactive) { + if (neighborhood != pollset->neighborhood) { + gpr_mu_unlock(&neighborhood->mu); + neighborhood = pollset->neighborhood; + gpr_mu_unlock(&pollset->mu); + goto retry_lock_neighborhood; + } + + /* In the brief time we released the pollset locks above, the worker MAY + have been kicked. In this case, the worker should get out of this + pollset ASAP and hence this should neither add the pollset to + neighborhood nor mark the pollset as active. + + On a side note, the only way a worker's kick state could have changed + at this point is if it were "kicked specifically". Since the worker has + not added itself to the pollset yet (by calling worker_insert()), it is + not visible in the "kick any" path yet */ + if (worker->state == UNKICKED) { + pollset->seen_inactive = false; + if (neighborhood->active_root == NULL) { + neighborhood->active_root = pollset->next = pollset->prev = pollset; + /* Make this the designated poller if there isn't one already */ + if (worker->state == UNKICKED && + gpr_atm_no_barrier_cas(&g_active_poller, 0, (gpr_atm)worker)) { + SET_KICK_STATE(worker, DESIGNATED_POLLER); + } + } else { + pollset->next = neighborhood->active_root; + pollset->prev = pollset->next->prev; + pollset->next->prev = pollset->prev->next = pollset; + } + } + } + if (is_reassigning) { + GPR_ASSERT(pollset->reassigning_neighborhood); + pollset->reassigning_neighborhood = false; + } + gpr_mu_unlock(&neighborhood->mu); + } + + worker_insert(pollset, worker); + pollset->begin_refs--; + if (worker->state == UNKICKED && !pollset->kicked_without_poller) { + GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker); + worker->initialized_cv = true; + gpr_cv_init(&worker->cv); + while (worker->state == UNKICKED && !pollset->shutting_down) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, "PS:%p BEGIN_WAIT:%p kick_state=%s shutdown=%d", + pollset, worker, kick_state_string(worker->state), + pollset->shutting_down); + } + + if (gpr_cv_wait(&worker->cv, &pollset->mu, deadline) && + worker->state == UNKICKED) { + /* If gpr_cv_wait returns true (i.e a timeout), pretend that the worker + received a kick */ + SET_KICK_STATE(worker, KICKED); + } + } + *now = gpr_now(now->clock_type); + } + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, + "PS:%p BEGIN_DONE:%p kick_state=%s shutdown=%d " + "kicked_without_poller: %d", + pollset, worker, kick_state_string(worker->state), + pollset->shutting_down, pollset->kicked_without_poller); + } + + /* We release pollset lock in this function at a couple of places: + * 1. Briefly when assigning pollset to a neighborhood + * 2. When doing gpr_cv_wait() + * It is possible that 'kicked_without_poller' was set to true during (1) and + * 'shutting_down' is set to true during (1) or (2). If either of them is + * true, this worker cannot do polling */ + /* TODO(sreek): Perhaps there is a better way to handle kicked_without_poller + * case; especially when the worker is the DESIGNATED_POLLER */ + + if (pollset->kicked_without_poller) { + pollset->kicked_without_poller = false; + GPR_TIMER_END("begin_worker", 0); + return false; + } + + GPR_TIMER_END("begin_worker", 0); + return worker->state == DESIGNATED_POLLER && !pollset->shutting_down; +} + +static bool check_neighborhood_for_available_poller( + grpc_exec_ctx *exec_ctx, pollset_neighborhood *neighborhood) { + GPR_TIMER_BEGIN("check_neighborhood_for_available_poller", 0); + bool found_worker = false; + do { + grpc_pollset *inspect = neighborhood->active_root; + if (inspect == NULL) { + break; + } + gpr_mu_lock(&inspect->mu); + GPR_ASSERT(!inspect->seen_inactive); + grpc_pollset_worker *inspect_worker = inspect->root_worker; + if (inspect_worker != NULL) { + do { + switch (inspect_worker->state) { + case UNKICKED: + if (gpr_atm_no_barrier_cas(&g_active_poller, 0, + (gpr_atm)inspect_worker)) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, " .. choose next poller to be %p", + inspect_worker); + } + SET_KICK_STATE(inspect_worker, DESIGNATED_POLLER); + if (inspect_worker->initialized_cv) { + GPR_TIMER_MARK("signal worker", 0); + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); + gpr_cv_signal(&inspect_worker->cv); + } + } else { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, " .. beaten to choose next poller"); + } + } + // even if we didn't win the cas, there's a worker, we can stop + found_worker = true; + break; + case KICKED: + break; + case DESIGNATED_POLLER: + found_worker = true; // ok, so someone else found the worker, but + // we'll accept that + break; + } + inspect_worker = inspect_worker->next; + } while (!found_worker && inspect_worker != inspect->root_worker); + } + if (!found_worker) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, " .. mark pollset %p inactive", inspect); + } + inspect->seen_inactive = true; + if (inspect == neighborhood->active_root) { + neighborhood->active_root = + inspect->next == inspect ? NULL : inspect->next; + } + inspect->next->prev = inspect->prev; + inspect->prev->next = inspect->next; + inspect->next = inspect->prev = NULL; + } + gpr_mu_unlock(&inspect->mu); + } while (!found_worker); + GPR_TIMER_END("check_neighborhood_for_available_poller", 0); + return found_worker; +} + +static void end_worker(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *worker, + grpc_pollset_worker **worker_hdl) { + GPR_TIMER_BEGIN("end_worker", 0); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p END_WORKER:%p", pollset, worker); + } + if (worker_hdl != NULL) *worker_hdl = NULL; + /* Make sure we appear kicked */ + SET_KICK_STATE(worker, KICKED); + grpc_closure_list_move(&worker->schedule_on_end_work, + &exec_ctx->closure_list); + if (gpr_atm_no_barrier_load(&g_active_poller) == (gpr_atm)worker) { + if (worker->next != worker && worker->next->state == UNKICKED) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, " .. choose next poller to be peer %p", worker); + } + GPR_ASSERT(worker->next->initialized_cv); + gpr_atm_no_barrier_store(&g_active_poller, (gpr_atm)worker->next); + SET_KICK_STATE(worker->next, DESIGNATED_POLLER); + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); + gpr_cv_signal(&worker->next->cv); + if (grpc_exec_ctx_has_work(exec_ctx)) { + gpr_mu_unlock(&pollset->mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); + } + } else { + gpr_atm_no_barrier_store(&g_active_poller, 0); + size_t poller_neighborhood_idx = + (size_t)(pollset->neighborhood - g_neighborhoods); + gpr_mu_unlock(&pollset->mu); + bool found_worker = false; + bool scan_state[MAX_NEIGHBORHOODS]; + for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) { + pollset_neighborhood *neighborhood = + &g_neighborhoods[(poller_neighborhood_idx + i) % + g_num_neighborhoods]; + if (gpr_mu_trylock(&neighborhood->mu)) { + found_worker = + check_neighborhood_for_available_poller(exec_ctx, neighborhood); + gpr_mu_unlock(&neighborhood->mu); + scan_state[i] = true; + } else { + scan_state[i] = false; + } + } + for (size_t i = 0; !found_worker && i < g_num_neighborhoods; i++) { + if (scan_state[i]) continue; + pollset_neighborhood *neighborhood = + &g_neighborhoods[(poller_neighborhood_idx + i) % + g_num_neighborhoods]; + gpr_mu_lock(&neighborhood->mu); + found_worker = + check_neighborhood_for_available_poller(exec_ctx, neighborhood); + gpr_mu_unlock(&neighborhood->mu); + } + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); + } + } else if (grpc_exec_ctx_has_work(exec_ctx)) { + gpr_mu_unlock(&pollset->mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); + } + if (worker->initialized_cv) { + gpr_cv_destroy(&worker->cv); + } + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, " .. remove worker"); + } + if (EMPTIED == worker_remove(pollset, worker)) { + pollset_maybe_finish_shutdown(exec_ctx, pollset); + } + GPR_ASSERT(gpr_atm_no_barrier_load(&g_active_poller) != (gpr_atm)worker); + GPR_TIMER_END("end_worker", 0); +} + +/* pollset->po.mu lock must be held by the caller before calling this. + The function pollset_work() may temporarily release the lock (pollset->po.mu) + during the course of its execution but it will always re-acquire the lock and + ensure that it is held by the time the function returns */ +static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *ps, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + grpc_pollset_worker worker; + grpc_error *error = GRPC_ERROR_NONE; + static const char *err_desc = "pollset_work"; + GPR_TIMER_BEGIN("pollset_work", 0); + if (ps->kicked_without_poller) { + ps->kicked_without_poller = false; + GPR_TIMER_END("pollset_work", 0); + return GRPC_ERROR_NONE; + } + + if (begin_worker(ps, &worker, worker_hdl, &now, deadline)) { + gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps); + gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); + GPR_ASSERT(!ps->shutting_down); + GPR_ASSERT(!ps->seen_inactive); + + gpr_mu_unlock(&ps->mu); /* unlock */ + /* This is the designated polling thread at this point and should ideally do + polling. However, if there are unprocessed events left from a previous + call to do_epoll_wait(), skip calling epoll_wait() in this iteration and + process the pending epoll events. + + The reason for decoupling do_epoll_wait and process_epoll_events is to + better distrubute the work (i.e handling epoll events) across multiple + threads + + process_epoll_events() returns very quickly: It just queues the work on + exec_ctx but does not execute it (the actual exectution or more + accurately grpc_exec_ctx_flush() happens in end_worker() AFTER selecting + a designated poller). So we are not waiting long periods without a + designated poller */ + if (gpr_atm_acq_load(&g_epoll_set.cursor) == + gpr_atm_acq_load(&g_epoll_set.num_events)) { + append_error(&error, do_epoll_wait(exec_ctx, ps, now, deadline), + err_desc); + } + append_error(&error, process_epoll_events(exec_ctx, ps), err_desc); + + gpr_mu_lock(&ps->mu); /* lock */ + + gpr_tls_set(&g_current_thread_worker, 0); + } else { + gpr_tls_set(&g_current_thread_pollset, (intptr_t)ps); + } + end_worker(exec_ctx, ps, &worker, worker_hdl); + + gpr_tls_set(&g_current_thread_pollset, 0); + GPR_TIMER_END("pollset_work", 0); + return error; +} + +static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *specific_worker) { + GPR_TIMER_BEGIN("pollset_kick", 0); + GRPC_STATS_INC_POLLSET_KICK(exec_ctx); + grpc_error *ret_err = GRPC_ERROR_NONE; + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_strvec log; + gpr_strvec_init(&log); + char *tmp; + gpr_asprintf( + &tmp, "PS:%p KICK:%p curps=%p curworker=%p root=%p", pollset, + specific_worker, (void *)gpr_tls_get(&g_current_thread_pollset), + (void *)gpr_tls_get(&g_current_thread_worker), pollset->root_worker); + gpr_strvec_add(&log, tmp); + if (pollset->root_worker != NULL) { + gpr_asprintf(&tmp, " {kick_state=%s next=%p {kick_state=%s}}", + kick_state_string(pollset->root_worker->state), + pollset->root_worker->next, + kick_state_string(pollset->root_worker->next->state)); + gpr_strvec_add(&log, tmp); + } + if (specific_worker != NULL) { + gpr_asprintf(&tmp, " worker_kick_state=%s", + kick_state_string(specific_worker->state)); + gpr_strvec_add(&log, tmp); + } + tmp = gpr_strvec_flatten(&log, NULL); + gpr_strvec_destroy(&log); + gpr_log(GPR_ERROR, "%s", tmp); + gpr_free(tmp); + } + + if (specific_worker == NULL) { + if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { + grpc_pollset_worker *root_worker = pollset->root_worker; + if (root_worker == NULL) { + GRPC_STATS_INC_POLLSET_KICKED_WITHOUT_POLLER(exec_ctx); + pollset->kicked_without_poller = true; + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kicked_without_poller"); + } + goto done; + } + grpc_pollset_worker *next_worker = root_worker->next; + if (root_worker->state == KICKED) { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. already kicked %p", root_worker); + } + SET_KICK_STATE(root_worker, KICKED); + goto done; + } else if (next_worker->state == KICKED) { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. already kicked %p", next_worker); + } + SET_KICK_STATE(next_worker, KICKED); + goto done; + } else if (root_worker == + next_worker && // only try and wake up a poller if + // there is no next worker + root_worker == (grpc_pollset_worker *)gpr_atm_no_barrier_load( + &g_active_poller)) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kicked %p", root_worker); + } + SET_KICK_STATE(root_worker, KICKED); + ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); + goto done; + } else if (next_worker->state == UNKICKED) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kicked %p", next_worker); + } + GPR_ASSERT(next_worker->initialized_cv); + SET_KICK_STATE(next_worker, KICKED); + gpr_cv_signal(&next_worker->cv); + goto done; + } else if (next_worker->state == DESIGNATED_POLLER) { + if (root_worker->state != DESIGNATED_POLLER) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log( + GPR_ERROR, + " .. kicked root non-poller %p (initialized_cv=%d) (poller=%p)", + root_worker, root_worker->initialized_cv, next_worker); + } + SET_KICK_STATE(root_worker, KICKED); + if (root_worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); + gpr_cv_signal(&root_worker->cv); + } + goto done; + } else { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. non-root poller %p (root=%p)", next_worker, + root_worker); + } + SET_KICK_STATE(next_worker, KICKED); + ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); + goto done; + } + } else { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); + GPR_ASSERT(next_worker->state == KICKED); + SET_KICK_STATE(next_worker, KICKED); + goto done; + } + } else { + GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kicked while waking up"); + } + goto done; + } + + GPR_UNREACHABLE_CODE(goto done); + } + + if (specific_worker->state == KICKED) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. specific worker already kicked"); + } + goto done; + } else if (gpr_tls_get(&g_current_thread_worker) == + (intptr_t)specific_worker) { + GRPC_STATS_INC_POLLSET_KICK_OWN_THREAD(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. mark %p kicked", specific_worker); + } + SET_KICK_STATE(specific_worker, KICKED); + goto done; + } else if (specific_worker == + (grpc_pollset_worker *)gpr_atm_no_barrier_load(&g_active_poller)) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_FD(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kick active poller"); + } + SET_KICK_STATE(specific_worker, KICKED); + ret_err = grpc_wakeup_fd_wakeup(&global_wakeup_fd); + goto done; + } else if (specific_worker->initialized_cv) { + GRPC_STATS_INC_POLLSET_KICK_WAKEUP_CV(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kick waiting worker"); + } + SET_KICK_STATE(specific_worker, KICKED); + gpr_cv_signal(&specific_worker->cv); + goto done; + } else { + GRPC_STATS_INC_POLLSET_KICKED_AGAIN(exec_ctx); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, " .. kick non-waiting worker"); + } + SET_KICK_STATE(specific_worker, KICKED); + goto done; + } +done: + GPR_TIMER_END("pollset_kick", 0); + return ret_err; +} + +static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) {} + +/******************************************************************************* + * Pollset-set Definitions + */ + +static grpc_pollset_set *pollset_set_create(void) { + return (grpc_pollset_set *)((intptr_t)0xdeafbeef); +} + +static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss) {} + +static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, + grpc_fd *fd) {} + +static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, + grpc_fd *fd) {} + +static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss, grpc_pollset *ps) {} + +static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss, grpc_pollset *ps) {} + +static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) {} + +static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) {} + +/******************************************************************************* + * Event engine binding + */ + +static void shutdown_engine(void) { + fd_global_shutdown(); + pollset_global_shutdown(); + epoll_set_shutdown(); +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_is_shutdown, + fd_get_read_notifier_pollset, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_destroy, + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_engine, +}; + +/* It is possible that GLIBC has epoll but the underlying kernel doesn't. + * Create epoll_fd (epoll_set_init() takes care of that) to make sure epoll + * support is available */ +const grpc_event_engine_vtable *grpc_init_epoll1_linux(bool explicit_request) { + if (!grpc_has_wakeup_fd()) { + return NULL; + } + + if (!epoll_set_init()) { + return NULL; + } + + fd_global_init(); + + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + fd_global_shutdown(); + epoll_set_shutdown(); + return NULL; + } + + return &vtable; +} + +#else /* defined(GRPC_LINUX_EPOLL) */ +#if defined(GRPC_POSIX_SOCKET) +#include "src/core/lib/iomgr/ev_posix.h" +/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return + * NULL */ +const grpc_event_engine_vtable *grpc_init_epoll1_linux(bool explicit_request) { + return NULL; +} +#endif /* defined(GRPC_POSIX_SOCKET) */ +#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epollex_linux.c b/src/core/lib/iomgr/ev_epollex_linux.c deleted file mode 100644 index 8eb4de44d9..0000000000 --- a/src/core/lib/iomgr/ev_epollex_linux.c +++ /dev/null @@ -1,1461 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GRPC_LINUX_EPOLL - -#include "src/core/lib/iomgr/ev_epollex_linux.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/is_epollexclusive_available.h" -#include "src/core/lib/iomgr/lockfree_event.h" -#include "src/core/lib/iomgr/sys_epoll_wrapper.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/spinlock.h" - -/******************************************************************************* - * Polling object - */ - -typedef enum { - PO_POLLING_GROUP, - PO_POLLSET_SET, - PO_POLLSET, - PO_FD, /* ordering is important: we always want to lock pollsets before fds: - this guarantees that using an fd as a pollable is safe */ - PO_EMPTY_POLLABLE, - PO_COUNT -} polling_obj_type; - -typedef struct polling_obj polling_obj; -typedef struct polling_group polling_group; - -struct polling_obj { - gpr_mu mu; - polling_obj_type type; - polling_group *group; - struct polling_obj *next; - struct polling_obj *prev; -}; - -struct polling_group { - polling_obj po; - gpr_refcount refs; -}; - -static void po_init(polling_obj *po, polling_obj_type type); -static void po_destroy(polling_obj *po); -static void po_join(grpc_exec_ctx *exec_ctx, polling_obj *a, polling_obj *b); -static int po_cmp(polling_obj *a, polling_obj *b); - -static void pg_create(grpc_exec_ctx *exec_ctx, polling_obj **initial_po, - size_t initial_po_count); -static polling_group *pg_ref(polling_group *pg); -static void pg_unref(polling_group *pg); -static void pg_merge(grpc_exec_ctx *exec_ctx, polling_group *a, - polling_group *b); -static void pg_join(grpc_exec_ctx *exec_ctx, polling_group *pg, - polling_obj *po); - -/******************************************************************************* - * pollable Declarations - */ - -typedef struct pollable { - polling_obj po; - int epfd; - grpc_wakeup_fd wakeup; - grpc_pollset_worker *root_worker; -} pollable; - -static const char *polling_obj_type_string(polling_obj_type t) { - switch (t) { - case PO_POLLING_GROUP: - return "polling_group"; - case PO_POLLSET_SET: - return "pollset_set"; - case PO_POLLSET: - return "pollset"; - case PO_FD: - return "fd"; - case PO_EMPTY_POLLABLE: - return "empty_pollable"; - case PO_COUNT: - return ""; - } - return ""; -} - -static char *pollable_desc(pollable *p) { - char *out; - gpr_asprintf(&out, "type=%s group=%p epfd=%d wakeup=%d", - polling_obj_type_string(p->po.type), p->po.group, p->epfd, - p->wakeup.read_fd); - return out; -} - -static pollable g_empty_pollable; - -static void pollable_init(pollable *p, polling_obj_type type); -static void pollable_destroy(pollable *p); -/* ensure that p->epfd, p->wakeup are initialized; p->po.mu must be held */ -static grpc_error *pollable_materialize(pollable *p); - -/******************************************************************************* - * Fd Declarations - */ - -struct grpc_fd { - pollable pollable_obj; - int fd; - /* refst format: - bit 0 : 1=Active / 0=Orphaned - bits 1-n : refcount - Ref/Unref by two to avoid altering the orphaned bit */ - gpr_atm refst; - - /* The fd is either closed or we relinquished control of it. In either - cases, this indicates that the 'fd' on this structure is no longer - valid */ - gpr_mu orphaned_mu; - bool orphaned; - - gpr_atm read_closure; - gpr_atm write_closure; - - struct grpc_fd *freelist_next; - grpc_closure *on_done_closure; - - /* The pollset that last noticed that the fd is readable. The actual type - * stored in this is (grpc_pollset *) */ - gpr_atm read_notifier_pollset; - - grpc_iomgr_object iomgr_object; -}; - -static void fd_global_init(void); -static void fd_global_shutdown(void); - -/******************************************************************************* - * Pollset Declarations - */ - -typedef struct pollset_worker_link { - grpc_pollset_worker *next; - grpc_pollset_worker *prev; -} pollset_worker_link; - -typedef enum { - PWL_POLLSET, - PWL_POLLABLE, - POLLSET_WORKER_LINK_COUNT -} pollset_worker_links; - -struct grpc_pollset_worker { - bool kicked; - bool initialized_cv; - pollset_worker_link links[POLLSET_WORKER_LINK_COUNT]; - gpr_cv cv; - grpc_pollset *pollset; - pollable *pollable_obj; -}; - -#define MAX_EPOLL_EVENTS 100 -#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 5 - -struct grpc_pollset { - pollable pollable_obj; - pollable *current_pollable_obj; - int kick_alls_pending; - bool kicked_without_poller; - grpc_closure *shutdown_closure; - grpc_pollset_worker *root_worker; - - int event_cursor; - int event_count; - struct epoll_event events[MAX_EPOLL_EVENTS]; -}; - -/******************************************************************************* - * Pollset-set Declarations - */ -struct grpc_pollset_set { - polling_obj po; -}; - -/******************************************************************************* - * Common helpers - */ - -static bool append_error(grpc_error **composite, grpc_error *error, - const char *desc) { - if (error == GRPC_ERROR_NONE) return true; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); - } - *composite = grpc_error_add_child(*composite, error); - return false; -} - -/******************************************************************************* - * Fd Definitions - */ - -/* We need to keep a freelist not because of any concerns of malloc performance - * but instead so that implementations with multiple threads in (for example) - * epoll_wait deal with the race between pollset removal and incoming poll - * notifications. - * - * The problem is that the poller ultimately holds a reference to this - * object, so it is very difficult to know when is safe to free it, at least - * without some expensive synchronization. - * - * If we keep the object freelisted, in the worst case losing this race just - * becomes a spurious read notification on a reused fd. - */ - -/* The alarm system needs to be able to wakeup 'some poller' sometimes - * (specifically when a new alarm needs to be triggered earlier than the next - * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a - * case occurs. */ - -static grpc_fd *fd_freelist = NULL; -static gpr_mu fd_freelist_mu; - -#ifndef NDEBUG -#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) -#define UNREF_BY(ec, fd, n, reason) \ - unref_by(ec, fd, n, reason, __FILE__, __LINE__) -static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, - "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", - fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), - gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); - } -#else -#define REF_BY(fd, n, reason) ref_by(fd, n) -#define UNREF_BY(ec, fd, n, reason) unref_by(ec, fd, n) -static void ref_by(grpc_fd *fd, int n) { -#endif - GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); -} - -static void fd_destroy(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_fd *fd = (grpc_fd *)arg; - /* Add the fd to the freelist */ - grpc_iomgr_unregister_object(&fd->iomgr_object); - pollable_destroy(&fd->pollable_obj); - gpr_mu_destroy(&fd->orphaned_mu); - gpr_mu_lock(&fd_freelist_mu); - fd->freelist_next = fd_freelist; - fd_freelist = fd; - - grpc_lfev_destroy(&fd->read_closure); - grpc_lfev_destroy(&fd->write_closure); - - gpr_mu_unlock(&fd_freelist_mu); -} - -#ifndef NDEBUG -static void unref_by(grpc_exec_ctx *exec_ctx, grpc_fd *fd, int n, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, - "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", - fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), - gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); - } -#else -static void unref_by(grpc_exec_ctx *exec_ctx, grpc_fd *fd, int n) { -#endif - gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); - if (old == n) { - GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(fd_destroy, fd, - grpc_schedule_on_exec_ctx), - GRPC_ERROR_NONE); - } else { - GPR_ASSERT(old > n); - } -} - -static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } - -static void fd_global_shutdown(void) { - gpr_mu_lock(&fd_freelist_mu); - gpr_mu_unlock(&fd_freelist_mu); - while (fd_freelist != NULL) { - grpc_fd *fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - gpr_free(fd); - } - gpr_mu_destroy(&fd_freelist_mu); -} - -static grpc_fd *fd_create(int fd, const char *name) { - grpc_fd *new_fd = NULL; - - gpr_mu_lock(&fd_freelist_mu); - if (fd_freelist != NULL) { - new_fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - } - gpr_mu_unlock(&fd_freelist_mu); - - if (new_fd == NULL) { - new_fd = (grpc_fd *)gpr_malloc(sizeof(grpc_fd)); - } - - pollable_init(&new_fd->pollable_obj, PO_FD); - - gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); - new_fd->fd = fd; - gpr_mu_init(&new_fd->orphaned_mu); - new_fd->orphaned = false; - grpc_lfev_init(&new_fd->read_closure); - grpc_lfev_init(&new_fd->write_closure); - gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); - - new_fd->freelist_next = NULL; - new_fd->on_done_closure = NULL; - - char *fd_name; - gpr_asprintf(&fd_name, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); - } -#endif - gpr_free(fd_name); - return new_fd; -} - -static int fd_wrapped_fd(grpc_fd *fd) { - int ret_fd = -1; - gpr_mu_lock(&fd->orphaned_mu); - if (!fd->orphaned) { - ret_fd = fd->fd; - } - gpr_mu_unlock(&fd->orphaned_mu); - - return ret_fd; -} - -static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *on_done, int *release_fd, - bool already_closed, const char *reason) { - bool is_fd_closed = already_closed; - grpc_error *error = GRPC_ERROR_NONE; - - gpr_mu_lock(&fd->pollable_obj.po.mu); - gpr_mu_lock(&fd->orphaned_mu); - fd->on_done_closure = on_done; - - /* If release_fd is not NULL, we should be relinquishing control of the file - descriptor fd->fd (but we still own the grpc_fd structure). */ - if (release_fd != NULL) { - *release_fd = fd->fd; - } else if (!is_fd_closed) { - close(fd->fd); - is_fd_closed = true; - } - - fd->orphaned = true; - - if (!is_fd_closed) { - gpr_log(GPR_DEBUG, "TODO: handle fd removal?"); - } - - /* Remove the active status but keep referenced. We want this grpc_fd struct - to be alive (and not added to freelist) until the end of this function */ - REF_BY(fd, 1, reason); - - GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error)); - - gpr_mu_unlock(&fd->orphaned_mu); - gpr_mu_unlock(&fd->pollable_obj.po.mu); - UNREF_BY(exec_ctx, fd, 2, reason); /* Drop the reference */ - GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error)); - GRPC_ERROR_UNREF(error); -} - -static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); - return (grpc_pollset *)notifier; -} - -static bool fd_is_shutdown(grpc_fd *fd) { - return grpc_lfev_is_shutdown(&fd->read_closure); -} - -/* Might be called multiple times */ -static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, - GRPC_ERROR_REF(why))) { - shutdown(fd->fd, SHUT_RDWR); - grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); - } - GRPC_ERROR_UNREF(why); -} - -static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); -} - -static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); -} - -/******************************************************************************* - * Pollable Definitions - */ - -static void pollable_init(pollable *p, polling_obj_type type) { - po_init(&p->po, type); - p->root_worker = NULL; - p->epfd = -1; -} - -static void pollable_destroy(pollable *p) { - po_destroy(&p->po); - if (p->epfd != -1) { - close(p->epfd); - grpc_wakeup_fd_destroy(&p->wakeup); - } -} - -/* ensure that p->epfd, p->wakeup are initialized; p->po.mu must be held */ -static grpc_error *pollable_materialize(pollable *p) { - if (p->epfd == -1) { - int new_epfd = epoll_create1(EPOLL_CLOEXEC); - if (new_epfd < 0) { - return GRPC_OS_ERROR(errno, "epoll_create1"); - } - grpc_error *err = grpc_wakeup_fd_init(&p->wakeup); - if (err != GRPC_ERROR_NONE) { - close(new_epfd); - return err; - } - struct epoll_event ev; - ev.events = (uint32_t)(EPOLLIN | EPOLLET); - ev.data.ptr = (void *)(1 | (intptr_t)&p->wakeup); - if (epoll_ctl(new_epfd, EPOLL_CTL_ADD, p->wakeup.read_fd, &ev) != 0) { - err = GRPC_OS_ERROR(errno, "epoll_ctl"); - close(new_epfd); - grpc_wakeup_fd_destroy(&p->wakeup); - return err; - } - - p->epfd = new_epfd; - } - return GRPC_ERROR_NONE; -} - -/* pollable must be materialized */ -static grpc_error *pollable_add_fd(pollable *p, grpc_fd *fd) { - grpc_error *error = GRPC_ERROR_NONE; - static const char *err_desc = "pollable_add_fd"; - const int epfd = p->epfd; - GPR_ASSERT(epfd != -1); - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "add fd %p (%d) to pollable %p", fd, fd->fd, p); - } - - gpr_mu_lock(&fd->orphaned_mu); - if (fd->orphaned) { - gpr_mu_unlock(&fd->orphaned_mu); - return GRPC_ERROR_NONE; - } - struct epoll_event ev_fd; - ev_fd.events = (uint32_t)(EPOLLET | EPOLLIN | EPOLLOUT | EPOLLEXCLUSIVE); - ev_fd.data.ptr = fd; - if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd->fd, &ev_fd) != 0) { - switch (errno) { - case EEXIST: - break; - default: - append_error(&error, GRPC_OS_ERROR(errno, "epoll_ctl"), err_desc); - } - } - gpr_mu_unlock(&fd->orphaned_mu); - - return error; -} - -/******************************************************************************* - * Pollset Definitions - */ - -GPR_TLS_DECL(g_current_thread_pollset); -GPR_TLS_DECL(g_current_thread_worker); - -/* Global state management */ -static grpc_error *pollset_global_init(void) { - gpr_tls_init(&g_current_thread_pollset); - gpr_tls_init(&g_current_thread_worker); - pollable_init(&g_empty_pollable, PO_EMPTY_POLLABLE); - return GRPC_ERROR_NONE; -} - -static void pollset_global_shutdown(void) { - pollable_destroy(&g_empty_pollable); - gpr_tls_destroy(&g_current_thread_pollset); - gpr_tls_destroy(&g_current_thread_worker); -} - -static void pollset_maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - if (pollset->shutdown_closure != NULL && pollset->root_worker == NULL && - pollset->kick_alls_pending == 0) { - GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_closure, GRPC_ERROR_NONE); - pollset->shutdown_closure = NULL; - } -} - -static void do_kick_all(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error_unused) { - grpc_error *error = GRPC_ERROR_NONE; - grpc_pollset *pollset = (grpc_pollset *)arg; - gpr_mu_lock(&pollset->pollable_obj.po.mu); - if (pollset->root_worker != NULL) { - grpc_pollset_worker *worker = pollset->root_worker; - do { - GRPC_STATS_INC_POLLSET_KICK(exec_ctx); - if (worker->pollable_obj != &pollset->pollable_obj) { - gpr_mu_lock(&worker->pollable_obj->po.mu); - } - if (worker->initialized_cv && worker != pollset->root_worker) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kickall_via_cv %p (pollable %p vs %p)", - pollset, worker, &pollset->pollable_obj, - worker->pollable_obj); - } - worker->kicked = true; - gpr_cv_signal(&worker->cv); - } else { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kickall_via_wakeup %p (pollable %p vs %p)", - pollset, worker, &pollset->pollable_obj, - worker->pollable_obj); - } - append_error(&error, - grpc_wakeup_fd_wakeup(&worker->pollable_obj->wakeup), - "pollset_shutdown"); - } - if (worker->pollable_obj != &pollset->pollable_obj) { - gpr_mu_unlock(&worker->pollable_obj->po.mu); - } - - worker = worker->links[PWL_POLLSET].next; - } while (worker != pollset->root_worker); - } - pollset->kick_alls_pending--; - pollset_maybe_finish_shutdown(exec_ctx, pollset); - gpr_mu_unlock(&pollset->pollable_obj.po.mu); - GRPC_LOG_IF_ERROR("kick_all", error); -} - -static void pollset_kick_all(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - pollset->kick_alls_pending++; - GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(do_kick_all, pollset, - grpc_schedule_on_exec_ctx), - GRPC_ERROR_NONE); -} - -static grpc_error *pollset_kick_inner(grpc_pollset *pollset, pollable *p, - grpc_pollset_worker *specific_worker) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, - "PS:%p kick %p tls_pollset=%p tls_worker=%p " - "root_worker=(pollset:%p pollable:%p)", - p, specific_worker, (void *)gpr_tls_get(&g_current_thread_pollset), - (void *)gpr_tls_get(&g_current_thread_worker), pollset->root_worker, - p->root_worker); - } - if (specific_worker == NULL) { - if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { - if (pollset->root_worker == NULL) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_any_without_poller", p); - } - pollset->kicked_without_poller = true; - return GRPC_ERROR_NONE; - } else { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_any_via_wakeup_fd", p); - } - grpc_error *err = pollable_materialize(p); - if (err != GRPC_ERROR_NONE) return err; - return grpc_wakeup_fd_wakeup(&p->wakeup); - } - } else { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_any_but_awake", p); - } - return GRPC_ERROR_NONE; - } - } else if (specific_worker->kicked) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_already_kicked", p); - } - return GRPC_ERROR_NONE; - } else if (gpr_tls_get(&g_current_thread_worker) == - (intptr_t)specific_worker) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_awake", p); - } - specific_worker->kicked = true; - return GRPC_ERROR_NONE; - } else if (specific_worker == p->root_worker) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_wakeup_fd", p); - } - grpc_error *err = pollable_materialize(p); - if (err != GRPC_ERROR_NONE) return err; - specific_worker->kicked = true; - return grpc_wakeup_fd_wakeup(&p->wakeup); - } else { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_cv", p); - } - specific_worker->kicked = true; - gpr_cv_signal(&specific_worker->cv); - return GRPC_ERROR_NONE; - } -} - -/* p->po.mu must be held before calling this function */ -static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *specific_worker) { - pollable *p = pollset->current_pollable_obj; - GRPC_STATS_INC_POLLSET_KICK(exec_ctx); - if (p != &pollset->pollable_obj) { - gpr_mu_lock(&p->po.mu); - } - grpc_error *error = pollset_kick_inner(pollset, p, specific_worker); - if (p != &pollset->pollable_obj) { - gpr_mu_unlock(&p->po.mu); - } - return error; -} - -static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - pollable_init(&pollset->pollable_obj, PO_POLLSET); - pollset->current_pollable_obj = &g_empty_pollable; - pollset->kicked_without_poller = false; - pollset->shutdown_closure = NULL; - pollset->root_worker = NULL; - *mu = &pollset->pollable_obj.po.mu; -} - -/* Convert a timespec to milliseconds: - - Very small or negative poll times are clamped to zero to do a non-blocking - poll (which becomes spin polling) - - Other small values are rounded up to one millisecond - - Longer than a millisecond polls are rounded up to the next nearest - millisecond to avoid spinning - - Infinite timeouts are converted to -1 */ -static int poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return -1; - } - - if (gpr_time_cmp(deadline, now) <= 0) { - return 0; - } - - static const gpr_timespec round_up = { - 0, /* tv_sec */ - GPR_NS_PER_MS - 1, /* tv_nsec */ - GPR_TIMESPAN /* clock_type */ - }; - timeout = gpr_time_sub(deadline, now); - int millis = gpr_time_to_millis(gpr_time_add(timeout, round_up)); - return millis >= 1 ? millis : 1; -} - -static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_pollset *notifier) { - grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); - - /* Note, it is possible that fd_become_readable might be called twice with - different 'notifier's when an fd becomes readable and it is in two epoll - sets (This can happen briefly during polling island merges). In such cases - it does not really matter which notifer is set as the read_notifier_pollset - (They would both point to the same polling island anyway) */ - /* Use release store to match with acquire load in fd_get_read_notifier */ - gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); -} - -static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); -} - -static grpc_error *fd_become_pollable_locked(grpc_fd *fd) { - grpc_error *error = GRPC_ERROR_NONE; - static const char *err_desc = "fd_become_pollable"; - if (append_error(&error, pollable_materialize(&fd->pollable_obj), err_desc)) { - append_error(&error, pollable_add_fd(&fd->pollable_obj, fd), err_desc); - } - return error; -} - -/* pollset->po.mu lock must be held by the caller before calling this */ -static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_ASSERT(pollset->shutdown_closure == NULL); - pollset->shutdown_closure = closure; - pollset_kick_all(exec_ctx, pollset); - pollset_maybe_finish_shutdown(exec_ctx, pollset); -} - -static bool pollset_is_pollable_fd(grpc_pollset *pollset, pollable *p) { - return p != &g_empty_pollable && p != &pollset->pollable_obj; -} - -static grpc_error *pollset_process_events(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, bool drain) { - static const char *err_desc = "pollset_process_events"; - grpc_error *error = GRPC_ERROR_NONE; - for (int i = 0; (drain || i < MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) && - pollset->event_cursor != pollset->event_count; - i++) { - int n = pollset->event_cursor++; - struct epoll_event *ev = &pollset->events[n]; - void *data_ptr = ev->data.ptr; - if (1 & (intptr_t)data_ptr) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p got pollset_wakeup %p", pollset, data_ptr); - } - append_error(&error, - grpc_wakeup_fd_consume_wakeup( - (grpc_wakeup_fd *)((~(intptr_t)1) & (intptr_t)data_ptr)), - err_desc); - } else { - grpc_fd *fd = (grpc_fd *)data_ptr; - bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0; - bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0; - bool write_ev = (ev->events & EPOLLOUT) != 0; - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, - "PS:%p got fd %p: cancel=%d read=%d " - "write=%d", - pollset, fd, cancel, read_ev, write_ev); - } - if (read_ev || cancel) { - fd_become_readable(exec_ctx, fd, pollset); - } - if (write_ev || cancel) { - fd_become_writable(exec_ctx, fd); - } - } - } - - return error; -} - -/* pollset_shutdown is guaranteed to be called before pollset_destroy. */ -static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - pollable_destroy(&pollset->pollable_obj); - if (pollset_is_pollable_fd(pollset, pollset->current_pollable_obj)) { - UNREF_BY(exec_ctx, (grpc_fd *)pollset->current_pollable_obj, 2, - "pollset_pollable"); - } - GRPC_LOG_IF_ERROR("pollset_process_events", - pollset_process_events(exec_ctx, pollset, true)); -} - -static grpc_error *pollset_epoll(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - pollable *p, gpr_timespec now, - gpr_timespec deadline) { - int timeout = poll_deadline_to_millis_timeout(deadline, now); - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - char *desc = pollable_desc(p); - gpr_log(GPR_DEBUG, "PS:%p poll %p[%s] for %dms", pollset, p, desc, timeout); - gpr_free(desc); - } - - if (timeout != 0) { - GRPC_SCHEDULING_START_BLOCKING_REGION; - } - int r; - do { - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - r = epoll_wait(p->epfd, pollset->events, MAX_EPOLL_EVENTS, timeout); - } while (r < 0 && errno == EINTR); - if (timeout != 0) { - GRPC_SCHEDULING_END_BLOCKING_REGION; - } - - if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait"); - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p poll %p got %d events", pollset, p, r); - } - - pollset->event_cursor = 0; - pollset->event_count = r; - - return GRPC_ERROR_NONE; -} - -/* Return true if first in list */ -static bool worker_insert(grpc_pollset_worker **root, pollset_worker_links link, - grpc_pollset_worker *worker) { - if (*root == NULL) { - *root = worker; - worker->links[link].next = worker->links[link].prev = worker; - return true; - } else { - worker->links[link].next = *root; - worker->links[link].prev = worker->links[link].next->links[link].prev; - worker->links[link].next->links[link].prev = worker; - worker->links[link].prev->links[link].next = worker; - return false; - } -} - -/* Return true if last in list */ -typedef enum { EMPTIED, NEW_ROOT, REMOVED } worker_remove_result; - -static worker_remove_result worker_remove(grpc_pollset_worker **root, - pollset_worker_links link, - grpc_pollset_worker *worker) { - if (worker == *root) { - if (worker == worker->links[link].next) { - *root = NULL; - return EMPTIED; - } else { - *root = worker->links[link].next; - worker->links[link].prev->links[link].next = worker->links[link].next; - worker->links[link].next->links[link].prev = worker->links[link].prev; - return NEW_ROOT; - } - } else { - worker->links[link].prev->links[link].next = worker->links[link].next; - worker->links[link].next->links[link].prev = worker->links[link].prev; - return REMOVED; - } -} - -/* Return true if this thread should poll */ -static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker, - grpc_pollset_worker **worker_hdl, gpr_timespec *now, - gpr_timespec deadline) { - bool do_poll = true; - if (worker_hdl != NULL) *worker_hdl = worker; - worker->initialized_cv = false; - worker->kicked = false; - worker->pollset = pollset; - worker->pollable_obj = pollset->current_pollable_obj; - - if (pollset_is_pollable_fd(pollset, worker->pollable_obj)) { - REF_BY((grpc_fd *)worker->pollable_obj, 2, "one_poll"); - } - - worker_insert(&pollset->root_worker, PWL_POLLSET, worker); - if (!worker_insert(&worker->pollable_obj->root_worker, PWL_POLLABLE, - worker)) { - worker->initialized_cv = true; - gpr_cv_init(&worker->cv); - if (worker->pollable_obj != &pollset->pollable_obj) { - gpr_mu_unlock(&pollset->pollable_obj.po.mu); - } - if (GRPC_TRACER_ON(grpc_polling_trace) && - worker->pollable_obj->root_worker != worker) { - gpr_log(GPR_DEBUG, "PS:%p wait %p w=%p for %dms", pollset, - worker->pollable_obj, worker, - poll_deadline_to_millis_timeout(deadline, *now)); - } - while (do_poll && worker->pollable_obj->root_worker != worker) { - if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->po.mu, deadline)) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p timeout_wait %p w=%p", pollset, - worker->pollable_obj, worker); - } - do_poll = false; - } else if (worker->kicked) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p wakeup %p w=%p", pollset, - worker->pollable_obj, worker); - } - do_poll = false; - } else if (GRPC_TRACER_ON(grpc_polling_trace) && - worker->pollable_obj->root_worker != worker) { - gpr_log(GPR_DEBUG, "PS:%p spurious_wakeup %p w=%p", pollset, - worker->pollable_obj, worker); - } - } - if (worker->pollable_obj != &pollset->pollable_obj) { - gpr_mu_unlock(&worker->pollable_obj->po.mu); - gpr_mu_lock(&pollset->pollable_obj.po.mu); - gpr_mu_lock(&worker->pollable_obj->po.mu); - } - *now = gpr_now(now->clock_type); - } - - return do_poll && pollset->shutdown_closure == NULL && - pollset->current_pollable_obj == worker->pollable_obj; -} - -static void end_worker(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *worker, - grpc_pollset_worker **worker_hdl) { - if (NEW_ROOT == - worker_remove(&worker->pollable_obj->root_worker, PWL_POLLABLE, worker)) { - gpr_cv_signal(&worker->pollable_obj->root_worker->cv); - } - if (worker->initialized_cv) { - gpr_cv_destroy(&worker->cv); - } - if (pollset_is_pollable_fd(pollset, worker->pollable_obj)) { - UNREF_BY(exec_ctx, (grpc_fd *)worker->pollable_obj, 2, "one_poll"); - } - if (EMPTIED == worker_remove(&pollset->root_worker, PWL_POLLSET, worker)) { - pollset_maybe_finish_shutdown(exec_ctx, pollset); - } -} - -/* pollset->po.mu lock must be held by the caller before calling this. - The function pollset_work() may temporarily release the lock (pollset->po.mu) - during the course of its execution but it will always re-acquire the lock and - ensure that it is held by the time the function returns */ -static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - grpc_pollset_worker worker; - if (0 && GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p work hdl=%p worker=%p now=%" PRId64 - ".%09d deadline=%" PRId64 ".%09d kwp=%d root_worker=%p", - pollset, worker_hdl, &worker, now.tv_sec, now.tv_nsec, - deadline.tv_sec, deadline.tv_nsec, pollset->kicked_without_poller, - pollset->root_worker); - } - grpc_error *error = GRPC_ERROR_NONE; - static const char *err_desc = "pollset_work"; - if (pollset->kicked_without_poller) { - pollset->kicked_without_poller = false; - return GRPC_ERROR_NONE; - } - if (pollset->current_pollable_obj != &pollset->pollable_obj) { - gpr_mu_lock(&pollset->current_pollable_obj->po.mu); - } - if (begin_worker(pollset, &worker, worker_hdl, &now, deadline)) { - gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); - gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); - GPR_ASSERT(!pollset->shutdown_closure); - append_error(&error, pollable_materialize(worker.pollable_obj), err_desc); - if (worker.pollable_obj != &pollset->pollable_obj) { - gpr_mu_unlock(&worker.pollable_obj->po.mu); - } - gpr_mu_unlock(&pollset->pollable_obj.po.mu); - if (pollset->event_cursor == pollset->event_count) { - append_error(&error, pollset_epoll(exec_ctx, pollset, worker.pollable_obj, - now, deadline), - err_desc); - } - append_error(&error, pollset_process_events(exec_ctx, pollset, false), - err_desc); - gpr_mu_lock(&pollset->pollable_obj.po.mu); - if (worker.pollable_obj != &pollset->pollable_obj) { - gpr_mu_lock(&worker.pollable_obj->po.mu); - } - gpr_tls_set(&g_current_thread_pollset, 0); - gpr_tls_set(&g_current_thread_worker, 0); - pollset_maybe_finish_shutdown(exec_ctx, pollset); - } - end_worker(exec_ctx, pollset, &worker, worker_hdl); - if (worker.pollable_obj != &pollset->pollable_obj) { - gpr_mu_unlock(&worker.pollable_obj->po.mu); - } - if (grpc_exec_ctx_has_work(exec_ctx)) { - gpr_mu_unlock(&pollset->pollable_obj.po.mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->pollable_obj.po.mu); - } - return error; -} - -static void unref_fd_no_longer_poller(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_fd *fd = (grpc_fd *)arg; - UNREF_BY(exec_ctx, fd, 2, "pollset_pollable"); -} - -/* expects pollsets locked, flag whether fd is locked or not */ -static grpc_error *pollset_add_fd_locked(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, grpc_fd *fd, - bool fd_locked) { - static const char *err_desc = "pollset_add_fd"; - grpc_error *error = GRPC_ERROR_NONE; - if (pollset->current_pollable_obj == &g_empty_pollable) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, - "PS:%p add fd %p; transition pollable from empty to fd", pollset, - fd); - } - /* empty pollable --> single fd pollable */ - pollset_kick_all(exec_ctx, pollset); - pollset->current_pollable_obj = &fd->pollable_obj; - if (!fd_locked) gpr_mu_lock(&fd->pollable_obj.po.mu); - append_error(&error, fd_become_pollable_locked(fd), err_desc); - if (!fd_locked) gpr_mu_unlock(&fd->pollable_obj.po.mu); - REF_BY(fd, 2, "pollset_pollable"); - } else if (pollset->current_pollable_obj == &pollset->pollable_obj) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "PS:%p add fd %p; already multipolling", pollset, fd); - } - append_error(&error, pollable_add_fd(pollset->current_pollable_obj, fd), - err_desc); - } else if (pollset->current_pollable_obj != &fd->pollable_obj) { - grpc_fd *had_fd = (grpc_fd *)pollset->current_pollable_obj; - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, - "PS:%p add fd %p; transition pollable from fd %p to multipoller", - pollset, fd, had_fd); - } - /* Introduce a spurious completion. - If we do not, then it may be that the fd-specific epoll set consumed - a completion without being polled, leading to a missed edge going up. */ - grpc_lfev_set_ready(exec_ctx, &had_fd->read_closure, "read"); - grpc_lfev_set_ready(exec_ctx, &had_fd->write_closure, "write"); - pollset_kick_all(exec_ctx, pollset); - pollset->current_pollable_obj = &pollset->pollable_obj; - if (append_error(&error, pollable_materialize(&pollset->pollable_obj), - err_desc)) { - pollable_add_fd(&pollset->pollable_obj, had_fd); - pollable_add_fd(&pollset->pollable_obj, fd); - } - GRPC_CLOSURE_SCHED(exec_ctx, - GRPC_CLOSURE_CREATE(unref_fd_no_longer_poller, had_fd, - grpc_schedule_on_exec_ctx), - GRPC_ERROR_NONE); - } - return error; -} - -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - gpr_mu_lock(&pollset->pollable_obj.po.mu); - grpc_error *error = pollset_add_fd_locked(exec_ctx, pollset, fd, false); - gpr_mu_unlock(&pollset->pollable_obj.po.mu); - GRPC_LOG_IF_ERROR("pollset_add_fd", error); -} - -/******************************************************************************* - * Pollset-set Definitions - */ - -static grpc_pollset_set *pollset_set_create(void) { - grpc_pollset_set *pss = (grpc_pollset_set *)gpr_zalloc(sizeof(*pss)); - po_init(&pss->po, PO_POLLSET_SET); - return pss; -} - -static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss) { - po_destroy(&pss->po); - gpr_free(pss); -} - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - po_join(exec_ctx, &pss->po, &fd->pollable_obj.po); -} - -static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) {} - -static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - po_join(exec_ctx, &pss->po, &ps->pollable_obj.po); -} - -static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) {} - -static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - po_join(exec_ctx, &bag->po, &item->po); -} - -static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) {} - -static void po_init(polling_obj *po, polling_obj_type type) { - gpr_mu_init(&po->mu); - po->type = type; - po->group = NULL; - po->next = po; - po->prev = po; -} - -static polling_group *pg_lock_latest(polling_group *pg) { - /* assumes pg unlocked; consumes ref, returns ref */ - gpr_mu_lock(&pg->po.mu); - while (pg->po.group != NULL) { - polling_group *new_pg = pg_ref(pg->po.group); - gpr_mu_unlock(&pg->po.mu); - pg_unref(pg); - pg = new_pg; - gpr_mu_lock(&pg->po.mu); - } - return pg; -} - -static void po_destroy(polling_obj *po) { - if (po->group != NULL) { - polling_group *pg = pg_lock_latest(po->group); - po->prev->next = po->next; - po->next->prev = po->prev; - gpr_mu_unlock(&pg->po.mu); - pg_unref(pg); - } - gpr_mu_destroy(&po->mu); -} - -static polling_group *pg_ref(polling_group *pg) { - gpr_ref(&pg->refs); - return pg; -} - -static void pg_unref(polling_group *pg) { - if (gpr_unref(&pg->refs)) { - po_destroy(&pg->po); - gpr_free(pg); - } -} - -static int po_cmp(polling_obj *a, polling_obj *b) { - if (a == b) return 0; - if (a->type < b->type) return -1; - if (a->type > b->type) return 1; - if (a < b) return -1; - assert(a > b); - return 1; -} - -static void po_join(grpc_exec_ctx *exec_ctx, polling_obj *a, polling_obj *b) { - switch (po_cmp(a, b)) { - case 0: - return; - case 1: - GPR_SWAP(polling_obj *, a, b); - /* fall through */ - case -1: - gpr_mu_lock(&a->mu); - gpr_mu_lock(&b->mu); - - if (a->group == NULL) { - if (b->group == NULL) { - polling_obj *initial_po[] = {a, b}; - pg_create(exec_ctx, initial_po, GPR_ARRAY_SIZE(initial_po)); - gpr_mu_unlock(&a->mu); - gpr_mu_unlock(&b->mu); - } else { - polling_group *b_group = pg_ref(b->group); - gpr_mu_unlock(&b->mu); - gpr_mu_unlock(&a->mu); - pg_join(exec_ctx, b_group, a); - } - } else if (b->group == NULL) { - polling_group *a_group = pg_ref(a->group); - gpr_mu_unlock(&a->mu); - gpr_mu_unlock(&b->mu); - pg_join(exec_ctx, a_group, b); - } else if (a->group == b->group) { - /* nothing to do */ - gpr_mu_unlock(&a->mu); - gpr_mu_unlock(&b->mu); - } else { - polling_group *a_group = pg_ref(a->group); - polling_group *b_group = pg_ref(b->group); - gpr_mu_unlock(&a->mu); - gpr_mu_unlock(&b->mu); - pg_merge(exec_ctx, a_group, b_group); - } - } -} - -static void pg_notify(grpc_exec_ctx *exec_ctx, polling_obj *a, polling_obj *b) { - if (a->type == PO_FD && b->type == PO_POLLSET) { - pollset_add_fd_locked(exec_ctx, (grpc_pollset *)b, (grpc_fd *)a, true); - } else if (a->type == PO_POLLSET && b->type == PO_FD) { - pollset_add_fd_locked(exec_ctx, (grpc_pollset *)a, (grpc_fd *)b, true); - } -} - -static void pg_broadcast(grpc_exec_ctx *exec_ctx, polling_group *from, - polling_group *to) { - for (polling_obj *a = from->po.next; a != &from->po; a = a->next) { - for (polling_obj *b = to->po.next; b != &to->po; b = b->next) { - if (po_cmp(a, b) < 0) { - gpr_mu_lock(&a->mu); - gpr_mu_lock(&b->mu); - } else { - GPR_ASSERT(po_cmp(a, b) != 0); - gpr_mu_lock(&b->mu); - gpr_mu_lock(&a->mu); - } - pg_notify(exec_ctx, a, b); - gpr_mu_unlock(&a->mu); - gpr_mu_unlock(&b->mu); - } - } -} - -static void pg_create(grpc_exec_ctx *exec_ctx, polling_obj **initial_po, - size_t initial_po_count) { - /* assumes all polling objects in initial_po are locked */ - polling_group *pg = (polling_group *)gpr_malloc(sizeof(*pg)); - po_init(&pg->po, PO_POLLING_GROUP); - gpr_ref_init(&pg->refs, (int)initial_po_count); - for (size_t i = 0; i < initial_po_count; i++) { - GPR_ASSERT(initial_po[i]->group == NULL); - initial_po[i]->group = pg; - } - for (size_t i = 1; i < initial_po_count; i++) { - initial_po[i]->prev = initial_po[i - 1]; - } - for (size_t i = 0; i < initial_po_count - 1; i++) { - initial_po[i]->next = initial_po[i + 1]; - } - initial_po[0]->prev = &pg->po; - initial_po[initial_po_count - 1]->next = &pg->po; - pg->po.next = initial_po[0]; - pg->po.prev = initial_po[initial_po_count - 1]; - for (size_t i = 1; i < initial_po_count; i++) { - for (size_t j = 0; j < i; j++) { - pg_notify(exec_ctx, initial_po[i], initial_po[j]); - } - } -} - -static void pg_join(grpc_exec_ctx *exec_ctx, polling_group *pg, - polling_obj *po) { - /* assumes neither pg nor po are locked; consumes one ref to pg */ - pg = pg_lock_latest(pg); - /* pg locked */ - for (polling_obj *existing = pg->po.next /* skip pg - it's just a stub */; - existing != &pg->po; existing = existing->next) { - if (po_cmp(po, existing) < 0) { - gpr_mu_lock(&po->mu); - gpr_mu_lock(&existing->mu); - } else { - GPR_ASSERT(po_cmp(po, existing) != 0); - gpr_mu_lock(&existing->mu); - gpr_mu_lock(&po->mu); - } - /* pg, po, existing locked */ - if (po->group != NULL) { - gpr_mu_unlock(&pg->po.mu); - polling_group *po_group = pg_ref(po->group); - gpr_mu_unlock(&po->mu); - gpr_mu_unlock(&existing->mu); - pg_merge(exec_ctx, pg, po_group); - /* early exit: polling obj picked up a group during joining: we needed - to do a full merge */ - return; - } - pg_notify(exec_ctx, po, existing); - gpr_mu_unlock(&po->mu); - gpr_mu_unlock(&existing->mu); - } - gpr_mu_lock(&po->mu); - if (po->group != NULL) { - gpr_mu_unlock(&pg->po.mu); - polling_group *po_group = pg_ref(po->group); - gpr_mu_unlock(&po->mu); - pg_merge(exec_ctx, pg, po_group); - /* early exit: polling obj picked up a group during joining: we needed - to do a full merge */ - return; - } - po->group = pg; - po->next = &pg->po; - po->prev = pg->po.prev; - po->prev->next = po->next->prev = po; - gpr_mu_unlock(&pg->po.mu); - gpr_mu_unlock(&po->mu); -} - -static void pg_merge(grpc_exec_ctx *exec_ctx, polling_group *a, - polling_group *b) { - for (;;) { - if (a == b) { - pg_unref(a); - pg_unref(b); - return; - } - if (a > b) GPR_SWAP(polling_group *, a, b); - gpr_mu_lock(&a->po.mu); - gpr_mu_lock(&b->po.mu); - if (a->po.group != NULL) { - polling_group *m2 = pg_ref(a->po.group); - gpr_mu_unlock(&a->po.mu); - gpr_mu_unlock(&b->po.mu); - pg_unref(a); - a = m2; - } else if (b->po.group != NULL) { - polling_group *m2 = pg_ref(b->po.group); - gpr_mu_unlock(&a->po.mu); - gpr_mu_unlock(&b->po.mu); - pg_unref(b); - b = m2; - } else { - break; - } - } - polling_group **unref = NULL; - size_t unref_count = 0; - size_t unref_cap = 0; - b->po.group = a; - pg_broadcast(exec_ctx, a, b); - pg_broadcast(exec_ctx, b, a); - while (b->po.next != &b->po) { - polling_obj *po = b->po.next; - gpr_mu_lock(&po->mu); - if (unref_count == unref_cap) { - unref_cap = GPR_MAX(8, 3 * unref_cap / 2); - unref = (polling_group **)gpr_realloc(unref, unref_cap * sizeof(*unref)); - } - unref[unref_count++] = po->group; - po->group = pg_ref(a); - // unlink from b - po->prev->next = po->next; - po->next->prev = po->prev; - // link to a - po->next = &a->po; - po->prev = a->po.prev; - po->next->prev = po->prev->next = po; - gpr_mu_unlock(&po->mu); - } - gpr_mu_unlock(&a->po.mu); - gpr_mu_unlock(&b->po.mu); - for (size_t i = 0; i < unref_count; i++) { - pg_unref(unref[i]); - } - gpr_free(unref); - pg_unref(b); -} - -/******************************************************************************* - * Event engine binding - */ - -static void shutdown_engine(void) { - fd_global_shutdown(); - pollset_global_shutdown(); -} - -static const grpc_event_engine_vtable vtable = { - sizeof(grpc_pollset), - - fd_create, - fd_wrapped_fd, - fd_orphan, - fd_shutdown, - fd_notify_on_read, - fd_notify_on_write, - fd_is_shutdown, - fd_get_read_notifier_pollset, - - pollset_init, - pollset_shutdown, - pollset_destroy, - pollset_work, - pollset_kick, - pollset_add_fd, - - pollset_set_create, - pollset_set_destroy, - pollset_set_add_pollset, - pollset_set_del_pollset, - pollset_set_add_pollset_set, - pollset_set_del_pollset_set, - pollset_set_add_fd, - pollset_set_del_fd, - - shutdown_engine, -}; - -const grpc_event_engine_vtable *grpc_init_epollex_linux( - bool explicitly_requested) { - if (!grpc_has_wakeup_fd()) { - return NULL; - } - - if (!grpc_is_epollexclusive_available()) { - return NULL; - } - - fd_global_init(); - - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - pollset_global_shutdown(); - fd_global_shutdown(); - return NULL; - } - - return &vtable; -} - -#else /* defined(GRPC_LINUX_EPOLL) */ -#if defined(GRPC_POSIX_SOCKET) -#include "src/core/lib/iomgr/ev_posix.h" -/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return - * NULL */ -const grpc_event_engine_vtable *grpc_init_epollex_linux( - bool explicitly_requested) { - return NULL; -} -#endif /* defined(GRPC_POSIX_SOCKET) */ - -#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epollex_linux.cc b/src/core/lib/iomgr/ev_epollex_linux.cc new file mode 100644 index 0000000000..8eb4de44d9 --- /dev/null +++ b/src/core/lib/iomgr/ev_epollex_linux.cc @@ -0,0 +1,1461 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +/* This polling engine is only relevant on linux kernels supporting epoll() */ +#ifdef GRPC_LINUX_EPOLL + +#include "src/core/lib/iomgr/ev_epollex_linux.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/is_epollexclusive_available.h" +#include "src/core/lib/iomgr/lockfree_event.h" +#include "src/core/lib/iomgr/sys_epoll_wrapper.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/spinlock.h" + +/******************************************************************************* + * Polling object + */ + +typedef enum { + PO_POLLING_GROUP, + PO_POLLSET_SET, + PO_POLLSET, + PO_FD, /* ordering is important: we always want to lock pollsets before fds: + this guarantees that using an fd as a pollable is safe */ + PO_EMPTY_POLLABLE, + PO_COUNT +} polling_obj_type; + +typedef struct polling_obj polling_obj; +typedef struct polling_group polling_group; + +struct polling_obj { + gpr_mu mu; + polling_obj_type type; + polling_group *group; + struct polling_obj *next; + struct polling_obj *prev; +}; + +struct polling_group { + polling_obj po; + gpr_refcount refs; +}; + +static void po_init(polling_obj *po, polling_obj_type type); +static void po_destroy(polling_obj *po); +static void po_join(grpc_exec_ctx *exec_ctx, polling_obj *a, polling_obj *b); +static int po_cmp(polling_obj *a, polling_obj *b); + +static void pg_create(grpc_exec_ctx *exec_ctx, polling_obj **initial_po, + size_t initial_po_count); +static polling_group *pg_ref(polling_group *pg); +static void pg_unref(polling_group *pg); +static void pg_merge(grpc_exec_ctx *exec_ctx, polling_group *a, + polling_group *b); +static void pg_join(grpc_exec_ctx *exec_ctx, polling_group *pg, + polling_obj *po); + +/******************************************************************************* + * pollable Declarations + */ + +typedef struct pollable { + polling_obj po; + int epfd; + grpc_wakeup_fd wakeup; + grpc_pollset_worker *root_worker; +} pollable; + +static const char *polling_obj_type_string(polling_obj_type t) { + switch (t) { + case PO_POLLING_GROUP: + return "polling_group"; + case PO_POLLSET_SET: + return "pollset_set"; + case PO_POLLSET: + return "pollset"; + case PO_FD: + return "fd"; + case PO_EMPTY_POLLABLE: + return "empty_pollable"; + case PO_COUNT: + return ""; + } + return ""; +} + +static char *pollable_desc(pollable *p) { + char *out; + gpr_asprintf(&out, "type=%s group=%p epfd=%d wakeup=%d", + polling_obj_type_string(p->po.type), p->po.group, p->epfd, + p->wakeup.read_fd); + return out; +} + +static pollable g_empty_pollable; + +static void pollable_init(pollable *p, polling_obj_type type); +static void pollable_destroy(pollable *p); +/* ensure that p->epfd, p->wakeup are initialized; p->po.mu must be held */ +static grpc_error *pollable_materialize(pollable *p); + +/******************************************************************************* + * Fd Declarations + */ + +struct grpc_fd { + pollable pollable_obj; + int fd; + /* refst format: + bit 0 : 1=Active / 0=Orphaned + bits 1-n : refcount + Ref/Unref by two to avoid altering the orphaned bit */ + gpr_atm refst; + + /* The fd is either closed or we relinquished control of it. In either + cases, this indicates that the 'fd' on this structure is no longer + valid */ + gpr_mu orphaned_mu; + bool orphaned; + + gpr_atm read_closure; + gpr_atm write_closure; + + struct grpc_fd *freelist_next; + grpc_closure *on_done_closure; + + /* The pollset that last noticed that the fd is readable. The actual type + * stored in this is (grpc_pollset *) */ + gpr_atm read_notifier_pollset; + + grpc_iomgr_object iomgr_object; +}; + +static void fd_global_init(void); +static void fd_global_shutdown(void); + +/******************************************************************************* + * Pollset Declarations + */ + +typedef struct pollset_worker_link { + grpc_pollset_worker *next; + grpc_pollset_worker *prev; +} pollset_worker_link; + +typedef enum { + PWL_POLLSET, + PWL_POLLABLE, + POLLSET_WORKER_LINK_COUNT +} pollset_worker_links; + +struct grpc_pollset_worker { + bool kicked; + bool initialized_cv; + pollset_worker_link links[POLLSET_WORKER_LINK_COUNT]; + gpr_cv cv; + grpc_pollset *pollset; + pollable *pollable_obj; +}; + +#define MAX_EPOLL_EVENTS 100 +#define MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL 5 + +struct grpc_pollset { + pollable pollable_obj; + pollable *current_pollable_obj; + int kick_alls_pending; + bool kicked_without_poller; + grpc_closure *shutdown_closure; + grpc_pollset_worker *root_worker; + + int event_cursor; + int event_count; + struct epoll_event events[MAX_EPOLL_EVENTS]; +}; + +/******************************************************************************* + * Pollset-set Declarations + */ +struct grpc_pollset_set { + polling_obj po; +}; + +/******************************************************************************* + * Common helpers + */ + +static bool append_error(grpc_error **composite, grpc_error *error, + const char *desc) { + if (error == GRPC_ERROR_NONE) return true; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + } + *composite = grpc_error_add_child(*composite, error); + return false; +} + +/******************************************************************************* + * Fd Definitions + */ + +/* We need to keep a freelist not because of any concerns of malloc performance + * but instead so that implementations with multiple threads in (for example) + * epoll_wait deal with the race between pollset removal and incoming poll + * notifications. + * + * The problem is that the poller ultimately holds a reference to this + * object, so it is very difficult to know when is safe to free it, at least + * without some expensive synchronization. + * + * If we keep the object freelisted, in the worst case losing this race just + * becomes a spurious read notification on a reused fd. + */ + +/* The alarm system needs to be able to wakeup 'some poller' sometimes + * (specifically when a new alarm needs to be triggered earlier than the next + * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a + * case occurs. */ + +static grpc_fd *fd_freelist = NULL; +static gpr_mu fd_freelist_mu; + +#ifndef NDEBUG +#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) +#define UNREF_BY(ec, fd, n, reason) \ + unref_by(ec, fd, n, reason, __FILE__, __LINE__) +static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, + "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", + fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); + } +#else +#define REF_BY(fd, n, reason) ref_by(fd, n) +#define UNREF_BY(ec, fd, n, reason) unref_by(ec, fd, n) +static void ref_by(grpc_fd *fd, int n) { +#endif + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); +} + +static void fd_destroy(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_fd *fd = (grpc_fd *)arg; + /* Add the fd to the freelist */ + grpc_iomgr_unregister_object(&fd->iomgr_object); + pollable_destroy(&fd->pollable_obj); + gpr_mu_destroy(&fd->orphaned_mu); + gpr_mu_lock(&fd_freelist_mu); + fd->freelist_next = fd_freelist; + fd_freelist = fd; + + grpc_lfev_destroy(&fd->read_closure); + grpc_lfev_destroy(&fd->write_closure); + + gpr_mu_unlock(&fd_freelist_mu); +} + +#ifndef NDEBUG +static void unref_by(grpc_exec_ctx *exec_ctx, grpc_fd *fd, int n, + const char *reason, const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, + "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", + fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); + } +#else +static void unref_by(grpc_exec_ctx *exec_ctx, grpc_fd *fd, int n) { +#endif + gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { + GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(fd_destroy, fd, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } else { + GPR_ASSERT(old > n); + } +} + +static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } + +static void fd_global_shutdown(void) { + gpr_mu_lock(&fd_freelist_mu); + gpr_mu_unlock(&fd_freelist_mu); + while (fd_freelist != NULL) { + grpc_fd *fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + gpr_free(fd); + } + gpr_mu_destroy(&fd_freelist_mu); +} + +static grpc_fd *fd_create(int fd, const char *name) { + grpc_fd *new_fd = NULL; + + gpr_mu_lock(&fd_freelist_mu); + if (fd_freelist != NULL) { + new_fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + } + gpr_mu_unlock(&fd_freelist_mu); + + if (new_fd == NULL) { + new_fd = (grpc_fd *)gpr_malloc(sizeof(grpc_fd)); + } + + pollable_init(&new_fd->pollable_obj, PO_FD); + + gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); + new_fd->fd = fd; + gpr_mu_init(&new_fd->orphaned_mu); + new_fd->orphaned = false; + grpc_lfev_init(&new_fd->read_closure); + grpc_lfev_init(&new_fd->write_closure); + gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); + + new_fd->freelist_next = NULL; + new_fd->on_done_closure = NULL; + + char *fd_name; + gpr_asprintf(&fd_name, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, "FD %d %p create %s", fd, new_fd, fd_name); + } +#endif + gpr_free(fd_name); + return new_fd; +} + +static int fd_wrapped_fd(grpc_fd *fd) { + int ret_fd = -1; + gpr_mu_lock(&fd->orphaned_mu); + if (!fd->orphaned) { + ret_fd = fd->fd; + } + gpr_mu_unlock(&fd->orphaned_mu); + + return ret_fd; +} + +static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *on_done, int *release_fd, + bool already_closed, const char *reason) { + bool is_fd_closed = already_closed; + grpc_error *error = GRPC_ERROR_NONE; + + gpr_mu_lock(&fd->pollable_obj.po.mu); + gpr_mu_lock(&fd->orphaned_mu); + fd->on_done_closure = on_done; + + /* If release_fd is not NULL, we should be relinquishing control of the file + descriptor fd->fd (but we still own the grpc_fd structure). */ + if (release_fd != NULL) { + *release_fd = fd->fd; + } else if (!is_fd_closed) { + close(fd->fd); + is_fd_closed = true; + } + + fd->orphaned = true; + + if (!is_fd_closed) { + gpr_log(GPR_DEBUG, "TODO: handle fd removal?"); + } + + /* Remove the active status but keep referenced. We want this grpc_fd struct + to be alive (and not added to freelist) until the end of this function */ + REF_BY(fd, 1, reason); + + GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error)); + + gpr_mu_unlock(&fd->orphaned_mu); + gpr_mu_unlock(&fd->pollable_obj.po.mu); + UNREF_BY(exec_ctx, fd, 2, reason); /* Drop the reference */ + GRPC_LOG_IF_ERROR("fd_orphan", GRPC_ERROR_REF(error)); + GRPC_ERROR_UNREF(error); +} + +static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, + grpc_fd *fd) { + gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); + return (grpc_pollset *)notifier; +} + +static bool fd_is_shutdown(grpc_fd *fd) { + return grpc_lfev_is_shutdown(&fd->read_closure); +} + +/* Might be called multiple times */ +static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { + if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, + GRPC_ERROR_REF(why))) { + shutdown(fd->fd, SHUT_RDWR); + grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); + } + GRPC_ERROR_UNREF(why); +} + +static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); +} + +static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); +} + +/******************************************************************************* + * Pollable Definitions + */ + +static void pollable_init(pollable *p, polling_obj_type type) { + po_init(&p->po, type); + p->root_worker = NULL; + p->epfd = -1; +} + +static void pollable_destroy(pollable *p) { + po_destroy(&p->po); + if (p->epfd != -1) { + close(p->epfd); + grpc_wakeup_fd_destroy(&p->wakeup); + } +} + +/* ensure that p->epfd, p->wakeup are initialized; p->po.mu must be held */ +static grpc_error *pollable_materialize(pollable *p) { + if (p->epfd == -1) { + int new_epfd = epoll_create1(EPOLL_CLOEXEC); + if (new_epfd < 0) { + return GRPC_OS_ERROR(errno, "epoll_create1"); + } + grpc_error *err = grpc_wakeup_fd_init(&p->wakeup); + if (err != GRPC_ERROR_NONE) { + close(new_epfd); + return err; + } + struct epoll_event ev; + ev.events = (uint32_t)(EPOLLIN | EPOLLET); + ev.data.ptr = (void *)(1 | (intptr_t)&p->wakeup); + if (epoll_ctl(new_epfd, EPOLL_CTL_ADD, p->wakeup.read_fd, &ev) != 0) { + err = GRPC_OS_ERROR(errno, "epoll_ctl"); + close(new_epfd); + grpc_wakeup_fd_destroy(&p->wakeup); + return err; + } + + p->epfd = new_epfd; + } + return GRPC_ERROR_NONE; +} + +/* pollable must be materialized */ +static grpc_error *pollable_add_fd(pollable *p, grpc_fd *fd) { + grpc_error *error = GRPC_ERROR_NONE; + static const char *err_desc = "pollable_add_fd"; + const int epfd = p->epfd; + GPR_ASSERT(epfd != -1); + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "add fd %p (%d) to pollable %p", fd, fd->fd, p); + } + + gpr_mu_lock(&fd->orphaned_mu); + if (fd->orphaned) { + gpr_mu_unlock(&fd->orphaned_mu); + return GRPC_ERROR_NONE; + } + struct epoll_event ev_fd; + ev_fd.events = (uint32_t)(EPOLLET | EPOLLIN | EPOLLOUT | EPOLLEXCLUSIVE); + ev_fd.data.ptr = fd; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd->fd, &ev_fd) != 0) { + switch (errno) { + case EEXIST: + break; + default: + append_error(&error, GRPC_OS_ERROR(errno, "epoll_ctl"), err_desc); + } + } + gpr_mu_unlock(&fd->orphaned_mu); + + return error; +} + +/******************************************************************************* + * Pollset Definitions + */ + +GPR_TLS_DECL(g_current_thread_pollset); +GPR_TLS_DECL(g_current_thread_worker); + +/* Global state management */ +static grpc_error *pollset_global_init(void) { + gpr_tls_init(&g_current_thread_pollset); + gpr_tls_init(&g_current_thread_worker); + pollable_init(&g_empty_pollable, PO_EMPTY_POLLABLE); + return GRPC_ERROR_NONE; +} + +static void pollset_global_shutdown(void) { + pollable_destroy(&g_empty_pollable); + gpr_tls_destroy(&g_current_thread_pollset); + gpr_tls_destroy(&g_current_thread_worker); +} + +static void pollset_maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset) { + if (pollset->shutdown_closure != NULL && pollset->root_worker == NULL && + pollset->kick_alls_pending == 0) { + GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_closure, GRPC_ERROR_NONE); + pollset->shutdown_closure = NULL; + } +} + +static void do_kick_all(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error_unused) { + grpc_error *error = GRPC_ERROR_NONE; + grpc_pollset *pollset = (grpc_pollset *)arg; + gpr_mu_lock(&pollset->pollable_obj.po.mu); + if (pollset->root_worker != NULL) { + grpc_pollset_worker *worker = pollset->root_worker; + do { + GRPC_STATS_INC_POLLSET_KICK(exec_ctx); + if (worker->pollable_obj != &pollset->pollable_obj) { + gpr_mu_lock(&worker->pollable_obj->po.mu); + } + if (worker->initialized_cv && worker != pollset->root_worker) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kickall_via_cv %p (pollable %p vs %p)", + pollset, worker, &pollset->pollable_obj, + worker->pollable_obj); + } + worker->kicked = true; + gpr_cv_signal(&worker->cv); + } else { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kickall_via_wakeup %p (pollable %p vs %p)", + pollset, worker, &pollset->pollable_obj, + worker->pollable_obj); + } + append_error(&error, + grpc_wakeup_fd_wakeup(&worker->pollable_obj->wakeup), + "pollset_shutdown"); + } + if (worker->pollable_obj != &pollset->pollable_obj) { + gpr_mu_unlock(&worker->pollable_obj->po.mu); + } + + worker = worker->links[PWL_POLLSET].next; + } while (worker != pollset->root_worker); + } + pollset->kick_alls_pending--; + pollset_maybe_finish_shutdown(exec_ctx, pollset); + gpr_mu_unlock(&pollset->pollable_obj.po.mu); + GRPC_LOG_IF_ERROR("kick_all", error); +} + +static void pollset_kick_all(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + pollset->kick_alls_pending++; + GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(do_kick_all, pollset, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); +} + +static grpc_error *pollset_kick_inner(grpc_pollset *pollset, pollable *p, + grpc_pollset_worker *specific_worker) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, + "PS:%p kick %p tls_pollset=%p tls_worker=%p " + "root_worker=(pollset:%p pollable:%p)", + p, specific_worker, (void *)gpr_tls_get(&g_current_thread_pollset), + (void *)gpr_tls_get(&g_current_thread_worker), pollset->root_worker, + p->root_worker); + } + if (specific_worker == NULL) { + if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)pollset) { + if (pollset->root_worker == NULL) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_any_without_poller", p); + } + pollset->kicked_without_poller = true; + return GRPC_ERROR_NONE; + } else { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_any_via_wakeup_fd", p); + } + grpc_error *err = pollable_materialize(p); + if (err != GRPC_ERROR_NONE) return err; + return grpc_wakeup_fd_wakeup(&p->wakeup); + } + } else { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_any_but_awake", p); + } + return GRPC_ERROR_NONE; + } + } else if (specific_worker->kicked) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_already_kicked", p); + } + return GRPC_ERROR_NONE; + } else if (gpr_tls_get(&g_current_thread_worker) == + (intptr_t)specific_worker) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_specific_but_awake", p); + } + specific_worker->kicked = true; + return GRPC_ERROR_NONE; + } else if (specific_worker == p->root_worker) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_wakeup_fd", p); + } + grpc_error *err = pollable_materialize(p); + if (err != GRPC_ERROR_NONE) return err; + specific_worker->kicked = true; + return grpc_wakeup_fd_wakeup(&p->wakeup); + } else { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p kicked_specific_via_cv", p); + } + specific_worker->kicked = true; + gpr_cv_signal(&specific_worker->cv); + return GRPC_ERROR_NONE; + } +} + +/* p->po.mu must be held before calling this function */ +static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *specific_worker) { + pollable *p = pollset->current_pollable_obj; + GRPC_STATS_INC_POLLSET_KICK(exec_ctx); + if (p != &pollset->pollable_obj) { + gpr_mu_lock(&p->po.mu); + } + grpc_error *error = pollset_kick_inner(pollset, p, specific_worker); + if (p != &pollset->pollable_obj) { + gpr_mu_unlock(&p->po.mu); + } + return error; +} + +static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + pollable_init(&pollset->pollable_obj, PO_POLLSET); + pollset->current_pollable_obj = &g_empty_pollable; + pollset->kicked_without_poller = false; + pollset->shutdown_closure = NULL; + pollset->root_worker = NULL; + *mu = &pollset->pollable_obj.po.mu; +} + +/* Convert a timespec to milliseconds: + - Very small or negative poll times are clamped to zero to do a non-blocking + poll (which becomes spin polling) + - Other small values are rounded up to one millisecond + - Longer than a millisecond polls are rounded up to the next nearest + millisecond to avoid spinning + - Infinite timeouts are converted to -1 */ +static int poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return -1; + } + + if (gpr_time_cmp(deadline, now) <= 0) { + return 0; + } + + static const gpr_timespec round_up = { + 0, /* tv_sec */ + GPR_NS_PER_MS - 1, /* tv_nsec */ + GPR_TIMESPAN /* clock_type */ + }; + timeout = gpr_time_sub(deadline, now); + int millis = gpr_time_to_millis(gpr_time_add(timeout, round_up)); + return millis >= 1 ? millis : 1; +} + +static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_pollset *notifier) { + grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); + + /* Note, it is possible that fd_become_readable might be called twice with + different 'notifier's when an fd becomes readable and it is in two epoll + sets (This can happen briefly during polling island merges). In such cases + it does not really matter which notifer is set as the read_notifier_pollset + (They would both point to the same polling island anyway) */ + /* Use release store to match with acquire load in fd_get_read_notifier */ + gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); +} + +static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); +} + +static grpc_error *fd_become_pollable_locked(grpc_fd *fd) { + grpc_error *error = GRPC_ERROR_NONE; + static const char *err_desc = "fd_become_pollable"; + if (append_error(&error, pollable_materialize(&fd->pollable_obj), err_desc)) { + append_error(&error, pollable_add_fd(&fd->pollable_obj, fd), err_desc); + } + return error; +} + +/* pollset->po.mu lock must be held by the caller before calling this */ +static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + GPR_ASSERT(pollset->shutdown_closure == NULL); + pollset->shutdown_closure = closure; + pollset_kick_all(exec_ctx, pollset); + pollset_maybe_finish_shutdown(exec_ctx, pollset); +} + +static bool pollset_is_pollable_fd(grpc_pollset *pollset, pollable *p) { + return p != &g_empty_pollable && p != &pollset->pollable_obj; +} + +static grpc_error *pollset_process_events(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, bool drain) { + static const char *err_desc = "pollset_process_events"; + grpc_error *error = GRPC_ERROR_NONE; + for (int i = 0; (drain || i < MAX_EPOLL_EVENTS_HANDLED_EACH_POLL_CALL) && + pollset->event_cursor != pollset->event_count; + i++) { + int n = pollset->event_cursor++; + struct epoll_event *ev = &pollset->events[n]; + void *data_ptr = ev->data.ptr; + if (1 & (intptr_t)data_ptr) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p got pollset_wakeup %p", pollset, data_ptr); + } + append_error(&error, + grpc_wakeup_fd_consume_wakeup( + (grpc_wakeup_fd *)((~(intptr_t)1) & (intptr_t)data_ptr)), + err_desc); + } else { + grpc_fd *fd = (grpc_fd *)data_ptr; + bool cancel = (ev->events & (EPOLLERR | EPOLLHUP)) != 0; + bool read_ev = (ev->events & (EPOLLIN | EPOLLPRI)) != 0; + bool write_ev = (ev->events & EPOLLOUT) != 0; + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, + "PS:%p got fd %p: cancel=%d read=%d " + "write=%d", + pollset, fd, cancel, read_ev, write_ev); + } + if (read_ev || cancel) { + fd_become_readable(exec_ctx, fd, pollset); + } + if (write_ev || cancel) { + fd_become_writable(exec_ctx, fd); + } + } + } + + return error; +} + +/* pollset_shutdown is guaranteed to be called before pollset_destroy. */ +static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + pollable_destroy(&pollset->pollable_obj); + if (pollset_is_pollable_fd(pollset, pollset->current_pollable_obj)) { + UNREF_BY(exec_ctx, (grpc_fd *)pollset->current_pollable_obj, 2, + "pollset_pollable"); + } + GRPC_LOG_IF_ERROR("pollset_process_events", + pollset_process_events(exec_ctx, pollset, true)); +} + +static grpc_error *pollset_epoll(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + pollable *p, gpr_timespec now, + gpr_timespec deadline) { + int timeout = poll_deadline_to_millis_timeout(deadline, now); + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + char *desc = pollable_desc(p); + gpr_log(GPR_DEBUG, "PS:%p poll %p[%s] for %dms", pollset, p, desc, timeout); + gpr_free(desc); + } + + if (timeout != 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + } + int r; + do { + GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); + r = epoll_wait(p->epfd, pollset->events, MAX_EPOLL_EVENTS, timeout); + } while (r < 0 && errno == EINTR); + if (timeout != 0) { + GRPC_SCHEDULING_END_BLOCKING_REGION; + } + + if (r < 0) return GRPC_OS_ERROR(errno, "epoll_wait"); + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p poll %p got %d events", pollset, p, r); + } + + pollset->event_cursor = 0; + pollset->event_count = r; + + return GRPC_ERROR_NONE; +} + +/* Return true if first in list */ +static bool worker_insert(grpc_pollset_worker **root, pollset_worker_links link, + grpc_pollset_worker *worker) { + if (*root == NULL) { + *root = worker; + worker->links[link].next = worker->links[link].prev = worker; + return true; + } else { + worker->links[link].next = *root; + worker->links[link].prev = worker->links[link].next->links[link].prev; + worker->links[link].next->links[link].prev = worker; + worker->links[link].prev->links[link].next = worker; + return false; + } +} + +/* Return true if last in list */ +typedef enum { EMPTIED, NEW_ROOT, REMOVED } worker_remove_result; + +static worker_remove_result worker_remove(grpc_pollset_worker **root, + pollset_worker_links link, + grpc_pollset_worker *worker) { + if (worker == *root) { + if (worker == worker->links[link].next) { + *root = NULL; + return EMPTIED; + } else { + *root = worker->links[link].next; + worker->links[link].prev->links[link].next = worker->links[link].next; + worker->links[link].next->links[link].prev = worker->links[link].prev; + return NEW_ROOT; + } + } else { + worker->links[link].prev->links[link].next = worker->links[link].next; + worker->links[link].next->links[link].prev = worker->links[link].prev; + return REMOVED; + } +} + +/* Return true if this thread should poll */ +static bool begin_worker(grpc_pollset *pollset, grpc_pollset_worker *worker, + grpc_pollset_worker **worker_hdl, gpr_timespec *now, + gpr_timespec deadline) { + bool do_poll = true; + if (worker_hdl != NULL) *worker_hdl = worker; + worker->initialized_cv = false; + worker->kicked = false; + worker->pollset = pollset; + worker->pollable_obj = pollset->current_pollable_obj; + + if (pollset_is_pollable_fd(pollset, worker->pollable_obj)) { + REF_BY((grpc_fd *)worker->pollable_obj, 2, "one_poll"); + } + + worker_insert(&pollset->root_worker, PWL_POLLSET, worker); + if (!worker_insert(&worker->pollable_obj->root_worker, PWL_POLLABLE, + worker)) { + worker->initialized_cv = true; + gpr_cv_init(&worker->cv); + if (worker->pollable_obj != &pollset->pollable_obj) { + gpr_mu_unlock(&pollset->pollable_obj.po.mu); + } + if (GRPC_TRACER_ON(grpc_polling_trace) && + worker->pollable_obj->root_worker != worker) { + gpr_log(GPR_DEBUG, "PS:%p wait %p w=%p for %dms", pollset, + worker->pollable_obj, worker, + poll_deadline_to_millis_timeout(deadline, *now)); + } + while (do_poll && worker->pollable_obj->root_worker != worker) { + if (gpr_cv_wait(&worker->cv, &worker->pollable_obj->po.mu, deadline)) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p timeout_wait %p w=%p", pollset, + worker->pollable_obj, worker); + } + do_poll = false; + } else if (worker->kicked) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p wakeup %p w=%p", pollset, + worker->pollable_obj, worker); + } + do_poll = false; + } else if (GRPC_TRACER_ON(grpc_polling_trace) && + worker->pollable_obj->root_worker != worker) { + gpr_log(GPR_DEBUG, "PS:%p spurious_wakeup %p w=%p", pollset, + worker->pollable_obj, worker); + } + } + if (worker->pollable_obj != &pollset->pollable_obj) { + gpr_mu_unlock(&worker->pollable_obj->po.mu); + gpr_mu_lock(&pollset->pollable_obj.po.mu); + gpr_mu_lock(&worker->pollable_obj->po.mu); + } + *now = gpr_now(now->clock_type); + } + + return do_poll && pollset->shutdown_closure == NULL && + pollset->current_pollable_obj == worker->pollable_obj; +} + +static void end_worker(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *worker, + grpc_pollset_worker **worker_hdl) { + if (NEW_ROOT == + worker_remove(&worker->pollable_obj->root_worker, PWL_POLLABLE, worker)) { + gpr_cv_signal(&worker->pollable_obj->root_worker->cv); + } + if (worker->initialized_cv) { + gpr_cv_destroy(&worker->cv); + } + if (pollset_is_pollable_fd(pollset, worker->pollable_obj)) { + UNREF_BY(exec_ctx, (grpc_fd *)worker->pollable_obj, 2, "one_poll"); + } + if (EMPTIED == worker_remove(&pollset->root_worker, PWL_POLLSET, worker)) { + pollset_maybe_finish_shutdown(exec_ctx, pollset); + } +} + +/* pollset->po.mu lock must be held by the caller before calling this. + The function pollset_work() may temporarily release the lock (pollset->po.mu) + during the course of its execution but it will always re-acquire the lock and + ensure that it is held by the time the function returns */ +static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + grpc_pollset_worker worker; + if (0 && GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p work hdl=%p worker=%p now=%" PRId64 + ".%09d deadline=%" PRId64 ".%09d kwp=%d root_worker=%p", + pollset, worker_hdl, &worker, now.tv_sec, now.tv_nsec, + deadline.tv_sec, deadline.tv_nsec, pollset->kicked_without_poller, + pollset->root_worker); + } + grpc_error *error = GRPC_ERROR_NONE; + static const char *err_desc = "pollset_work"; + if (pollset->kicked_without_poller) { + pollset->kicked_without_poller = false; + return GRPC_ERROR_NONE; + } + if (pollset->current_pollable_obj != &pollset->pollable_obj) { + gpr_mu_lock(&pollset->current_pollable_obj->po.mu); + } + if (begin_worker(pollset, &worker, worker_hdl, &now, deadline)) { + gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); + gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); + GPR_ASSERT(!pollset->shutdown_closure); + append_error(&error, pollable_materialize(worker.pollable_obj), err_desc); + if (worker.pollable_obj != &pollset->pollable_obj) { + gpr_mu_unlock(&worker.pollable_obj->po.mu); + } + gpr_mu_unlock(&pollset->pollable_obj.po.mu); + if (pollset->event_cursor == pollset->event_count) { + append_error(&error, pollset_epoll(exec_ctx, pollset, worker.pollable_obj, + now, deadline), + err_desc); + } + append_error(&error, pollset_process_events(exec_ctx, pollset, false), + err_desc); + gpr_mu_lock(&pollset->pollable_obj.po.mu); + if (worker.pollable_obj != &pollset->pollable_obj) { + gpr_mu_lock(&worker.pollable_obj->po.mu); + } + gpr_tls_set(&g_current_thread_pollset, 0); + gpr_tls_set(&g_current_thread_worker, 0); + pollset_maybe_finish_shutdown(exec_ctx, pollset); + } + end_worker(exec_ctx, pollset, &worker, worker_hdl); + if (worker.pollable_obj != &pollset->pollable_obj) { + gpr_mu_unlock(&worker.pollable_obj->po.mu); + } + if (grpc_exec_ctx_has_work(exec_ctx)) { + gpr_mu_unlock(&pollset->pollable_obj.po.mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->pollable_obj.po.mu); + } + return error; +} + +static void unref_fd_no_longer_poller(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_fd *fd = (grpc_fd *)arg; + UNREF_BY(exec_ctx, fd, 2, "pollset_pollable"); +} + +/* expects pollsets locked, flag whether fd is locked or not */ +static grpc_error *pollset_add_fd_locked(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, grpc_fd *fd, + bool fd_locked) { + static const char *err_desc = "pollset_add_fd"; + grpc_error *error = GRPC_ERROR_NONE; + if (pollset->current_pollable_obj == &g_empty_pollable) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, + "PS:%p add fd %p; transition pollable from empty to fd", pollset, + fd); + } + /* empty pollable --> single fd pollable */ + pollset_kick_all(exec_ctx, pollset); + pollset->current_pollable_obj = &fd->pollable_obj; + if (!fd_locked) gpr_mu_lock(&fd->pollable_obj.po.mu); + append_error(&error, fd_become_pollable_locked(fd), err_desc); + if (!fd_locked) gpr_mu_unlock(&fd->pollable_obj.po.mu); + REF_BY(fd, 2, "pollset_pollable"); + } else if (pollset->current_pollable_obj == &pollset->pollable_obj) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "PS:%p add fd %p; already multipolling", pollset, fd); + } + append_error(&error, pollable_add_fd(pollset->current_pollable_obj, fd), + err_desc); + } else if (pollset->current_pollable_obj != &fd->pollable_obj) { + grpc_fd *had_fd = (grpc_fd *)pollset->current_pollable_obj; + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, + "PS:%p add fd %p; transition pollable from fd %p to multipoller", + pollset, fd, had_fd); + } + /* Introduce a spurious completion. + If we do not, then it may be that the fd-specific epoll set consumed + a completion without being polled, leading to a missed edge going up. */ + grpc_lfev_set_ready(exec_ctx, &had_fd->read_closure, "read"); + grpc_lfev_set_ready(exec_ctx, &had_fd->write_closure, "write"); + pollset_kick_all(exec_ctx, pollset); + pollset->current_pollable_obj = &pollset->pollable_obj; + if (append_error(&error, pollable_materialize(&pollset->pollable_obj), + err_desc)) { + pollable_add_fd(&pollset->pollable_obj, had_fd); + pollable_add_fd(&pollset->pollable_obj, fd); + } + GRPC_CLOSURE_SCHED(exec_ctx, + GRPC_CLOSURE_CREATE(unref_fd_no_longer_poller, had_fd, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); + } + return error; +} + +static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) { + gpr_mu_lock(&pollset->pollable_obj.po.mu); + grpc_error *error = pollset_add_fd_locked(exec_ctx, pollset, fd, false); + gpr_mu_unlock(&pollset->pollable_obj.po.mu); + GRPC_LOG_IF_ERROR("pollset_add_fd", error); +} + +/******************************************************************************* + * Pollset-set Definitions + */ + +static grpc_pollset_set *pollset_set_create(void) { + grpc_pollset_set *pss = (grpc_pollset_set *)gpr_zalloc(sizeof(*pss)); + po_init(&pss->po, PO_POLLSET_SET); + return pss; +} + +static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss) { + po_destroy(&pss->po); + gpr_free(pss); +} + +static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, + grpc_fd *fd) { + po_join(exec_ctx, &pss->po, &fd->pollable_obj.po); +} + +static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, + grpc_fd *fd) {} + +static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss, grpc_pollset *ps) { + po_join(exec_ctx, &pss->po, &ps->pollable_obj.po); +} + +static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss, grpc_pollset *ps) {} + +static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + po_join(exec_ctx, &bag->po, &item->po); +} + +static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) {} + +static void po_init(polling_obj *po, polling_obj_type type) { + gpr_mu_init(&po->mu); + po->type = type; + po->group = NULL; + po->next = po; + po->prev = po; +} + +static polling_group *pg_lock_latest(polling_group *pg) { + /* assumes pg unlocked; consumes ref, returns ref */ + gpr_mu_lock(&pg->po.mu); + while (pg->po.group != NULL) { + polling_group *new_pg = pg_ref(pg->po.group); + gpr_mu_unlock(&pg->po.mu); + pg_unref(pg); + pg = new_pg; + gpr_mu_lock(&pg->po.mu); + } + return pg; +} + +static void po_destroy(polling_obj *po) { + if (po->group != NULL) { + polling_group *pg = pg_lock_latest(po->group); + po->prev->next = po->next; + po->next->prev = po->prev; + gpr_mu_unlock(&pg->po.mu); + pg_unref(pg); + } + gpr_mu_destroy(&po->mu); +} + +static polling_group *pg_ref(polling_group *pg) { + gpr_ref(&pg->refs); + return pg; +} + +static void pg_unref(polling_group *pg) { + if (gpr_unref(&pg->refs)) { + po_destroy(&pg->po); + gpr_free(pg); + } +} + +static int po_cmp(polling_obj *a, polling_obj *b) { + if (a == b) return 0; + if (a->type < b->type) return -1; + if (a->type > b->type) return 1; + if (a < b) return -1; + assert(a > b); + return 1; +} + +static void po_join(grpc_exec_ctx *exec_ctx, polling_obj *a, polling_obj *b) { + switch (po_cmp(a, b)) { + case 0: + return; + case 1: + GPR_SWAP(polling_obj *, a, b); + /* fall through */ + case -1: + gpr_mu_lock(&a->mu); + gpr_mu_lock(&b->mu); + + if (a->group == NULL) { + if (b->group == NULL) { + polling_obj *initial_po[] = {a, b}; + pg_create(exec_ctx, initial_po, GPR_ARRAY_SIZE(initial_po)); + gpr_mu_unlock(&a->mu); + gpr_mu_unlock(&b->mu); + } else { + polling_group *b_group = pg_ref(b->group); + gpr_mu_unlock(&b->mu); + gpr_mu_unlock(&a->mu); + pg_join(exec_ctx, b_group, a); + } + } else if (b->group == NULL) { + polling_group *a_group = pg_ref(a->group); + gpr_mu_unlock(&a->mu); + gpr_mu_unlock(&b->mu); + pg_join(exec_ctx, a_group, b); + } else if (a->group == b->group) { + /* nothing to do */ + gpr_mu_unlock(&a->mu); + gpr_mu_unlock(&b->mu); + } else { + polling_group *a_group = pg_ref(a->group); + polling_group *b_group = pg_ref(b->group); + gpr_mu_unlock(&a->mu); + gpr_mu_unlock(&b->mu); + pg_merge(exec_ctx, a_group, b_group); + } + } +} + +static void pg_notify(grpc_exec_ctx *exec_ctx, polling_obj *a, polling_obj *b) { + if (a->type == PO_FD && b->type == PO_POLLSET) { + pollset_add_fd_locked(exec_ctx, (grpc_pollset *)b, (grpc_fd *)a, true); + } else if (a->type == PO_POLLSET && b->type == PO_FD) { + pollset_add_fd_locked(exec_ctx, (grpc_pollset *)a, (grpc_fd *)b, true); + } +} + +static void pg_broadcast(grpc_exec_ctx *exec_ctx, polling_group *from, + polling_group *to) { + for (polling_obj *a = from->po.next; a != &from->po; a = a->next) { + for (polling_obj *b = to->po.next; b != &to->po; b = b->next) { + if (po_cmp(a, b) < 0) { + gpr_mu_lock(&a->mu); + gpr_mu_lock(&b->mu); + } else { + GPR_ASSERT(po_cmp(a, b) != 0); + gpr_mu_lock(&b->mu); + gpr_mu_lock(&a->mu); + } + pg_notify(exec_ctx, a, b); + gpr_mu_unlock(&a->mu); + gpr_mu_unlock(&b->mu); + } + } +} + +static void pg_create(grpc_exec_ctx *exec_ctx, polling_obj **initial_po, + size_t initial_po_count) { + /* assumes all polling objects in initial_po are locked */ + polling_group *pg = (polling_group *)gpr_malloc(sizeof(*pg)); + po_init(&pg->po, PO_POLLING_GROUP); + gpr_ref_init(&pg->refs, (int)initial_po_count); + for (size_t i = 0; i < initial_po_count; i++) { + GPR_ASSERT(initial_po[i]->group == NULL); + initial_po[i]->group = pg; + } + for (size_t i = 1; i < initial_po_count; i++) { + initial_po[i]->prev = initial_po[i - 1]; + } + for (size_t i = 0; i < initial_po_count - 1; i++) { + initial_po[i]->next = initial_po[i + 1]; + } + initial_po[0]->prev = &pg->po; + initial_po[initial_po_count - 1]->next = &pg->po; + pg->po.next = initial_po[0]; + pg->po.prev = initial_po[initial_po_count - 1]; + for (size_t i = 1; i < initial_po_count; i++) { + for (size_t j = 0; j < i; j++) { + pg_notify(exec_ctx, initial_po[i], initial_po[j]); + } + } +} + +static void pg_join(grpc_exec_ctx *exec_ctx, polling_group *pg, + polling_obj *po) { + /* assumes neither pg nor po are locked; consumes one ref to pg */ + pg = pg_lock_latest(pg); + /* pg locked */ + for (polling_obj *existing = pg->po.next /* skip pg - it's just a stub */; + existing != &pg->po; existing = existing->next) { + if (po_cmp(po, existing) < 0) { + gpr_mu_lock(&po->mu); + gpr_mu_lock(&existing->mu); + } else { + GPR_ASSERT(po_cmp(po, existing) != 0); + gpr_mu_lock(&existing->mu); + gpr_mu_lock(&po->mu); + } + /* pg, po, existing locked */ + if (po->group != NULL) { + gpr_mu_unlock(&pg->po.mu); + polling_group *po_group = pg_ref(po->group); + gpr_mu_unlock(&po->mu); + gpr_mu_unlock(&existing->mu); + pg_merge(exec_ctx, pg, po_group); + /* early exit: polling obj picked up a group during joining: we needed + to do a full merge */ + return; + } + pg_notify(exec_ctx, po, existing); + gpr_mu_unlock(&po->mu); + gpr_mu_unlock(&existing->mu); + } + gpr_mu_lock(&po->mu); + if (po->group != NULL) { + gpr_mu_unlock(&pg->po.mu); + polling_group *po_group = pg_ref(po->group); + gpr_mu_unlock(&po->mu); + pg_merge(exec_ctx, pg, po_group); + /* early exit: polling obj picked up a group during joining: we needed + to do a full merge */ + return; + } + po->group = pg; + po->next = &pg->po; + po->prev = pg->po.prev; + po->prev->next = po->next->prev = po; + gpr_mu_unlock(&pg->po.mu); + gpr_mu_unlock(&po->mu); +} + +static void pg_merge(grpc_exec_ctx *exec_ctx, polling_group *a, + polling_group *b) { + for (;;) { + if (a == b) { + pg_unref(a); + pg_unref(b); + return; + } + if (a > b) GPR_SWAP(polling_group *, a, b); + gpr_mu_lock(&a->po.mu); + gpr_mu_lock(&b->po.mu); + if (a->po.group != NULL) { + polling_group *m2 = pg_ref(a->po.group); + gpr_mu_unlock(&a->po.mu); + gpr_mu_unlock(&b->po.mu); + pg_unref(a); + a = m2; + } else if (b->po.group != NULL) { + polling_group *m2 = pg_ref(b->po.group); + gpr_mu_unlock(&a->po.mu); + gpr_mu_unlock(&b->po.mu); + pg_unref(b); + b = m2; + } else { + break; + } + } + polling_group **unref = NULL; + size_t unref_count = 0; + size_t unref_cap = 0; + b->po.group = a; + pg_broadcast(exec_ctx, a, b); + pg_broadcast(exec_ctx, b, a); + while (b->po.next != &b->po) { + polling_obj *po = b->po.next; + gpr_mu_lock(&po->mu); + if (unref_count == unref_cap) { + unref_cap = GPR_MAX(8, 3 * unref_cap / 2); + unref = (polling_group **)gpr_realloc(unref, unref_cap * sizeof(*unref)); + } + unref[unref_count++] = po->group; + po->group = pg_ref(a); + // unlink from b + po->prev->next = po->next; + po->next->prev = po->prev; + // link to a + po->next = &a->po; + po->prev = a->po.prev; + po->next->prev = po->prev->next = po; + gpr_mu_unlock(&po->mu); + } + gpr_mu_unlock(&a->po.mu); + gpr_mu_unlock(&b->po.mu); + for (size_t i = 0; i < unref_count; i++) { + pg_unref(unref[i]); + } + gpr_free(unref); + pg_unref(b); +} + +/******************************************************************************* + * Event engine binding + */ + +static void shutdown_engine(void) { + fd_global_shutdown(); + pollset_global_shutdown(); +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_is_shutdown, + fd_get_read_notifier_pollset, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_destroy, + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_engine, +}; + +const grpc_event_engine_vtable *grpc_init_epollex_linux( + bool explicitly_requested) { + if (!grpc_has_wakeup_fd()) { + return NULL; + } + + if (!grpc_is_epollexclusive_available()) { + return NULL; + } + + fd_global_init(); + + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + pollset_global_shutdown(); + fd_global_shutdown(); + return NULL; + } + + return &vtable; +} + +#else /* defined(GRPC_LINUX_EPOLL) */ +#if defined(GRPC_POSIX_SOCKET) +#include "src/core/lib/iomgr/ev_posix.h" +/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return + * NULL */ +const grpc_event_engine_vtable *grpc_init_epollex_linux( + bool explicitly_requested) { + return NULL; +} +#endif /* defined(GRPC_POSIX_SOCKET) */ + +#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epollsig_linux.c b/src/core/lib/iomgr/ev_epollsig_linux.c deleted file mode 100644 index 4d8bdf1401..0000000000 --- a/src/core/lib/iomgr/ev_epollsig_linux.c +++ /dev/null @@ -1,1769 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -#ifdef GRPC_LINUX_EPOLL - -#include "src/core/lib/iomgr/ev_epollsig_linux.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/lockfree_event.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/block_annotate.h" - -#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) - -#define GRPC_POLLING_TRACE(...) \ - if (GRPC_TRACER_ON(grpc_polling_trace)) { \ - gpr_log(GPR_INFO, __VA_ARGS__); \ - } - -static int grpc_wakeup_signal = -1; -static bool is_grpc_wakeup_signal_initialized = false; - -/* Implements the function defined in grpc_posix.h. This function might be - * called before even calling grpc_init() to set either a different signal to - * use. If signum == -1, then the use of signals is disabled */ -void grpc_use_signal(int signum) { - grpc_wakeup_signal = signum; - is_grpc_wakeup_signal_initialized = true; - - if (grpc_wakeup_signal < 0) { - gpr_log(GPR_INFO, - "Use of signals is disabled. Epoll engine will not be used"); - } else { - gpr_log(GPR_INFO, "epoll engine will be using signal: %d", - grpc_wakeup_signal); - } -} - -struct polling_island; - -typedef enum { - POLL_OBJ_FD, - POLL_OBJ_POLLSET, - POLL_OBJ_POLLSET_SET -} poll_obj_type; - -typedef struct poll_obj { -#ifndef NDEBUG - poll_obj_type obj_type; -#endif - gpr_mu mu; - struct polling_island *pi; -} poll_obj; - -const char *poll_obj_string(poll_obj_type po_type) { - switch (po_type) { - case POLL_OBJ_FD: - return "fd"; - case POLL_OBJ_POLLSET: - return "pollset"; - case POLL_OBJ_POLLSET_SET: - return "pollset_set"; - } - - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -/******************************************************************************* - * Fd Declarations - */ - -#define FD_FROM_PO(po) ((grpc_fd *)(po)) - -struct grpc_fd { - poll_obj po; - - int fd; - /* refst format: - bit 0 : 1=Active / 0=Orphaned - bits 1-n : refcount - Ref/Unref by two to avoid altering the orphaned bit */ - gpr_atm refst; - - /* The fd is either closed or we relinquished control of it. In either - cases, this indicates that the 'fd' on this structure is no longer - valid */ - bool orphaned; - - gpr_atm read_closure; - gpr_atm write_closure; - - struct grpc_fd *freelist_next; - grpc_closure *on_done_closure; - - /* The pollset that last noticed that the fd is readable. The actual type - * stored in this is (grpc_pollset *) */ - gpr_atm read_notifier_pollset; - - grpc_iomgr_object iomgr_object; -}; - -/* Reference counting for fds */ -#ifndef NDEBUG -static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); -static void fd_unref(grpc_fd *fd, const char *reason, const char *file, - int line); -#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__) -#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__) -#else -static void fd_ref(grpc_fd *fd); -static void fd_unref(grpc_fd *fd); -#define GRPC_FD_REF(fd, reason) fd_ref(fd) -#define GRPC_FD_UNREF(fd, reason) fd_unref(fd) -#endif - -static void fd_global_init(void); -static void fd_global_shutdown(void); - -/******************************************************************************* - * Polling island Declarations - */ - -#ifndef NDEBUG - -#define PI_ADD_REF(p, r) pi_add_ref_dbg((p), (r), __FILE__, __LINE__) -#define PI_UNREF(exec_ctx, p, r) \ - pi_unref_dbg((exec_ctx), (p), (r), __FILE__, __LINE__) - -#else - -#define PI_ADD_REF(p, r) pi_add_ref((p)) -#define PI_UNREF(exec_ctx, p, r) pi_unref((exec_ctx), (p)) - -#endif - -/* This is also used as grpc_workqueue (by directly casing it) */ -typedef struct polling_island { - gpr_mu mu; - /* Ref count. Use PI_ADD_REF() and PI_UNREF() macros to increment/decrement - the refcount. - Once the ref count becomes zero, this structure is destroyed which means - we should ensure that there is never a scenario where a PI_ADD_REF() is - racing with a PI_UNREF() that just made the ref_count zero. */ - gpr_atm ref_count; - - /* Pointer to the polling_island this merged into. - * merged_to value is only set once in polling_island's lifetime (and that too - * only if the island is merged with another island). Because of this, we can - * use gpr_atm type here so that we can do atomic access on this and reduce - * lock contention on 'mu' mutex. - * - * Note that if this field is not NULL (i.e not 0), all the remaining fields - * (except mu and ref_count) are invalid and must be ignored. */ - gpr_atm merged_to; - - /* Number of threads currently polling on this island */ - gpr_atm poller_count; - - /* The fd of the underlying epoll set */ - int epoll_fd; - - /* The file descriptors in the epoll set */ - size_t fd_cnt; - size_t fd_capacity; - grpc_fd **fds; -} polling_island; - -/******************************************************************************* - * Pollset Declarations - */ -struct grpc_pollset_worker { - /* Thread id of this worker */ - pthread_t pt_id; - - /* Used to prevent a worker from getting kicked multiple times */ - gpr_atm is_kicked; - struct grpc_pollset_worker *next; - struct grpc_pollset_worker *prev; -}; - -struct grpc_pollset { - poll_obj po; - - grpc_pollset_worker root_worker; - bool kicked_without_pollers; - - bool shutting_down; /* Is the pollset shutting down ? */ - bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */ - grpc_closure *shutdown_done; /* Called after after shutdown is complete */ -}; - -/******************************************************************************* - * Pollset-set Declarations - */ -struct grpc_pollset_set { - poll_obj po; -}; - -/******************************************************************************* - * Common helpers - */ - -static bool append_error(grpc_error **composite, grpc_error *error, - const char *desc) { - if (error == GRPC_ERROR_NONE) return true; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); - } - *composite = grpc_error_add_child(*composite, error); - return false; -} - -/******************************************************************************* - * Polling island Definitions - */ - -/* The wakeup fd that is used to wake up all threads in a Polling island. This - is useful in the polling island merge operation where we need to wakeup all - the threads currently polling the smaller polling island (so that they can - start polling the new/merged polling island) - - NOTE: This fd is initialized to be readable and MUST NOT be consumed i.e the - threads that woke up MUST NOT call grpc_wakeup_fd_consume_wakeup() */ -static grpc_wakeup_fd polling_island_wakeup_fd; - -/* The polling island being polled right now. - See comments in workqueue_maybe_wakeup for why this is tracked. */ -static __thread polling_island *g_current_thread_polling_island; - -/* Forward declaration */ -static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi); - -#ifdef GRPC_TSAN -/* Currently TSAN may incorrectly flag data races between epoll_ctl and - epoll_wait for any grpc_fd structs that are added to the epoll set via - epoll_ctl and are returned (within a very short window) via epoll_wait(). - - To work-around this race, we establish a happens-before relation between - the code just-before epoll_ctl() and the code after epoll_wait() by using - this atomic */ -gpr_atm g_epoll_sync; -#endif /* defined(GRPC_TSAN) */ - -static void pi_add_ref(polling_island *pi); -static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi); - -#ifndef NDEBUG -static void pi_add_ref_dbg(polling_island *pi, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); - gpr_log(GPR_DEBUG, "Add ref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - pi, old_cnt, old_cnt + 1, reason, file, line); - } - pi_add_ref(pi); -} - -static void pi_unref_dbg(grpc_exec_ctx *exec_ctx, polling_island *pi, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); - gpr_log(GPR_DEBUG, "Unref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR - " (%s) - (%s, %d)", - pi, old_cnt, (old_cnt - 1), reason, file, line); - } - pi_unref(exec_ctx, pi); -} -#endif - -static void pi_add_ref(polling_island *pi) { - gpr_atm_no_barrier_fetch_add(&pi->ref_count, 1); -} - -static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi) { - /* If ref count went to zero, delete the polling island. - Note that this deletion not be done under a lock. Once the ref count goes - to zero, we are guaranteed that no one else holds a reference to the - polling island (and that there is no racing pi_add_ref() call either). - - Also, if we are deleting the polling island and the merged_to field is - non-empty, we should remove a ref to the merged_to polling island - */ - if (1 == gpr_atm_full_fetch_add(&pi->ref_count, -1)) { - polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - polling_island_delete(exec_ctx, pi); - if (next != NULL) { - PI_UNREF(exec_ctx, next, "pi_delete"); /* Recursive call */ - } - } -} - -/* The caller is expected to hold pi->mu lock before calling this function */ -static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds, - size_t fd_count, bool add_fd_refs, - grpc_error **error) { - int err; - size_t i; - struct epoll_event ev; - char *err_msg; - const char *err_desc = "polling_island_add_fds"; - -#ifdef GRPC_TSAN - /* See the definition of g_epoll_sync for more context */ - gpr_atm_rel_store(&g_epoll_sync, (gpr_atm)0); -#endif /* defined(GRPC_TSAN) */ - - for (i = 0; i < fd_count; i++) { - ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); - ev.data.ptr = fds[i]; - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, fds[i]->fd, &ev); - - if (err < 0) { - if (errno != EEXIST) { - gpr_asprintf( - &err_msg, - "epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)", - pi->epoll_fd, fds[i]->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - - continue; - } - - if (pi->fd_cnt == pi->fd_capacity) { - pi->fd_capacity = GPR_MAX(pi->fd_capacity + 8, pi->fd_cnt * 3 / 2); - pi->fds = - (grpc_fd **)gpr_realloc(pi->fds, sizeof(grpc_fd *) * pi->fd_capacity); - } - - pi->fds[pi->fd_cnt++] = fds[i]; - if (add_fd_refs) { - GRPC_FD_REF(fds[i], "polling_island"); - } - } -} - -/* The caller is expected to hold pi->mu before calling this */ -static void polling_island_add_wakeup_fd_locked(polling_island *pi, - grpc_wakeup_fd *wakeup_fd, - grpc_error **error) { - struct epoll_event ev; - int err; - char *err_msg; - const char *err_desc = "polling_island_add_wakeup_fd"; - - ev.events = (uint32_t)(EPOLLIN | EPOLLET); - ev.data.ptr = wakeup_fd; - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, - GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev); - if (err < 0 && errno != EEXIST) { - gpr_asprintf(&err_msg, - "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with " - "error: %d (%s)", - pi->epoll_fd, GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), errno, - strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } -} - -/* The caller is expected to hold pi->mu lock before calling this function */ -static void polling_island_remove_all_fds_locked(polling_island *pi, - bool remove_fd_refs, - grpc_error **error) { - int err; - size_t i; - char *err_msg; - const char *err_desc = "polling_island_remove_fds"; - - for (i = 0; i < pi->fd_cnt; i++) { - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, pi->fds[i]->fd, NULL); - if (err < 0 && errno != ENOENT) { - gpr_asprintf(&err_msg, - "epoll_ctl (epoll_fd: %d) delete fds[%zu]: %d failed with " - "error: %d (%s)", - pi->epoll_fd, i, pi->fds[i]->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - - if (remove_fd_refs) { - GRPC_FD_UNREF(pi->fds[i], "polling_island"); - } - } - - pi->fd_cnt = 0; -} - -/* The caller is expected to hold pi->mu lock before calling this function */ -static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd, - bool is_fd_closed, - grpc_error **error) { - int err; - size_t i; - char *err_msg; - const char *err_desc = "polling_island_remove_fd"; - - /* If fd is already closed, then it would have been automatically been removed - from the epoll set */ - if (!is_fd_closed) { - err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); - if (err < 0 && errno != ENOENT) { - gpr_asprintf( - &err_msg, - "epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)", - pi->epoll_fd, fd->fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - gpr_free(err_msg); - } - } - - for (i = 0; i < pi->fd_cnt; i++) { - if (pi->fds[i] == fd) { - pi->fds[i] = pi->fds[--pi->fd_cnt]; - GRPC_FD_UNREF(fd, "polling_island"); - break; - } - } -} - -/* Might return NULL in case of an error */ -static polling_island *polling_island_create(grpc_exec_ctx *exec_ctx, - grpc_fd *initial_fd, - grpc_error **error) { - polling_island *pi = NULL; - const char *err_desc = "polling_island_create"; - - *error = GRPC_ERROR_NONE; - - pi = (polling_island *)gpr_malloc(sizeof(*pi)); - gpr_mu_init(&pi->mu); - pi->fd_cnt = 0; - pi->fd_capacity = 0; - pi->fds = NULL; - pi->epoll_fd = -1; - - gpr_atm_rel_store(&pi->ref_count, 0); - gpr_atm_rel_store(&pi->poller_count, 0); - gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL); - - pi->epoll_fd = epoll_create1(EPOLL_CLOEXEC); - - if (pi->epoll_fd < 0) { - append_error(error, GRPC_OS_ERROR(errno, "epoll_create1"), err_desc); - goto done; - } - - if (initial_fd != NULL) { - polling_island_add_fds_locked(pi, &initial_fd, 1, true, error); - } - -done: - if (*error != GRPC_ERROR_NONE) { - polling_island_delete(exec_ctx, pi); - pi = NULL; - } - return pi; -} - -static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi) { - GPR_ASSERT(pi->fd_cnt == 0); - - if (pi->epoll_fd >= 0) { - close(pi->epoll_fd); - } - gpr_mu_destroy(&pi->mu); - gpr_free(pi->fds); - gpr_free(pi); -} - -/* Attempts to gets the last polling island in the linked list (liked by the - * 'merged_to' field). Since this does not lock the polling island, there are no - * guarantees that the island returned is the last island */ -static polling_island *polling_island_maybe_get_latest(polling_island *pi) { - polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - while (next != NULL) { - pi = next; - next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - } - - return pi; -} - -/* Gets the lock on the *latest* polling island i.e the last polling island in - the linked list (linked by the 'merged_to' field). Call gpr_mu_unlock on the - returned polling island's mu. - Usage: To lock/unlock polling island "pi", do the following: - polling_island *pi_latest = polling_island_lock(pi); - ... - ... critical section .. - ... - gpr_mu_unlock(&pi_latest->mu); // NOTE: use pi_latest->mu. NOT pi->mu */ -static polling_island *polling_island_lock(polling_island *pi) { - polling_island *next = NULL; - - while (true) { - next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - if (next == NULL) { - /* Looks like 'pi' is the last node in the linked list but unless we check - this by holding the pi->mu lock, we cannot be sure (i.e without the - pi->mu lock, we don't prevent island merges). - To be absolutely sure, check once more by holding the pi->mu lock */ - gpr_mu_lock(&pi->mu); - next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); - if (next == NULL) { - /* pi is infact the last node and we have the pi->mu lock. we're done */ - break; - } - - /* pi->merged_to is not NULL i.e pi isn't the last node anymore. pi->mu - * isn't the lock we are interested in. Continue traversing the list */ - gpr_mu_unlock(&pi->mu); - } - - pi = next; - } - - return pi; -} - -/* Gets the lock on the *latest* polling islands in the linked lists pointed by - *p and *q (and also updates *p and *q to point to the latest polling islands) - - This function is needed because calling the following block of code to obtain - locks on polling islands (*p and *q) is prone to deadlocks. - { - polling_island_lock(*p, true); - polling_island_lock(*q, true); - } - - Usage/example: - polling_island *p1; - polling_island *p2; - .. - polling_island_lock_pair(&p1, &p2); - .. - .. Critical section with both p1 and p2 locked - .. - // Release locks: Always call polling_island_unlock_pair() to release locks - polling_island_unlock_pair(p1, p2); -*/ -static void polling_island_lock_pair(polling_island **p, polling_island **q) { - polling_island *pi_1 = *p; - polling_island *pi_2 = *q; - polling_island *next_1 = NULL; - polling_island *next_2 = NULL; - - /* The algorithm is simple: - - Go to the last polling islands in the linked lists *pi_1 and *pi_2 (and - keep updating pi_1 and pi_2) - - Then obtain locks on the islands by following a lock order rule of - locking polling_island with lower address first - Special case: Before obtaining the locks, check if pi_1 and pi_2 are - pointing to the same island. If that is the case, we can just call - polling_island_lock() - - After obtaining both the locks, double check that the polling islands - are still the last polling islands in their respective linked lists - (this is because there might have been polling island merges before - we got the lock) - - If the polling islands are the last islands, we are done. If not, - release the locks and continue the process from the first step */ - while (true) { - next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); - while (next_1 != NULL) { - pi_1 = next_1; - next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); - } - - next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); - while (next_2 != NULL) { - pi_2 = next_2; - next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); - } - - if (pi_1 == pi_2) { - pi_1 = pi_2 = polling_island_lock(pi_1); - break; - } - - if (pi_1 < pi_2) { - gpr_mu_lock(&pi_1->mu); - gpr_mu_lock(&pi_2->mu); - } else { - gpr_mu_lock(&pi_2->mu); - gpr_mu_lock(&pi_1->mu); - } - - next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); - next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); - if (next_1 == NULL && next_2 == NULL) { - break; - } - - gpr_mu_unlock(&pi_1->mu); - gpr_mu_unlock(&pi_2->mu); - } - - *p = pi_1; - *q = pi_2; -} - -static void polling_island_unlock_pair(polling_island *p, polling_island *q) { - if (p == q) { - gpr_mu_unlock(&p->mu); - } else { - gpr_mu_unlock(&p->mu); - gpr_mu_unlock(&q->mu); - } -} - -static polling_island *polling_island_merge(polling_island *p, - polling_island *q, - grpc_error **error) { - /* Get locks on both the polling islands */ - polling_island_lock_pair(&p, &q); - - if (p != q) { - /* Make sure that p points to the polling island with fewer fds than q */ - if (p->fd_cnt > q->fd_cnt) { - GPR_SWAP(polling_island *, p, q); - } - - /* Merge p with q i.e move all the fds from p (The one with fewer fds) to q - Note that the refcounts on the fds being moved will not change here. - This is why the last param in the following two functions is 'false') */ - polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false, error); - polling_island_remove_all_fds_locked(p, false, error); - - /* Wakeup all the pollers (if any) on p so that they pickup this change */ - polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd, error); - - /* Add the 'merged_to' link from p --> q */ - gpr_atm_rel_store(&p->merged_to, (gpr_atm)q); - PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */ - } - /* else if p == q, nothing needs to be done */ - - polling_island_unlock_pair(p, q); - - /* Return the merged polling island (Note that no merge would have happened - if p == q which is ok) */ - return q; -} - -static grpc_error *polling_island_global_init() { - grpc_error *error = GRPC_ERROR_NONE; - - error = grpc_wakeup_fd_init(&polling_island_wakeup_fd); - if (error == GRPC_ERROR_NONE) { - error = grpc_wakeup_fd_wakeup(&polling_island_wakeup_fd); - } - - return error; -} - -static void polling_island_global_shutdown() { - grpc_wakeup_fd_destroy(&polling_island_wakeup_fd); -} - -/******************************************************************************* - * Fd Definitions - */ - -/* We need to keep a freelist not because of any concerns of malloc performance - * but instead so that implementations with multiple threads in (for example) - * epoll_wait deal with the race between pollset removal and incoming poll - * notifications. - * - * The problem is that the poller ultimately holds a reference to this - * object, so it is very difficult to know when is safe to free it, at least - * without some expensive synchronization. - * - * If we keep the object freelisted, in the worst case losing this race just - * becomes a spurious read notification on a reused fd. - */ - -/* The alarm system needs to be able to wakeup 'some poller' sometimes - * (specifically when a new alarm needs to be triggered earlier than the next - * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a - * case occurs. */ - -static grpc_fd *fd_freelist = NULL; -static gpr_mu fd_freelist_mu; - -#ifndef NDEBUG -#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) -#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__) -static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, - "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", - fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), - gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); - } -#else -#define REF_BY(fd, n, reason) ref_by(fd, n) -#define UNREF_BY(fd, n, reason) unref_by(fd, n) -static void ref_by(grpc_fd *fd, int n) { -#endif - GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); -} - -#ifndef NDEBUG -static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, - "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", - fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), - gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); - } -#else -static void unref_by(grpc_fd *fd, int n) { -#endif - gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); - if (old == n) { - /* Add the fd to the freelist */ - gpr_mu_lock(&fd_freelist_mu); - fd->freelist_next = fd_freelist; - fd_freelist = fd; - grpc_iomgr_unregister_object(&fd->iomgr_object); - - grpc_lfev_destroy(&fd->read_closure); - grpc_lfev_destroy(&fd->write_closure); - - gpr_mu_unlock(&fd_freelist_mu); - } else { - GPR_ASSERT(old > n); - } -} - -/* Increment refcount by two to avoid changing the orphan bit */ -#ifndef NDEBUG -static void fd_ref(grpc_fd *fd, const char *reason, const char *file, - int line) { - ref_by(fd, 2, reason, file, line); -} - -static void fd_unref(grpc_fd *fd, const char *reason, const char *file, - int line) { - unref_by(fd, 2, reason, file, line); -} -#else -static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); } -static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); } -#endif - -static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } - -static void fd_global_shutdown(void) { - gpr_mu_lock(&fd_freelist_mu); - gpr_mu_unlock(&fd_freelist_mu); - while (fd_freelist != NULL) { - grpc_fd *fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - gpr_mu_destroy(&fd->po.mu); - gpr_free(fd); - } - gpr_mu_destroy(&fd_freelist_mu); -} - -static grpc_fd *fd_create(int fd, const char *name) { - grpc_fd *new_fd = NULL; - - gpr_mu_lock(&fd_freelist_mu); - if (fd_freelist != NULL) { - new_fd = fd_freelist; - fd_freelist = fd_freelist->freelist_next; - } - gpr_mu_unlock(&fd_freelist_mu); - - if (new_fd == NULL) { - new_fd = (grpc_fd *)gpr_malloc(sizeof(grpc_fd)); - gpr_mu_init(&new_fd->po.mu); - } - - /* Note: It is not really needed to get the new_fd->po.mu lock here. If this - * is a newly created fd (or an fd we got from the freelist), no one else - * would be holding a lock to it anyway. */ - gpr_mu_lock(&new_fd->po.mu); - new_fd->po.pi = NULL; -#ifndef NDEBUG - new_fd->po.obj_type = POLL_OBJ_FD; -#endif - - gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); - new_fd->fd = fd; - new_fd->orphaned = false; - grpc_lfev_init(&new_fd->read_closure); - grpc_lfev_init(&new_fd->write_closure); - gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); - - new_fd->freelist_next = NULL; - new_fd->on_done_closure = NULL; - - gpr_mu_unlock(&new_fd->po.mu); - - char *fd_name; - gpr_asprintf(&fd_name, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); - gpr_free(fd_name); - return new_fd; -} - -static int fd_wrapped_fd(grpc_fd *fd) { - int ret_fd = -1; - gpr_mu_lock(&fd->po.mu); - if (!fd->orphaned) { - ret_fd = fd->fd; - } - gpr_mu_unlock(&fd->po.mu); - - return ret_fd; -} - -static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *on_done, int *release_fd, - bool already_closed, const char *reason) { - grpc_error *error = GRPC_ERROR_NONE; - polling_island *unref_pi = NULL; - - gpr_mu_lock(&fd->po.mu); - fd->on_done_closure = on_done; - - /* Remove the active status but keep referenced. We want this grpc_fd struct - to be alive (and not added to freelist) until the end of this function */ - REF_BY(fd, 1, reason); - - /* Remove the fd from the polling island: - - Get a lock on the latest polling island (i.e the last island in the - linked list pointed by fd->po.pi). This is the island that - would actually contain the fd - - Remove the fd from the latest polling island - - Unlock the latest polling island - - Set fd->po.pi to NULL (but remove the ref on the polling island - before doing this.) */ - if (fd->po.pi != NULL) { - polling_island *pi_latest = polling_island_lock(fd->po.pi); - polling_island_remove_fd_locked(pi_latest, fd, already_closed, &error); - gpr_mu_unlock(&pi_latest->mu); - - unref_pi = fd->po.pi; - fd->po.pi = NULL; - } - - /* If release_fd is not NULL, we should be relinquishing control of the file - descriptor fd->fd (but we still own the grpc_fd structure). */ - if (release_fd != NULL) { - *release_fd = fd->fd; - } else { - close(fd->fd); - } - - fd->orphaned = true; - - GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error)); - - gpr_mu_unlock(&fd->po.mu); - UNREF_BY(fd, 2, reason); /* Drop the reference */ - if (unref_pi != NULL) { - /* Unref stale polling island here, outside the fd lock above. - The polling island owns a workqueue which owns an fd, and unreffing - inside the lock can cause an eventual lock loop that makes TSAN very - unhappy. */ - PI_UNREF(exec_ctx, unref_pi, "fd_orphan"); - } - if (error != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(error); - gpr_log(GPR_DEBUG, "fd_orphan: %s", msg); - } - GRPC_ERROR_UNREF(error); -} - -static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); - return (grpc_pollset *)notifier; -} - -static bool fd_is_shutdown(grpc_fd *fd) { - return grpc_lfev_is_shutdown(&fd->read_closure); -} - -/* Might be called multiple times */ -static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, - GRPC_ERROR_REF(why))) { - shutdown(fd->fd, SHUT_RDWR); - grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); - } - GRPC_ERROR_UNREF(why); -} - -static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); -} - -static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); -} - -/******************************************************************************* - * Pollset Definitions - */ -GPR_TLS_DECL(g_current_thread_pollset); -GPR_TLS_DECL(g_current_thread_worker); -static __thread bool g_initialized_sigmask; -static __thread sigset_t g_orig_sigmask; - -static void sig_handler(int sig_num) { -#ifdef GRPC_EPOLL_DEBUG - gpr_log(GPR_INFO, "Received signal %d", sig_num); -#endif -} - -static void poller_kick_init() { signal(grpc_wakeup_signal, sig_handler); } - -/* Global state management */ -static grpc_error *pollset_global_init(void) { - gpr_tls_init(&g_current_thread_pollset); - gpr_tls_init(&g_current_thread_worker); - poller_kick_init(); - return GRPC_ERROR_NONE; -} - -static void pollset_global_shutdown(void) { - gpr_tls_destroy(&g_current_thread_pollset); - gpr_tls_destroy(&g_current_thread_worker); -} - -static grpc_error *pollset_worker_kick(grpc_pollset_worker *worker) { - grpc_error *err = GRPC_ERROR_NONE; - - /* Kick the worker only if it was not already kicked */ - if (gpr_atm_no_barrier_cas(&worker->is_kicked, (gpr_atm)0, (gpr_atm)1)) { - GRPC_POLLING_TRACE( - "pollset_worker_kick: Kicking worker: %p (thread id: %ld)", - (void *)worker, (long int)worker->pt_id); - int err_num = pthread_kill(worker->pt_id, grpc_wakeup_signal); - if (err_num != 0) { - err = GRPC_OS_ERROR(err_num, "pthread_kill"); - } - } - return err; -} - -/* Return 1 if the pollset has active threads in pollset_work (pollset must - * be locked) */ -static int pollset_has_workers(grpc_pollset *p) { - return p->root_worker.next != &p->root_worker; -} - -static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev->next = worker->next; - worker->next->prev = worker->prev; -} - -static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { - if (pollset_has_workers(p)) { - grpc_pollset_worker *w = p->root_worker.next; - remove_worker(p, w); - return w; - } else { - return NULL; - } -} - -static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->next = &p->root_worker; - worker->prev = worker->next->prev; - worker->prev->next = worker->next->prev = worker; -} - -static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev = &p->root_worker; - worker->next = worker->prev->next; - worker->prev->next = worker->next->prev = worker; -} - -/* p->mu must be held before calling this function */ -static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *p, - grpc_pollset_worker *specific_worker) { - GPR_TIMER_BEGIN("pollset_kick", 0); - grpc_error *error = GRPC_ERROR_NONE; - GRPC_STATS_INC_POLLSET_KICK(exec_ctx); - const char *err_desc = "Kick Failure"; - grpc_pollset_worker *worker = specific_worker; - if (worker != NULL) { - if (worker == GRPC_POLLSET_KICK_BROADCAST) { - if (pollset_has_workers(p)) { - GPR_TIMER_BEGIN("pollset_kick.broadcast", 0); - for (worker = p->root_worker.next; worker != &p->root_worker; - worker = worker->next) { - if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { - append_error(&error, pollset_worker_kick(worker), err_desc); - } - } - GPR_TIMER_END("pollset_kick.broadcast", 0); - } else { - p->kicked_without_pollers = true; - } - } else { - GPR_TIMER_MARK("kicked_specifically", 0); - if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { - append_error(&error, pollset_worker_kick(worker), err_desc); - } - } - } else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) { - /* Since worker == NULL, it means that we can kick "any" worker on this - pollset 'p'. If 'p' happens to be the same pollset this thread is - currently polling (i.e in pollset_work() function), then there is no need - to kick any other worker since the current thread can just absorb the - kick. This is the reason why we enter this case only when - g_current_thread_pollset is != p */ - - GPR_TIMER_MARK("kick_anonymous", 0); - worker = pop_front_worker(p); - if (worker != NULL) { - GPR_TIMER_MARK("finally_kick", 0); - push_back_worker(p, worker); - append_error(&error, pollset_worker_kick(worker), err_desc); - } else { - GPR_TIMER_MARK("kicked_no_pollers", 0); - p->kicked_without_pollers = true; - } - } - - GPR_TIMER_END("pollset_kick", 0); - GRPC_LOG_IF_ERROR("pollset_kick", GRPC_ERROR_REF(error)); - return error; -} - -static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - gpr_mu_init(&pollset->po.mu); - *mu = &pollset->po.mu; - pollset->po.pi = NULL; -#ifndef NDEBUG - pollset->po.obj_type = POLL_OBJ_POLLSET; -#endif - - pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; - pollset->kicked_without_pollers = false; - - pollset->shutting_down = false; - pollset->finish_shutdown_called = false; - pollset->shutdown_done = NULL; -} - -/* Convert a timespec to milliseconds: - - Very small or negative poll times are clamped to zero to do a non-blocking - poll (which becomes spin polling) - - Other small values are rounded up to one millisecond - - Longer than a millisecond polls are rounded up to the next nearest - millisecond to avoid spinning - - Infinite timeouts are converted to -1 */ -static int poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - static const int64_t max_spin_polling_us = 10; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return -1; - } - - if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( - max_spin_polling_us, - GPR_TIMESPAN))) <= 0) { - return 0; - } - timeout = gpr_time_sub(deadline, now); - int millis = gpr_time_to_millis(gpr_time_add( - timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); - return millis >= 1 ? millis : 1; -} - -static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_pollset *notifier) { - grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); - - /* Note, it is possible that fd_become_readable might be called twice with - different 'notifier's when an fd becomes readable and it is in two epoll - sets (This can happen briefly during polling island merges). In such cases - it does not really matter which notifer is set as the read_notifier_pollset - (They would both point to the same polling island anyway) */ - /* Use release store to match with acquire load in fd_get_read_notifier */ - gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); -} - -static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); -} - -static void pollset_release_polling_island(grpc_exec_ctx *exec_ctx, - grpc_pollset *ps, - const char *reason) { - if (ps->po.pi != NULL) { - PI_UNREF(exec_ctx, ps->po.pi, reason); - } - ps->po.pi = NULL; -} - -static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - /* The pollset cannot have any workers if we are at this stage */ - GPR_ASSERT(!pollset_has_workers(pollset)); - - pollset->finish_shutdown_called = true; - - /* Release the ref and set pollset->po.pi to NULL */ - pollset_release_polling_island(exec_ctx, pollset, "ps_shutdown"); - GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE); -} - -/* pollset->po.mu lock must be held by the caller before calling this */ -static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_TIMER_BEGIN("pollset_shutdown", 0); - GPR_ASSERT(!pollset->shutting_down); - pollset->shutting_down = true; - pollset->shutdown_done = closure; - pollset_kick(exec_ctx, pollset, GRPC_POLLSET_KICK_BROADCAST); - - /* If the pollset has any workers, we cannot call finish_shutdown_locked() - because it would release the underlying polling island. In such a case, we - let the last worker call finish_shutdown_locked() from pollset_work() */ - if (!pollset_has_workers(pollset)) { - GPR_ASSERT(!pollset->finish_shutdown_called); - GPR_TIMER_MARK("pollset_shutdown.finish_shutdown_locked", 0); - finish_shutdown_locked(exec_ctx, pollset); - } - GPR_TIMER_END("pollset_shutdown", 0); -} - -/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other - * than destroying the mutexes, there is nothing special that needs to be done - * here */ -static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GPR_ASSERT(!pollset_has_workers(pollset)); - gpr_mu_destroy(&pollset->po.mu); -} - -#define GRPC_EPOLL_MAX_EVENTS 100 -/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */ -static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_pollset_worker *worker, int timeout_ms, - sigset_t *sig_mask, grpc_error **error) { - struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; - int epoll_fd = -1; - int ep_rv; - polling_island *pi = NULL; - char *err_msg; - const char *err_desc = "pollset_work_and_unlock"; - GPR_TIMER_BEGIN("pollset_work_and_unlock", 0); - - /* We need to get the epoll_fd to wait on. The epoll_fd is in inside the - latest polling island pointed by pollset->po.pi - - Since epoll_fd is immutable, we can read it without obtaining the polling - island lock. There is however a possibility that the polling island (from - which we got the epoll_fd) got merged with another island while we are - in this function. This is still okay because in such a case, we will wakeup - right-away from epoll_wait() and pick up the latest polling_island the next - this function (i.e pollset_work_and_unlock()) is called */ - - if (pollset->po.pi == NULL) { - pollset->po.pi = polling_island_create(exec_ctx, NULL, error); - if (pollset->po.pi == NULL) { - GPR_TIMER_END("pollset_work_and_unlock", 0); - return; /* Fatal error. We cannot continue */ - } - - PI_ADD_REF(pollset->po.pi, "ps"); - GRPC_POLLING_TRACE("pollset_work: pollset: %p created new pi: %p", - (void *)pollset, (void *)pollset->po.pi); - } - - pi = polling_island_maybe_get_latest(pollset->po.pi); - epoll_fd = pi->epoll_fd; - - /* Update the pollset->po.pi since the island being pointed by - pollset->po.pi maybe older than the one pointed by pi) */ - if (pollset->po.pi != pi) { - /* Always do PI_ADD_REF before PI_UNREF because PI_UNREF may cause the - polling island to be deleted */ - PI_ADD_REF(pi, "ps"); - PI_UNREF(exec_ctx, pollset->po.pi, "ps"); - pollset->po.pi = pi; - } - - /* Add an extra ref so that the island does not get destroyed (which means - the epoll_fd won't be closed) while we are are doing an epoll_wait() on the - epoll_fd */ - PI_ADD_REF(pi, "ps_work"); - gpr_mu_unlock(&pollset->po.mu); - - gpr_atm_no_barrier_fetch_add(&pi->poller_count, 1); - g_current_thread_polling_island = pi; - - GRPC_SCHEDULING_START_BLOCKING_REGION; - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - ep_rv = - epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms, sig_mask); - GRPC_SCHEDULING_END_BLOCKING_REGION; - if (ep_rv < 0) { - if (errno != EINTR) { - gpr_asprintf(&err_msg, - "epoll_wait() epoll fd: %d failed with error: %d (%s)", - epoll_fd, errno, strerror(errno)); - append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); - } else { - /* We were interrupted. Save an interation by doing a zero timeout - epoll_wait to see if there are any other events of interest */ - GRPC_POLLING_TRACE("pollset_work: pollset: %p, worker: %p received kick", - (void *)pollset, (void *)worker); - ep_rv = epoll_wait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0); - } - } - -#ifdef GRPC_TSAN - /* See the definition of g_poll_sync for more details */ - gpr_atm_acq_load(&g_epoll_sync); -#endif /* defined(GRPC_TSAN) */ - - for (int i = 0; i < ep_rv; ++i) { - void *data_ptr = ep_ev[i].data.ptr; - if (data_ptr == &polling_island_wakeup_fd) { - GRPC_POLLING_TRACE( - "pollset_work: pollset: %p, worker: %p polling island (epoll_fd: " - "%d) got merged", - (void *)pollset, (void *)worker, epoll_fd); - /* This means that our polling island is merged with a different - island. We do not have to do anything here since the subsequent call - to the function pollset_work_and_unlock() will pick up the correct - epoll_fd */ - } else { - grpc_fd *fd = (grpc_fd *)data_ptr; - int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); - int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI); - int write_ev = ep_ev[i].events & EPOLLOUT; - if (read_ev || cancel) { - fd_become_readable(exec_ctx, fd, pollset); - } - if (write_ev || cancel) { - fd_become_writable(exec_ctx, fd); - } - } - } - - g_current_thread_polling_island = NULL; - gpr_atm_no_barrier_fetch_add(&pi->poller_count, -1); - - GPR_ASSERT(pi != NULL); - - /* Before leaving, release the extra ref we added to the polling island. It - is important to use "pi" here (i.e our old copy of pollset->po.pi - that we got before releasing the polling island lock). This is because - pollset->po.pi pointer might get udpated in other parts of the - code when there is an island merge while we are doing epoll_wait() above */ - PI_UNREF(exec_ctx, pi, "ps_work"); - - GPR_TIMER_END("pollset_work_and_unlock", 0); -} - -/* pollset->po.mu lock must be held by the caller before calling this. - The function pollset_work() may temporarily release the lock (pollset->po.mu) - during the course of its execution but it will always re-acquire the lock and - ensure that it is held by the time the function returns */ -static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - GPR_TIMER_BEGIN("pollset_work", 0); - grpc_error *error = GRPC_ERROR_NONE; - int timeout_ms = poll_deadline_to_millis_timeout(deadline, now); - - sigset_t new_mask; - - grpc_pollset_worker worker; - worker.next = worker.prev = NULL; - worker.pt_id = pthread_self(); - gpr_atm_no_barrier_store(&worker.is_kicked, (gpr_atm)0); - - if (worker_hdl) *worker_hdl = &worker; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); - gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); - - if (pollset->kicked_without_pollers) { - /* If the pollset was kicked without pollers, pretend that the current - worker got the kick and skip polling. A kick indicates that there is some - work that needs attention like an event on the completion queue or an - alarm */ - GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); - pollset->kicked_without_pollers = 0; - } else if (!pollset->shutting_down) { - /* We use the posix-signal with number 'grpc_wakeup_signal' for waking up - (i.e 'kicking') a worker in the pollset. A 'kick' is a way to inform the - worker that there is some pending work that needs immediate attention - (like an event on the completion queue, or a polling island merge that - results in a new epoll-fd to wait on) and that the worker should not - spend time waiting in epoll_pwait(). - - A worker can be kicked anytime from the point it is added to the pollset - via push_front_worker() (or push_back_worker()) to the point it is - removed via remove_worker(). - If the worker is kicked before/during it calls epoll_pwait(), it should - immediately exit from epoll_wait(). If the worker is kicked after it - returns from epoll_wait(), then nothing really needs to be done. - - To accomplish this, we mask 'grpc_wakeup_signal' on this thread at all - times *except* when it is in epoll_pwait(). This way, the worker never - misses acting on a kick */ - - if (!g_initialized_sigmask) { - sigemptyset(&new_mask); - sigaddset(&new_mask, grpc_wakeup_signal); - pthread_sigmask(SIG_BLOCK, &new_mask, &g_orig_sigmask); - sigdelset(&g_orig_sigmask, grpc_wakeup_signal); - g_initialized_sigmask = true; - /* new_mask: The new thread mask which blocks 'grpc_wakeup_signal'. - This is the mask used at all times *except during - epoll_wait()*" - g_orig_sigmask: The thread mask which allows 'grpc_wakeup_signal' and - this is the mask to use *during epoll_wait()* - - The new_mask is set on the worker before it is added to the pollset - (i.e before it can be kicked) */ - } - - push_front_worker(pollset, &worker); /* Add worker to pollset */ - - pollset_work_and_unlock(exec_ctx, pollset, &worker, timeout_ms, - &g_orig_sigmask, &error); - grpc_exec_ctx_flush(exec_ctx); - - gpr_mu_lock(&pollset->po.mu); - - /* Note: There is no need to reset worker.is_kicked to 0 since we are no - longer going to use this worker */ - remove_worker(pollset, &worker); - } - - /* If we are the last worker on the pollset (i.e pollset_has_workers() is - false at this point) and the pollset is shutting down, we may have to - finish the shutdown process by calling finish_shutdown_locked(). - See pollset_shutdown() for more details. - - Note: Continuing to access pollset here is safe; it is the caller's - responsibility to not destroy a pollset when it has outstanding calls to - pollset_work() */ - if (pollset->shutting_down && !pollset_has_workers(pollset) && - !pollset->finish_shutdown_called) { - GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0); - finish_shutdown_locked(exec_ctx, pollset); - - gpr_mu_unlock(&pollset->po.mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->po.mu); - } - - if (worker_hdl) *worker_hdl = NULL; - - gpr_tls_set(&g_current_thread_pollset, (intptr_t)0); - gpr_tls_set(&g_current_thread_worker, (intptr_t)0); - - GPR_TIMER_END("pollset_work", 0); - - GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); - return error; -} - -static void add_poll_object(grpc_exec_ctx *exec_ctx, poll_obj *bag, - poll_obj_type bag_type, poll_obj *item, - poll_obj_type item_type) { - GPR_TIMER_BEGIN("add_poll_object", 0); - -#ifndef NDEBUG - GPR_ASSERT(item->obj_type == item_type); - GPR_ASSERT(bag->obj_type == bag_type); -#endif - - grpc_error *error = GRPC_ERROR_NONE; - polling_island *pi_new = NULL; - - gpr_mu_lock(&bag->mu); - gpr_mu_lock(&item->mu); - -retry: - /* - * 1) If item->pi and bag->pi are both non-NULL and equal, do nothing - * 2) If item->pi and bag->pi are both NULL, create a new polling island (with - * a refcount of 2) and point item->pi and bag->pi to the new island - * 3) If exactly one of item->pi or bag->pi is NULL, update it to point to - * the other's non-NULL pi - * 4) Finally if item->pi and bag-pi are non-NULL and not-equal, merge the - * polling islands and update item->pi and bag->pi to point to the new - * island - */ - - /* Early out if we are trying to add an 'fd' to a 'bag' but the fd is already - * orphaned */ - if (item_type == POLL_OBJ_FD && (FD_FROM_PO(item))->orphaned) { - gpr_mu_unlock(&item->mu); - gpr_mu_unlock(&bag->mu); - return; - } - - if (item->pi == bag->pi) { - pi_new = item->pi; - if (pi_new == NULL) { - /* GPR_ASSERT(item->pi == bag->pi == NULL) */ - - /* If we are adding an fd to a bag (i.e pollset or pollset_set), then - * we need to do some extra work to make TSAN happy */ - if (item_type == POLL_OBJ_FD) { - /* Unlock before creating a new polling island: the polling island will - create a workqueue which creates a file descriptor, and holding an fd - lock here can eventually cause a loop to appear to TSAN (making it - unhappy). We don't think it's a real loop (there's an epoch point - where that loop possibility disappears), but the advantages of - keeping TSAN happy outweigh any performance advantage we might have - by keeping the lock held. */ - gpr_mu_unlock(&item->mu); - pi_new = polling_island_create(exec_ctx, FD_FROM_PO(item), &error); - gpr_mu_lock(&item->mu); - - /* Need to reverify any assumptions made between the initial lock and - getting to this branch: if they've changed, we need to throw away our - work and figure things out again. */ - if (item->pi != NULL) { - GRPC_POLLING_TRACE( - "add_poll_object: Raced creating new polling island. pi_new: %p " - "(fd: %d, %s: %p)", - (void *)pi_new, FD_FROM_PO(item)->fd, poll_obj_string(bag_type), - (void *)bag); - /* No need to lock 'pi_new' here since this is a new polling island - and no one has a reference to it yet */ - polling_island_remove_all_fds_locked(pi_new, true, &error); - - /* Ref and unref so that the polling island gets deleted during unref - */ - PI_ADD_REF(pi_new, "dance_of_destruction"); - PI_UNREF(exec_ctx, pi_new, "dance_of_destruction"); - goto retry; - } - } else { - pi_new = polling_island_create(exec_ctx, NULL, &error); - } - - GRPC_POLLING_TRACE( - "add_poll_object: Created new polling island. pi_new: %p (%s: %p, " - "%s: %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } else { - GRPC_POLLING_TRACE( - "add_poll_object: Same polling island. pi: %p (%s, %s)", - (void *)pi_new, poll_obj_string(item_type), - poll_obj_string(bag_type)); - } - } else if (item->pi == NULL) { - /* GPR_ASSERT(bag->pi != NULL) */ - /* Make pi_new point to latest pi*/ - pi_new = polling_island_lock(bag->pi); - - if (item_type == POLL_OBJ_FD) { - grpc_fd *fd = FD_FROM_PO(item); - polling_island_add_fds_locked(pi_new, &fd, 1, true, &error); - } - - gpr_mu_unlock(&pi_new->mu); - GRPC_POLLING_TRACE( - "add_poll_obj: item->pi was NULL. pi_new: %p (item(%s): %p, " - "bag(%s): %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } else if (bag->pi == NULL) { - /* GPR_ASSERT(item->pi != NULL) */ - /* Make pi_new to point to latest pi */ - pi_new = polling_island_lock(item->pi); - gpr_mu_unlock(&pi_new->mu); - GRPC_POLLING_TRACE( - "add_poll_obj: bag->pi was NULL. pi_new: %p (item(%s): %p, " - "bag(%s): %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } else { - pi_new = polling_island_merge(item->pi, bag->pi, &error); - GRPC_POLLING_TRACE( - "add_poll_obj: polling islands merged. pi_new: %p (item(%s): %p, " - "bag(%s): %p)", - (void *)pi_new, poll_obj_string(item_type), (void *)item, - poll_obj_string(bag_type), (void *)bag); - } - - /* At this point, pi_new is the polling island that both item->pi and bag->pi - MUST be pointing to */ - - if (item->pi != pi_new) { - PI_ADD_REF(pi_new, poll_obj_string(item_type)); - if (item->pi != NULL) { - PI_UNREF(exec_ctx, item->pi, poll_obj_string(item_type)); - } - item->pi = pi_new; - } - - if (bag->pi != pi_new) { - PI_ADD_REF(pi_new, poll_obj_string(bag_type)); - if (bag->pi != NULL) { - PI_UNREF(exec_ctx, bag->pi, poll_obj_string(bag_type)); - } - bag->pi = pi_new; - } - - gpr_mu_unlock(&item->mu); - gpr_mu_unlock(&bag->mu); - - GRPC_LOG_IF_ERROR("add_poll_object", error); - GPR_TIMER_END("add_poll_object", 0); -} - -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - add_poll_object(exec_ctx, &pollset->po, POLL_OBJ_POLLSET, &fd->po, - POLL_OBJ_FD); -} - -/******************************************************************************* - * Pollset-set Definitions - */ - -static grpc_pollset_set *pollset_set_create(void) { - grpc_pollset_set *pss = (grpc_pollset_set *)gpr_malloc(sizeof(*pss)); - gpr_mu_init(&pss->po.mu); - pss->po.pi = NULL; -#ifndef NDEBUG - pss->po.obj_type = POLL_OBJ_POLLSET_SET; -#endif - return pss; -} - -static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss) { - gpr_mu_destroy(&pss->po.mu); - - if (pss->po.pi != NULL) { - PI_UNREF(exec_ctx, pss->po.pi, "pss_destroy"); - } - - gpr_free(pss); -} - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &fd->po, - POLL_OBJ_FD); -} - -static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, - grpc_fd *fd) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &ps->po, - POLL_OBJ_POLLSET); -} - -static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pss, grpc_pollset *ps) { - /* Nothing to do */ -} - -static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - add_poll_object(exec_ctx, &bag->po, POLL_OBJ_POLLSET_SET, &item->po, - POLL_OBJ_POLLSET_SET); -} - -static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - /* Nothing to do */ -} - -/* Test helper functions - * */ -void *grpc_fd_get_polling_island(grpc_fd *fd) { - polling_island *pi; - - gpr_mu_lock(&fd->po.mu); - pi = fd->po.pi; - gpr_mu_unlock(&fd->po.mu); - - return pi; -} - -void *grpc_pollset_get_polling_island(grpc_pollset *ps) { - polling_island *pi; - - gpr_mu_lock(&ps->po.mu); - pi = ps->po.pi; - gpr_mu_unlock(&ps->po.mu); - - return pi; -} - -bool grpc_are_polling_islands_equal(void *p, void *q) { - polling_island *p1 = (polling_island *)p; - polling_island *p2 = (polling_island *)q; - - /* Note: polling_island_lock_pair() may change p1 and p2 to point to the - latest polling islands in their respective linked lists */ - polling_island_lock_pair(&p1, &p2); - polling_island_unlock_pair(p1, p2); - - return p1 == p2; -} - -/******************************************************************************* - * Event engine binding - */ - -static void shutdown_engine(void) { - fd_global_shutdown(); - pollset_global_shutdown(); - polling_island_global_shutdown(); -} - -static const grpc_event_engine_vtable vtable = { - sizeof(grpc_pollset), - - fd_create, - fd_wrapped_fd, - fd_orphan, - fd_shutdown, - fd_notify_on_read, - fd_notify_on_write, - fd_is_shutdown, - fd_get_read_notifier_pollset, - - pollset_init, - pollset_shutdown, - pollset_destroy, - pollset_work, - pollset_kick, - pollset_add_fd, - - pollset_set_create, - pollset_set_destroy, - pollset_set_add_pollset, - pollset_set_del_pollset, - pollset_set_add_pollset_set, - pollset_set_del_pollset_set, - pollset_set_add_fd, - pollset_set_del_fd, - - shutdown_engine, -}; - -/* It is possible that GLIBC has epoll but the underlying kernel doesn't. - * Create a dummy epoll_fd to make sure epoll support is available */ -static bool is_epoll_available() { - int fd = epoll_create1(EPOLL_CLOEXEC); - if (fd < 0) { - gpr_log( - GPR_ERROR, - "epoll_create1 failed with error: %d. Not using epoll polling engine", - fd); - return false; - } - close(fd); - return true; -} - -const grpc_event_engine_vtable *grpc_init_epollsig_linux( - bool explicit_request) { - /* If use of signals is disabled, we cannot use epoll engine*/ - if (is_grpc_wakeup_signal_initialized && grpc_wakeup_signal < 0) { - return NULL; - } - - if (!grpc_has_wakeup_fd()) { - return NULL; - } - - if (!is_epoll_available()) { - return NULL; - } - - if (!is_grpc_wakeup_signal_initialized) { - if (explicit_request) { - grpc_use_signal(SIGRTMIN + 6); - } else { - return NULL; - } - } - - fd_global_init(); - - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - return NULL; - } - - if (!GRPC_LOG_IF_ERROR("polling_island_global_init", - polling_island_global_init())) { - return NULL; - } - - return &vtable; -} - -#else /* defined(GRPC_LINUX_EPOLL) */ -#if defined(GRPC_POSIX_SOCKET) -#include "src/core/lib/iomgr/ev_posix.h" -/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return - * NULL */ -const grpc_event_engine_vtable *grpc_init_epollsig_linux( - bool explicit_request) { - return NULL; -} -#endif /* defined(GRPC_POSIX_SOCKET) */ - -void grpc_use_signal(int signum) {} -#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_epollsig_linux.cc b/src/core/lib/iomgr/ev_epollsig_linux.cc new file mode 100644 index 0000000000..4d8bdf1401 --- /dev/null +++ b/src/core/lib/iomgr/ev_epollsig_linux.cc @@ -0,0 +1,1769 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +/* This polling engine is only relevant on linux kernels supporting epoll() */ +#ifdef GRPC_LINUX_EPOLL + +#include "src/core/lib/iomgr/ev_epollsig_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/lockfree_event.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/block_annotate.h" + +#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) + +#define GRPC_POLLING_TRACE(...) \ + if (GRPC_TRACER_ON(grpc_polling_trace)) { \ + gpr_log(GPR_INFO, __VA_ARGS__); \ + } + +static int grpc_wakeup_signal = -1; +static bool is_grpc_wakeup_signal_initialized = false; + +/* Implements the function defined in grpc_posix.h. This function might be + * called before even calling grpc_init() to set either a different signal to + * use. If signum == -1, then the use of signals is disabled */ +void grpc_use_signal(int signum) { + grpc_wakeup_signal = signum; + is_grpc_wakeup_signal_initialized = true; + + if (grpc_wakeup_signal < 0) { + gpr_log(GPR_INFO, + "Use of signals is disabled. Epoll engine will not be used"); + } else { + gpr_log(GPR_INFO, "epoll engine will be using signal: %d", + grpc_wakeup_signal); + } +} + +struct polling_island; + +typedef enum { + POLL_OBJ_FD, + POLL_OBJ_POLLSET, + POLL_OBJ_POLLSET_SET +} poll_obj_type; + +typedef struct poll_obj { +#ifndef NDEBUG + poll_obj_type obj_type; +#endif + gpr_mu mu; + struct polling_island *pi; +} poll_obj; + +const char *poll_obj_string(poll_obj_type po_type) { + switch (po_type) { + case POLL_OBJ_FD: + return "fd"; + case POLL_OBJ_POLLSET: + return "pollset"; + case POLL_OBJ_POLLSET_SET: + return "pollset_set"; + } + + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +/******************************************************************************* + * Fd Declarations + */ + +#define FD_FROM_PO(po) ((grpc_fd *)(po)) + +struct grpc_fd { + poll_obj po; + + int fd; + /* refst format: + bit 0 : 1=Active / 0=Orphaned + bits 1-n : refcount + Ref/Unref by two to avoid altering the orphaned bit */ + gpr_atm refst; + + /* The fd is either closed or we relinquished control of it. In either + cases, this indicates that the 'fd' on this structure is no longer + valid */ + bool orphaned; + + gpr_atm read_closure; + gpr_atm write_closure; + + struct grpc_fd *freelist_next; + grpc_closure *on_done_closure; + + /* The pollset that last noticed that the fd is readable. The actual type + * stored in this is (grpc_pollset *) */ + gpr_atm read_notifier_pollset; + + grpc_iomgr_object iomgr_object; +}; + +/* Reference counting for fds */ +#ifndef NDEBUG +static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); +static void fd_unref(grpc_fd *fd, const char *reason, const char *file, + int line); +#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__) +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__) +#else +static void fd_ref(grpc_fd *fd); +static void fd_unref(grpc_fd *fd); +#define GRPC_FD_REF(fd, reason) fd_ref(fd) +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd) +#endif + +static void fd_global_init(void); +static void fd_global_shutdown(void); + +/******************************************************************************* + * Polling island Declarations + */ + +#ifndef NDEBUG + +#define PI_ADD_REF(p, r) pi_add_ref_dbg((p), (r), __FILE__, __LINE__) +#define PI_UNREF(exec_ctx, p, r) \ + pi_unref_dbg((exec_ctx), (p), (r), __FILE__, __LINE__) + +#else + +#define PI_ADD_REF(p, r) pi_add_ref((p)) +#define PI_UNREF(exec_ctx, p, r) pi_unref((exec_ctx), (p)) + +#endif + +/* This is also used as grpc_workqueue (by directly casing it) */ +typedef struct polling_island { + gpr_mu mu; + /* Ref count. Use PI_ADD_REF() and PI_UNREF() macros to increment/decrement + the refcount. + Once the ref count becomes zero, this structure is destroyed which means + we should ensure that there is never a scenario where a PI_ADD_REF() is + racing with a PI_UNREF() that just made the ref_count zero. */ + gpr_atm ref_count; + + /* Pointer to the polling_island this merged into. + * merged_to value is only set once in polling_island's lifetime (and that too + * only if the island is merged with another island). Because of this, we can + * use gpr_atm type here so that we can do atomic access on this and reduce + * lock contention on 'mu' mutex. + * + * Note that if this field is not NULL (i.e not 0), all the remaining fields + * (except mu and ref_count) are invalid and must be ignored. */ + gpr_atm merged_to; + + /* Number of threads currently polling on this island */ + gpr_atm poller_count; + + /* The fd of the underlying epoll set */ + int epoll_fd; + + /* The file descriptors in the epoll set */ + size_t fd_cnt; + size_t fd_capacity; + grpc_fd **fds; +} polling_island; + +/******************************************************************************* + * Pollset Declarations + */ +struct grpc_pollset_worker { + /* Thread id of this worker */ + pthread_t pt_id; + + /* Used to prevent a worker from getting kicked multiple times */ + gpr_atm is_kicked; + struct grpc_pollset_worker *next; + struct grpc_pollset_worker *prev; +}; + +struct grpc_pollset { + poll_obj po; + + grpc_pollset_worker root_worker; + bool kicked_without_pollers; + + bool shutting_down; /* Is the pollset shutting down ? */ + bool finish_shutdown_called; /* Is the 'finish_shutdown_locked()' called ? */ + grpc_closure *shutdown_done; /* Called after after shutdown is complete */ +}; + +/******************************************************************************* + * Pollset-set Declarations + */ +struct grpc_pollset_set { + poll_obj po; +}; + +/******************************************************************************* + * Common helpers + */ + +static bool append_error(grpc_error **composite, grpc_error *error, + const char *desc) { + if (error == GRPC_ERROR_NONE) return true; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(desc); + } + *composite = grpc_error_add_child(*composite, error); + return false; +} + +/******************************************************************************* + * Polling island Definitions + */ + +/* The wakeup fd that is used to wake up all threads in a Polling island. This + is useful in the polling island merge operation where we need to wakeup all + the threads currently polling the smaller polling island (so that they can + start polling the new/merged polling island) + + NOTE: This fd is initialized to be readable and MUST NOT be consumed i.e the + threads that woke up MUST NOT call grpc_wakeup_fd_consume_wakeup() */ +static grpc_wakeup_fd polling_island_wakeup_fd; + +/* The polling island being polled right now. + See comments in workqueue_maybe_wakeup for why this is tracked. */ +static __thread polling_island *g_current_thread_polling_island; + +/* Forward declaration */ +static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi); + +#ifdef GRPC_TSAN +/* Currently TSAN may incorrectly flag data races between epoll_ctl and + epoll_wait for any grpc_fd structs that are added to the epoll set via + epoll_ctl and are returned (within a very short window) via epoll_wait(). + + To work-around this race, we establish a happens-before relation between + the code just-before epoll_ctl() and the code after epoll_wait() by using + this atomic */ +gpr_atm g_epoll_sync; +#endif /* defined(GRPC_TSAN) */ + +static void pi_add_ref(polling_island *pi); +static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi); + +#ifndef NDEBUG +static void pi_add_ref_dbg(polling_island *pi, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); + gpr_log(GPR_DEBUG, "Add ref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR + " (%s) - (%s, %d)", + pi, old_cnt, old_cnt + 1, reason, file, line); + } + pi_add_ref(pi); +} + +static void pi_unref_dbg(grpc_exec_ctx *exec_ctx, polling_island *pi, + const char *reason, const char *file, int line) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_atm old_cnt = gpr_atm_acq_load(&pi->ref_count); + gpr_log(GPR_DEBUG, "Unref pi: %p, old:%" PRIdPTR " -> new:%" PRIdPTR + " (%s) - (%s, %d)", + pi, old_cnt, (old_cnt - 1), reason, file, line); + } + pi_unref(exec_ctx, pi); +} +#endif + +static void pi_add_ref(polling_island *pi) { + gpr_atm_no_barrier_fetch_add(&pi->ref_count, 1); +} + +static void pi_unref(grpc_exec_ctx *exec_ctx, polling_island *pi) { + /* If ref count went to zero, delete the polling island. + Note that this deletion not be done under a lock. Once the ref count goes + to zero, we are guaranteed that no one else holds a reference to the + polling island (and that there is no racing pi_add_ref() call either). + + Also, if we are deleting the polling island and the merged_to field is + non-empty, we should remove a ref to the merged_to polling island + */ + if (1 == gpr_atm_full_fetch_add(&pi->ref_count, -1)) { + polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); + polling_island_delete(exec_ctx, pi); + if (next != NULL) { + PI_UNREF(exec_ctx, next, "pi_delete"); /* Recursive call */ + } + } +} + +/* The caller is expected to hold pi->mu lock before calling this function */ +static void polling_island_add_fds_locked(polling_island *pi, grpc_fd **fds, + size_t fd_count, bool add_fd_refs, + grpc_error **error) { + int err; + size_t i; + struct epoll_event ev; + char *err_msg; + const char *err_desc = "polling_island_add_fds"; + +#ifdef GRPC_TSAN + /* See the definition of g_epoll_sync for more context */ + gpr_atm_rel_store(&g_epoll_sync, (gpr_atm)0); +#endif /* defined(GRPC_TSAN) */ + + for (i = 0; i < fd_count; i++) { + ev.events = (uint32_t)(EPOLLIN | EPOLLOUT | EPOLLET); + ev.data.ptr = fds[i]; + err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, fds[i]->fd, &ev); + + if (err < 0) { + if (errno != EEXIST) { + gpr_asprintf( + &err_msg, + "epoll_ctl (epoll_fd: %d) add fd: %d failed with error: %d (%s)", + pi->epoll_fd, fds[i]->fd, errno, strerror(errno)); + append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); + gpr_free(err_msg); + } + + continue; + } + + if (pi->fd_cnt == pi->fd_capacity) { + pi->fd_capacity = GPR_MAX(pi->fd_capacity + 8, pi->fd_cnt * 3 / 2); + pi->fds = + (grpc_fd **)gpr_realloc(pi->fds, sizeof(grpc_fd *) * pi->fd_capacity); + } + + pi->fds[pi->fd_cnt++] = fds[i]; + if (add_fd_refs) { + GRPC_FD_REF(fds[i], "polling_island"); + } + } +} + +/* The caller is expected to hold pi->mu before calling this */ +static void polling_island_add_wakeup_fd_locked(polling_island *pi, + grpc_wakeup_fd *wakeup_fd, + grpc_error **error) { + struct epoll_event ev; + int err; + char *err_msg; + const char *err_desc = "polling_island_add_wakeup_fd"; + + ev.events = (uint32_t)(EPOLLIN | EPOLLET); + ev.data.ptr = wakeup_fd; + err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_ADD, + GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), &ev); + if (err < 0 && errno != EEXIST) { + gpr_asprintf(&err_msg, + "epoll_ctl (epoll_fd: %d) add wakeup fd: %d failed with " + "error: %d (%s)", + pi->epoll_fd, GRPC_WAKEUP_FD_GET_READ_FD(wakeup_fd), errno, + strerror(errno)); + append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); + gpr_free(err_msg); + } +} + +/* The caller is expected to hold pi->mu lock before calling this function */ +static void polling_island_remove_all_fds_locked(polling_island *pi, + bool remove_fd_refs, + grpc_error **error) { + int err; + size_t i; + char *err_msg; + const char *err_desc = "polling_island_remove_fds"; + + for (i = 0; i < pi->fd_cnt; i++) { + err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, pi->fds[i]->fd, NULL); + if (err < 0 && errno != ENOENT) { + gpr_asprintf(&err_msg, + "epoll_ctl (epoll_fd: %d) delete fds[%zu]: %d failed with " + "error: %d (%s)", + pi->epoll_fd, i, pi->fds[i]->fd, errno, strerror(errno)); + append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); + gpr_free(err_msg); + } + + if (remove_fd_refs) { + GRPC_FD_UNREF(pi->fds[i], "polling_island"); + } + } + + pi->fd_cnt = 0; +} + +/* The caller is expected to hold pi->mu lock before calling this function */ +static void polling_island_remove_fd_locked(polling_island *pi, grpc_fd *fd, + bool is_fd_closed, + grpc_error **error) { + int err; + size_t i; + char *err_msg; + const char *err_desc = "polling_island_remove_fd"; + + /* If fd is already closed, then it would have been automatically been removed + from the epoll set */ + if (!is_fd_closed) { + err = epoll_ctl(pi->epoll_fd, EPOLL_CTL_DEL, fd->fd, NULL); + if (err < 0 && errno != ENOENT) { + gpr_asprintf( + &err_msg, + "epoll_ctl (epoll_fd: %d) del fd: %d failed with error: %d (%s)", + pi->epoll_fd, fd->fd, errno, strerror(errno)); + append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); + gpr_free(err_msg); + } + } + + for (i = 0; i < pi->fd_cnt; i++) { + if (pi->fds[i] == fd) { + pi->fds[i] = pi->fds[--pi->fd_cnt]; + GRPC_FD_UNREF(fd, "polling_island"); + break; + } + } +} + +/* Might return NULL in case of an error */ +static polling_island *polling_island_create(grpc_exec_ctx *exec_ctx, + grpc_fd *initial_fd, + grpc_error **error) { + polling_island *pi = NULL; + const char *err_desc = "polling_island_create"; + + *error = GRPC_ERROR_NONE; + + pi = (polling_island *)gpr_malloc(sizeof(*pi)); + gpr_mu_init(&pi->mu); + pi->fd_cnt = 0; + pi->fd_capacity = 0; + pi->fds = NULL; + pi->epoll_fd = -1; + + gpr_atm_rel_store(&pi->ref_count, 0); + gpr_atm_rel_store(&pi->poller_count, 0); + gpr_atm_rel_store(&pi->merged_to, (gpr_atm)NULL); + + pi->epoll_fd = epoll_create1(EPOLL_CLOEXEC); + + if (pi->epoll_fd < 0) { + append_error(error, GRPC_OS_ERROR(errno, "epoll_create1"), err_desc); + goto done; + } + + if (initial_fd != NULL) { + polling_island_add_fds_locked(pi, &initial_fd, 1, true, error); + } + +done: + if (*error != GRPC_ERROR_NONE) { + polling_island_delete(exec_ctx, pi); + pi = NULL; + } + return pi; +} + +static void polling_island_delete(grpc_exec_ctx *exec_ctx, polling_island *pi) { + GPR_ASSERT(pi->fd_cnt == 0); + + if (pi->epoll_fd >= 0) { + close(pi->epoll_fd); + } + gpr_mu_destroy(&pi->mu); + gpr_free(pi->fds); + gpr_free(pi); +} + +/* Attempts to gets the last polling island in the linked list (liked by the + * 'merged_to' field). Since this does not lock the polling island, there are no + * guarantees that the island returned is the last island */ +static polling_island *polling_island_maybe_get_latest(polling_island *pi) { + polling_island *next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); + while (next != NULL) { + pi = next; + next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); + } + + return pi; +} + +/* Gets the lock on the *latest* polling island i.e the last polling island in + the linked list (linked by the 'merged_to' field). Call gpr_mu_unlock on the + returned polling island's mu. + Usage: To lock/unlock polling island "pi", do the following: + polling_island *pi_latest = polling_island_lock(pi); + ... + ... critical section .. + ... + gpr_mu_unlock(&pi_latest->mu); // NOTE: use pi_latest->mu. NOT pi->mu */ +static polling_island *polling_island_lock(polling_island *pi) { + polling_island *next = NULL; + + while (true) { + next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); + if (next == NULL) { + /* Looks like 'pi' is the last node in the linked list but unless we check + this by holding the pi->mu lock, we cannot be sure (i.e without the + pi->mu lock, we don't prevent island merges). + To be absolutely sure, check once more by holding the pi->mu lock */ + gpr_mu_lock(&pi->mu); + next = (polling_island *)gpr_atm_acq_load(&pi->merged_to); + if (next == NULL) { + /* pi is infact the last node and we have the pi->mu lock. we're done */ + break; + } + + /* pi->merged_to is not NULL i.e pi isn't the last node anymore. pi->mu + * isn't the lock we are interested in. Continue traversing the list */ + gpr_mu_unlock(&pi->mu); + } + + pi = next; + } + + return pi; +} + +/* Gets the lock on the *latest* polling islands in the linked lists pointed by + *p and *q (and also updates *p and *q to point to the latest polling islands) + + This function is needed because calling the following block of code to obtain + locks on polling islands (*p and *q) is prone to deadlocks. + { + polling_island_lock(*p, true); + polling_island_lock(*q, true); + } + + Usage/example: + polling_island *p1; + polling_island *p2; + .. + polling_island_lock_pair(&p1, &p2); + .. + .. Critical section with both p1 and p2 locked + .. + // Release locks: Always call polling_island_unlock_pair() to release locks + polling_island_unlock_pair(p1, p2); +*/ +static void polling_island_lock_pair(polling_island **p, polling_island **q) { + polling_island *pi_1 = *p; + polling_island *pi_2 = *q; + polling_island *next_1 = NULL; + polling_island *next_2 = NULL; + + /* The algorithm is simple: + - Go to the last polling islands in the linked lists *pi_1 and *pi_2 (and + keep updating pi_1 and pi_2) + - Then obtain locks on the islands by following a lock order rule of + locking polling_island with lower address first + Special case: Before obtaining the locks, check if pi_1 and pi_2 are + pointing to the same island. If that is the case, we can just call + polling_island_lock() + - After obtaining both the locks, double check that the polling islands + are still the last polling islands in their respective linked lists + (this is because there might have been polling island merges before + we got the lock) + - If the polling islands are the last islands, we are done. If not, + release the locks and continue the process from the first step */ + while (true) { + next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); + while (next_1 != NULL) { + pi_1 = next_1; + next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); + } + + next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); + while (next_2 != NULL) { + pi_2 = next_2; + next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); + } + + if (pi_1 == pi_2) { + pi_1 = pi_2 = polling_island_lock(pi_1); + break; + } + + if (pi_1 < pi_2) { + gpr_mu_lock(&pi_1->mu); + gpr_mu_lock(&pi_2->mu); + } else { + gpr_mu_lock(&pi_2->mu); + gpr_mu_lock(&pi_1->mu); + } + + next_1 = (polling_island *)gpr_atm_acq_load(&pi_1->merged_to); + next_2 = (polling_island *)gpr_atm_acq_load(&pi_2->merged_to); + if (next_1 == NULL && next_2 == NULL) { + break; + } + + gpr_mu_unlock(&pi_1->mu); + gpr_mu_unlock(&pi_2->mu); + } + + *p = pi_1; + *q = pi_2; +} + +static void polling_island_unlock_pair(polling_island *p, polling_island *q) { + if (p == q) { + gpr_mu_unlock(&p->mu); + } else { + gpr_mu_unlock(&p->mu); + gpr_mu_unlock(&q->mu); + } +} + +static polling_island *polling_island_merge(polling_island *p, + polling_island *q, + grpc_error **error) { + /* Get locks on both the polling islands */ + polling_island_lock_pair(&p, &q); + + if (p != q) { + /* Make sure that p points to the polling island with fewer fds than q */ + if (p->fd_cnt > q->fd_cnt) { + GPR_SWAP(polling_island *, p, q); + } + + /* Merge p with q i.e move all the fds from p (The one with fewer fds) to q + Note that the refcounts on the fds being moved will not change here. + This is why the last param in the following two functions is 'false') */ + polling_island_add_fds_locked(q, p->fds, p->fd_cnt, false, error); + polling_island_remove_all_fds_locked(p, false, error); + + /* Wakeup all the pollers (if any) on p so that they pickup this change */ + polling_island_add_wakeup_fd_locked(p, &polling_island_wakeup_fd, error); + + /* Add the 'merged_to' link from p --> q */ + gpr_atm_rel_store(&p->merged_to, (gpr_atm)q); + PI_ADD_REF(q, "pi_merge"); /* To account for the new incoming ref from p */ + } + /* else if p == q, nothing needs to be done */ + + polling_island_unlock_pair(p, q); + + /* Return the merged polling island (Note that no merge would have happened + if p == q which is ok) */ + return q; +} + +static grpc_error *polling_island_global_init() { + grpc_error *error = GRPC_ERROR_NONE; + + error = grpc_wakeup_fd_init(&polling_island_wakeup_fd); + if (error == GRPC_ERROR_NONE) { + error = grpc_wakeup_fd_wakeup(&polling_island_wakeup_fd); + } + + return error; +} + +static void polling_island_global_shutdown() { + grpc_wakeup_fd_destroy(&polling_island_wakeup_fd); +} + +/******************************************************************************* + * Fd Definitions + */ + +/* We need to keep a freelist not because of any concerns of malloc performance + * but instead so that implementations with multiple threads in (for example) + * epoll_wait deal with the race between pollset removal and incoming poll + * notifications. + * + * The problem is that the poller ultimately holds a reference to this + * object, so it is very difficult to know when is safe to free it, at least + * without some expensive synchronization. + * + * If we keep the object freelisted, in the worst case losing this race just + * becomes a spurious read notification on a reused fd. + */ + +/* The alarm system needs to be able to wakeup 'some poller' sometimes + * (specifically when a new alarm needs to be triggered earlier than the next + * alarm 'epoch'). This wakeup_fd gives us something to alert on when such a + * case occurs. */ + +static grpc_fd *fd_freelist = NULL; +static gpr_mu fd_freelist_mu; + +#ifndef NDEBUG +#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) +#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__) +static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, + "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", + fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); + } +#else +#define REF_BY(fd, n, reason) ref_by(fd, n) +#define UNREF_BY(fd, n, reason) unref_by(fd, n) +static void ref_by(grpc_fd *fd, int n) { +#endif + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); +} + +#ifndef NDEBUG +static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, + "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", + fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); + } +#else +static void unref_by(grpc_fd *fd, int n) { +#endif + gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { + /* Add the fd to the freelist */ + gpr_mu_lock(&fd_freelist_mu); + fd->freelist_next = fd_freelist; + fd_freelist = fd; + grpc_iomgr_unregister_object(&fd->iomgr_object); + + grpc_lfev_destroy(&fd->read_closure); + grpc_lfev_destroy(&fd->write_closure); + + gpr_mu_unlock(&fd_freelist_mu); + } else { + GPR_ASSERT(old > n); + } +} + +/* Increment refcount by two to avoid changing the orphan bit */ +#ifndef NDEBUG +static void fd_ref(grpc_fd *fd, const char *reason, const char *file, + int line) { + ref_by(fd, 2, reason, file, line); +} + +static void fd_unref(grpc_fd *fd, const char *reason, const char *file, + int line) { + unref_by(fd, 2, reason, file, line); +} +#else +static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); } +static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); } +#endif + +static void fd_global_init(void) { gpr_mu_init(&fd_freelist_mu); } + +static void fd_global_shutdown(void) { + gpr_mu_lock(&fd_freelist_mu); + gpr_mu_unlock(&fd_freelist_mu); + while (fd_freelist != NULL) { + grpc_fd *fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + gpr_mu_destroy(&fd->po.mu); + gpr_free(fd); + } + gpr_mu_destroy(&fd_freelist_mu); +} + +static grpc_fd *fd_create(int fd, const char *name) { + grpc_fd *new_fd = NULL; + + gpr_mu_lock(&fd_freelist_mu); + if (fd_freelist != NULL) { + new_fd = fd_freelist; + fd_freelist = fd_freelist->freelist_next; + } + gpr_mu_unlock(&fd_freelist_mu); + + if (new_fd == NULL) { + new_fd = (grpc_fd *)gpr_malloc(sizeof(grpc_fd)); + gpr_mu_init(&new_fd->po.mu); + } + + /* Note: It is not really needed to get the new_fd->po.mu lock here. If this + * is a newly created fd (or an fd we got from the freelist), no one else + * would be holding a lock to it anyway. */ + gpr_mu_lock(&new_fd->po.mu); + new_fd->po.pi = NULL; +#ifndef NDEBUG + new_fd->po.obj_type = POLL_OBJ_FD; +#endif + + gpr_atm_rel_store(&new_fd->refst, (gpr_atm)1); + new_fd->fd = fd; + new_fd->orphaned = false; + grpc_lfev_init(&new_fd->read_closure); + grpc_lfev_init(&new_fd->write_closure); + gpr_atm_no_barrier_store(&new_fd->read_notifier_pollset, (gpr_atm)NULL); + + new_fd->freelist_next = NULL; + new_fd->on_done_closure = NULL; + + gpr_mu_unlock(&new_fd->po.mu); + + char *fd_name; + gpr_asprintf(&fd_name, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&new_fd->iomgr_object, fd_name); + gpr_free(fd_name); + return new_fd; +} + +static int fd_wrapped_fd(grpc_fd *fd) { + int ret_fd = -1; + gpr_mu_lock(&fd->po.mu); + if (!fd->orphaned) { + ret_fd = fd->fd; + } + gpr_mu_unlock(&fd->po.mu); + + return ret_fd; +} + +static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *on_done, int *release_fd, + bool already_closed, const char *reason) { + grpc_error *error = GRPC_ERROR_NONE; + polling_island *unref_pi = NULL; + + gpr_mu_lock(&fd->po.mu); + fd->on_done_closure = on_done; + + /* Remove the active status but keep referenced. We want this grpc_fd struct + to be alive (and not added to freelist) until the end of this function */ + REF_BY(fd, 1, reason); + + /* Remove the fd from the polling island: + - Get a lock on the latest polling island (i.e the last island in the + linked list pointed by fd->po.pi). This is the island that + would actually contain the fd + - Remove the fd from the latest polling island + - Unlock the latest polling island + - Set fd->po.pi to NULL (but remove the ref on the polling island + before doing this.) */ + if (fd->po.pi != NULL) { + polling_island *pi_latest = polling_island_lock(fd->po.pi); + polling_island_remove_fd_locked(pi_latest, fd, already_closed, &error); + gpr_mu_unlock(&pi_latest->mu); + + unref_pi = fd->po.pi; + fd->po.pi = NULL; + } + + /* If release_fd is not NULL, we should be relinquishing control of the file + descriptor fd->fd (but we still own the grpc_fd structure). */ + if (release_fd != NULL) { + *release_fd = fd->fd; + } else { + close(fd->fd); + } + + fd->orphaned = true; + + GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_REF(error)); + + gpr_mu_unlock(&fd->po.mu); + UNREF_BY(fd, 2, reason); /* Drop the reference */ + if (unref_pi != NULL) { + /* Unref stale polling island here, outside the fd lock above. + The polling island owns a workqueue which owns an fd, and unreffing + inside the lock can cause an eventual lock loop that makes TSAN very + unhappy. */ + PI_UNREF(exec_ctx, unref_pi, "fd_orphan"); + } + if (error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(error); + gpr_log(GPR_DEBUG, "fd_orphan: %s", msg); + } + GRPC_ERROR_UNREF(error); +} + +static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, + grpc_fd *fd) { + gpr_atm notifier = gpr_atm_acq_load(&fd->read_notifier_pollset); + return (grpc_pollset *)notifier; +} + +static bool fd_is_shutdown(grpc_fd *fd) { + return grpc_lfev_is_shutdown(&fd->read_closure); +} + +/* Might be called multiple times */ +static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { + if (grpc_lfev_set_shutdown(exec_ctx, &fd->read_closure, + GRPC_ERROR_REF(why))) { + shutdown(fd->fd, SHUT_RDWR); + grpc_lfev_set_shutdown(exec_ctx, &fd->write_closure, GRPC_ERROR_REF(why)); + } + GRPC_ERROR_UNREF(why); +} + +static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + grpc_lfev_notify_on(exec_ctx, &fd->read_closure, closure, "read"); +} + +static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + grpc_lfev_notify_on(exec_ctx, &fd->write_closure, closure, "write"); +} + +/******************************************************************************* + * Pollset Definitions + */ +GPR_TLS_DECL(g_current_thread_pollset); +GPR_TLS_DECL(g_current_thread_worker); +static __thread bool g_initialized_sigmask; +static __thread sigset_t g_orig_sigmask; + +static void sig_handler(int sig_num) { +#ifdef GRPC_EPOLL_DEBUG + gpr_log(GPR_INFO, "Received signal %d", sig_num); +#endif +} + +static void poller_kick_init() { signal(grpc_wakeup_signal, sig_handler); } + +/* Global state management */ +static grpc_error *pollset_global_init(void) { + gpr_tls_init(&g_current_thread_pollset); + gpr_tls_init(&g_current_thread_worker); + poller_kick_init(); + return GRPC_ERROR_NONE; +} + +static void pollset_global_shutdown(void) { + gpr_tls_destroy(&g_current_thread_pollset); + gpr_tls_destroy(&g_current_thread_worker); +} + +static grpc_error *pollset_worker_kick(grpc_pollset_worker *worker) { + grpc_error *err = GRPC_ERROR_NONE; + + /* Kick the worker only if it was not already kicked */ + if (gpr_atm_no_barrier_cas(&worker->is_kicked, (gpr_atm)0, (gpr_atm)1)) { + GRPC_POLLING_TRACE( + "pollset_worker_kick: Kicking worker: %p (thread id: %ld)", + (void *)worker, (long int)worker->pt_id); + int err_num = pthread_kill(worker->pt_id, grpc_wakeup_signal); + if (err_num != 0) { + err = GRPC_OS_ERROR(err_num, "pthread_kill"); + } + } + return err; +} + +/* Return 1 if the pollset has active threads in pollset_work (pollset must + * be locked) */ +static int pollset_has_workers(grpc_pollset *p) { + return p->root_worker.next != &p->root_worker; +} + +static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; +} + +static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { + if (pollset_has_workers(p)) { + grpc_pollset_worker *w = p->root_worker.next; + remove_worker(p, w); + return w; + } else { + return NULL; + } +} + +static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->next = &p->root_worker; + worker->prev = worker->next->prev; + worker->prev->next = worker->next->prev = worker; +} + +static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev = &p->root_worker; + worker->next = worker->prev->next; + worker->prev->next = worker->next->prev = worker; +} + +/* p->mu must be held before calling this function */ +static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *p, + grpc_pollset_worker *specific_worker) { + GPR_TIMER_BEGIN("pollset_kick", 0); + grpc_error *error = GRPC_ERROR_NONE; + GRPC_STATS_INC_POLLSET_KICK(exec_ctx); + const char *err_desc = "Kick Failure"; + grpc_pollset_worker *worker = specific_worker; + if (worker != NULL) { + if (worker == GRPC_POLLSET_KICK_BROADCAST) { + if (pollset_has_workers(p)) { + GPR_TIMER_BEGIN("pollset_kick.broadcast", 0); + for (worker = p->root_worker.next; worker != &p->root_worker; + worker = worker->next) { + if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { + append_error(&error, pollset_worker_kick(worker), err_desc); + } + } + GPR_TIMER_END("pollset_kick.broadcast", 0); + } else { + p->kicked_without_pollers = true; + } + } else { + GPR_TIMER_MARK("kicked_specifically", 0); + if (gpr_tls_get(&g_current_thread_worker) != (intptr_t)worker) { + append_error(&error, pollset_worker_kick(worker), err_desc); + } + } + } else if (gpr_tls_get(&g_current_thread_pollset) != (intptr_t)p) { + /* Since worker == NULL, it means that we can kick "any" worker on this + pollset 'p'. If 'p' happens to be the same pollset this thread is + currently polling (i.e in pollset_work() function), then there is no need + to kick any other worker since the current thread can just absorb the + kick. This is the reason why we enter this case only when + g_current_thread_pollset is != p */ + + GPR_TIMER_MARK("kick_anonymous", 0); + worker = pop_front_worker(p); + if (worker != NULL) { + GPR_TIMER_MARK("finally_kick", 0); + push_back_worker(p, worker); + append_error(&error, pollset_worker_kick(worker), err_desc); + } else { + GPR_TIMER_MARK("kicked_no_pollers", 0); + p->kicked_without_pollers = true; + } + } + + GPR_TIMER_END("pollset_kick", 0); + GRPC_LOG_IF_ERROR("pollset_kick", GRPC_ERROR_REF(error)); + return error; +} + +static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + gpr_mu_init(&pollset->po.mu); + *mu = &pollset->po.mu; + pollset->po.pi = NULL; +#ifndef NDEBUG + pollset->po.obj_type = POLL_OBJ_POLLSET; +#endif + + pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; + pollset->kicked_without_pollers = false; + + pollset->shutting_down = false; + pollset->finish_shutdown_called = false; + pollset->shutdown_done = NULL; +} + +/* Convert a timespec to milliseconds: + - Very small or negative poll times are clamped to zero to do a non-blocking + poll (which becomes spin polling) + - Other small values are rounded up to one millisecond + - Longer than a millisecond polls are rounded up to the next nearest + millisecond to avoid spinning + - Infinite timeouts are converted to -1 */ +static int poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + static const int64_t max_spin_polling_us = 10; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return -1; + } + + if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( + max_spin_polling_us, + GPR_TIMESPAN))) <= 0) { + return 0; + } + timeout = gpr_time_sub(deadline, now); + int millis = gpr_time_to_millis(gpr_time_add( + timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); + return millis >= 1 ? millis : 1; +} + +static void fd_become_readable(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_pollset *notifier) { + grpc_lfev_set_ready(exec_ctx, &fd->read_closure, "read"); + + /* Note, it is possible that fd_become_readable might be called twice with + different 'notifier's when an fd becomes readable and it is in two epoll + sets (This can happen briefly during polling island merges). In such cases + it does not really matter which notifer is set as the read_notifier_pollset + (They would both point to the same polling island anyway) */ + /* Use release store to match with acquire load in fd_get_read_notifier */ + gpr_atm_rel_store(&fd->read_notifier_pollset, (gpr_atm)notifier); +} + +static void fd_become_writable(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + grpc_lfev_set_ready(exec_ctx, &fd->write_closure, "write"); +} + +static void pollset_release_polling_island(grpc_exec_ctx *exec_ctx, + grpc_pollset *ps, + const char *reason) { + if (ps->po.pi != NULL) { + PI_UNREF(exec_ctx, ps->po.pi, reason); + } + ps->po.pi = NULL; +} + +static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset) { + /* The pollset cannot have any workers if we are at this stage */ + GPR_ASSERT(!pollset_has_workers(pollset)); + + pollset->finish_shutdown_called = true; + + /* Release the ref and set pollset->po.pi to NULL */ + pollset_release_polling_island(exec_ctx, pollset, "ps_shutdown"); + GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE); +} + +/* pollset->po.mu lock must be held by the caller before calling this */ +static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + GPR_TIMER_BEGIN("pollset_shutdown", 0); + GPR_ASSERT(!pollset->shutting_down); + pollset->shutting_down = true; + pollset->shutdown_done = closure; + pollset_kick(exec_ctx, pollset, GRPC_POLLSET_KICK_BROADCAST); + + /* If the pollset has any workers, we cannot call finish_shutdown_locked() + because it would release the underlying polling island. In such a case, we + let the last worker call finish_shutdown_locked() from pollset_work() */ + if (!pollset_has_workers(pollset)) { + GPR_ASSERT(!pollset->finish_shutdown_called); + GPR_TIMER_MARK("pollset_shutdown.finish_shutdown_locked", 0); + finish_shutdown_locked(exec_ctx, pollset); + } + GPR_TIMER_END("pollset_shutdown", 0); +} + +/* pollset_shutdown is guaranteed to be called before pollset_destroy. So other + * than destroying the mutexes, there is nothing special that needs to be done + * here */ +static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + GPR_ASSERT(!pollset_has_workers(pollset)); + gpr_mu_destroy(&pollset->po.mu); +} + +#define GRPC_EPOLL_MAX_EVENTS 100 +/* Note: sig_mask contains the signal mask to use *during* epoll_wait() */ +static void pollset_work_and_unlock(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, + grpc_pollset_worker *worker, int timeout_ms, + sigset_t *sig_mask, grpc_error **error) { + struct epoll_event ep_ev[GRPC_EPOLL_MAX_EVENTS]; + int epoll_fd = -1; + int ep_rv; + polling_island *pi = NULL; + char *err_msg; + const char *err_desc = "pollset_work_and_unlock"; + GPR_TIMER_BEGIN("pollset_work_and_unlock", 0); + + /* We need to get the epoll_fd to wait on. The epoll_fd is in inside the + latest polling island pointed by pollset->po.pi + + Since epoll_fd is immutable, we can read it without obtaining the polling + island lock. There is however a possibility that the polling island (from + which we got the epoll_fd) got merged with another island while we are + in this function. This is still okay because in such a case, we will wakeup + right-away from epoll_wait() and pick up the latest polling_island the next + this function (i.e pollset_work_and_unlock()) is called */ + + if (pollset->po.pi == NULL) { + pollset->po.pi = polling_island_create(exec_ctx, NULL, error); + if (pollset->po.pi == NULL) { + GPR_TIMER_END("pollset_work_and_unlock", 0); + return; /* Fatal error. We cannot continue */ + } + + PI_ADD_REF(pollset->po.pi, "ps"); + GRPC_POLLING_TRACE("pollset_work: pollset: %p created new pi: %p", + (void *)pollset, (void *)pollset->po.pi); + } + + pi = polling_island_maybe_get_latest(pollset->po.pi); + epoll_fd = pi->epoll_fd; + + /* Update the pollset->po.pi since the island being pointed by + pollset->po.pi maybe older than the one pointed by pi) */ + if (pollset->po.pi != pi) { + /* Always do PI_ADD_REF before PI_UNREF because PI_UNREF may cause the + polling island to be deleted */ + PI_ADD_REF(pi, "ps"); + PI_UNREF(exec_ctx, pollset->po.pi, "ps"); + pollset->po.pi = pi; + } + + /* Add an extra ref so that the island does not get destroyed (which means + the epoll_fd won't be closed) while we are are doing an epoll_wait() on the + epoll_fd */ + PI_ADD_REF(pi, "ps_work"); + gpr_mu_unlock(&pollset->po.mu); + + gpr_atm_no_barrier_fetch_add(&pi->poller_count, 1); + g_current_thread_polling_island = pi; + + GRPC_SCHEDULING_START_BLOCKING_REGION; + GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); + ep_rv = + epoll_pwait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, timeout_ms, sig_mask); + GRPC_SCHEDULING_END_BLOCKING_REGION; + if (ep_rv < 0) { + if (errno != EINTR) { + gpr_asprintf(&err_msg, + "epoll_wait() epoll fd: %d failed with error: %d (%s)", + epoll_fd, errno, strerror(errno)); + append_error(error, GRPC_OS_ERROR(errno, err_msg), err_desc); + } else { + /* We were interrupted. Save an interation by doing a zero timeout + epoll_wait to see if there are any other events of interest */ + GRPC_POLLING_TRACE("pollset_work: pollset: %p, worker: %p received kick", + (void *)pollset, (void *)worker); + ep_rv = epoll_wait(epoll_fd, ep_ev, GRPC_EPOLL_MAX_EVENTS, 0); + } + } + +#ifdef GRPC_TSAN + /* See the definition of g_poll_sync for more details */ + gpr_atm_acq_load(&g_epoll_sync); +#endif /* defined(GRPC_TSAN) */ + + for (int i = 0; i < ep_rv; ++i) { + void *data_ptr = ep_ev[i].data.ptr; + if (data_ptr == &polling_island_wakeup_fd) { + GRPC_POLLING_TRACE( + "pollset_work: pollset: %p, worker: %p polling island (epoll_fd: " + "%d) got merged", + (void *)pollset, (void *)worker, epoll_fd); + /* This means that our polling island is merged with a different + island. We do not have to do anything here since the subsequent call + to the function pollset_work_and_unlock() will pick up the correct + epoll_fd */ + } else { + grpc_fd *fd = (grpc_fd *)data_ptr; + int cancel = ep_ev[i].events & (EPOLLERR | EPOLLHUP); + int read_ev = ep_ev[i].events & (EPOLLIN | EPOLLPRI); + int write_ev = ep_ev[i].events & EPOLLOUT; + if (read_ev || cancel) { + fd_become_readable(exec_ctx, fd, pollset); + } + if (write_ev || cancel) { + fd_become_writable(exec_ctx, fd); + } + } + } + + g_current_thread_polling_island = NULL; + gpr_atm_no_barrier_fetch_add(&pi->poller_count, -1); + + GPR_ASSERT(pi != NULL); + + /* Before leaving, release the extra ref we added to the polling island. It + is important to use "pi" here (i.e our old copy of pollset->po.pi + that we got before releasing the polling island lock). This is because + pollset->po.pi pointer might get udpated in other parts of the + code when there is an island merge while we are doing epoll_wait() above */ + PI_UNREF(exec_ctx, pi, "ps_work"); + + GPR_TIMER_END("pollset_work_and_unlock", 0); +} + +/* pollset->po.mu lock must be held by the caller before calling this. + The function pollset_work() may temporarily release the lock (pollset->po.mu) + during the course of its execution but it will always re-acquire the lock and + ensure that it is held by the time the function returns */ +static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + GPR_TIMER_BEGIN("pollset_work", 0); + grpc_error *error = GRPC_ERROR_NONE; + int timeout_ms = poll_deadline_to_millis_timeout(deadline, now); + + sigset_t new_mask; + + grpc_pollset_worker worker; + worker.next = worker.prev = NULL; + worker.pt_id = pthread_self(); + gpr_atm_no_barrier_store(&worker.is_kicked, (gpr_atm)0); + + if (worker_hdl) *worker_hdl = &worker; + + gpr_tls_set(&g_current_thread_pollset, (intptr_t)pollset); + gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); + + if (pollset->kicked_without_pollers) { + /* If the pollset was kicked without pollers, pretend that the current + worker got the kick and skip polling. A kick indicates that there is some + work that needs attention like an event on the completion queue or an + alarm */ + GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); + pollset->kicked_without_pollers = 0; + } else if (!pollset->shutting_down) { + /* We use the posix-signal with number 'grpc_wakeup_signal' for waking up + (i.e 'kicking') a worker in the pollset. A 'kick' is a way to inform the + worker that there is some pending work that needs immediate attention + (like an event on the completion queue, or a polling island merge that + results in a new epoll-fd to wait on) and that the worker should not + spend time waiting in epoll_pwait(). + + A worker can be kicked anytime from the point it is added to the pollset + via push_front_worker() (or push_back_worker()) to the point it is + removed via remove_worker(). + If the worker is kicked before/during it calls epoll_pwait(), it should + immediately exit from epoll_wait(). If the worker is kicked after it + returns from epoll_wait(), then nothing really needs to be done. + + To accomplish this, we mask 'grpc_wakeup_signal' on this thread at all + times *except* when it is in epoll_pwait(). This way, the worker never + misses acting on a kick */ + + if (!g_initialized_sigmask) { + sigemptyset(&new_mask); + sigaddset(&new_mask, grpc_wakeup_signal); + pthread_sigmask(SIG_BLOCK, &new_mask, &g_orig_sigmask); + sigdelset(&g_orig_sigmask, grpc_wakeup_signal); + g_initialized_sigmask = true; + /* new_mask: The new thread mask which blocks 'grpc_wakeup_signal'. + This is the mask used at all times *except during + epoll_wait()*" + g_orig_sigmask: The thread mask which allows 'grpc_wakeup_signal' and + this is the mask to use *during epoll_wait()* + + The new_mask is set on the worker before it is added to the pollset + (i.e before it can be kicked) */ + } + + push_front_worker(pollset, &worker); /* Add worker to pollset */ + + pollset_work_and_unlock(exec_ctx, pollset, &worker, timeout_ms, + &g_orig_sigmask, &error); + grpc_exec_ctx_flush(exec_ctx); + + gpr_mu_lock(&pollset->po.mu); + + /* Note: There is no need to reset worker.is_kicked to 0 since we are no + longer going to use this worker */ + remove_worker(pollset, &worker); + } + + /* If we are the last worker on the pollset (i.e pollset_has_workers() is + false at this point) and the pollset is shutting down, we may have to + finish the shutdown process by calling finish_shutdown_locked(). + See pollset_shutdown() for more details. + + Note: Continuing to access pollset here is safe; it is the caller's + responsibility to not destroy a pollset when it has outstanding calls to + pollset_work() */ + if (pollset->shutting_down && !pollset_has_workers(pollset) && + !pollset->finish_shutdown_called) { + GPR_TIMER_MARK("pollset_work.finish_shutdown_locked", 0); + finish_shutdown_locked(exec_ctx, pollset); + + gpr_mu_unlock(&pollset->po.mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->po.mu); + } + + if (worker_hdl) *worker_hdl = NULL; + + gpr_tls_set(&g_current_thread_pollset, (intptr_t)0); + gpr_tls_set(&g_current_thread_worker, (intptr_t)0); + + GPR_TIMER_END("pollset_work", 0); + + GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); + return error; +} + +static void add_poll_object(grpc_exec_ctx *exec_ctx, poll_obj *bag, + poll_obj_type bag_type, poll_obj *item, + poll_obj_type item_type) { + GPR_TIMER_BEGIN("add_poll_object", 0); + +#ifndef NDEBUG + GPR_ASSERT(item->obj_type == item_type); + GPR_ASSERT(bag->obj_type == bag_type); +#endif + + grpc_error *error = GRPC_ERROR_NONE; + polling_island *pi_new = NULL; + + gpr_mu_lock(&bag->mu); + gpr_mu_lock(&item->mu); + +retry: + /* + * 1) If item->pi and bag->pi are both non-NULL and equal, do nothing + * 2) If item->pi and bag->pi are both NULL, create a new polling island (with + * a refcount of 2) and point item->pi and bag->pi to the new island + * 3) If exactly one of item->pi or bag->pi is NULL, update it to point to + * the other's non-NULL pi + * 4) Finally if item->pi and bag-pi are non-NULL and not-equal, merge the + * polling islands and update item->pi and bag->pi to point to the new + * island + */ + + /* Early out if we are trying to add an 'fd' to a 'bag' but the fd is already + * orphaned */ + if (item_type == POLL_OBJ_FD && (FD_FROM_PO(item))->orphaned) { + gpr_mu_unlock(&item->mu); + gpr_mu_unlock(&bag->mu); + return; + } + + if (item->pi == bag->pi) { + pi_new = item->pi; + if (pi_new == NULL) { + /* GPR_ASSERT(item->pi == bag->pi == NULL) */ + + /* If we are adding an fd to a bag (i.e pollset or pollset_set), then + * we need to do some extra work to make TSAN happy */ + if (item_type == POLL_OBJ_FD) { + /* Unlock before creating a new polling island: the polling island will + create a workqueue which creates a file descriptor, and holding an fd + lock here can eventually cause a loop to appear to TSAN (making it + unhappy). We don't think it's a real loop (there's an epoch point + where that loop possibility disappears), but the advantages of + keeping TSAN happy outweigh any performance advantage we might have + by keeping the lock held. */ + gpr_mu_unlock(&item->mu); + pi_new = polling_island_create(exec_ctx, FD_FROM_PO(item), &error); + gpr_mu_lock(&item->mu); + + /* Need to reverify any assumptions made between the initial lock and + getting to this branch: if they've changed, we need to throw away our + work and figure things out again. */ + if (item->pi != NULL) { + GRPC_POLLING_TRACE( + "add_poll_object: Raced creating new polling island. pi_new: %p " + "(fd: %d, %s: %p)", + (void *)pi_new, FD_FROM_PO(item)->fd, poll_obj_string(bag_type), + (void *)bag); + /* No need to lock 'pi_new' here since this is a new polling island + and no one has a reference to it yet */ + polling_island_remove_all_fds_locked(pi_new, true, &error); + + /* Ref and unref so that the polling island gets deleted during unref + */ + PI_ADD_REF(pi_new, "dance_of_destruction"); + PI_UNREF(exec_ctx, pi_new, "dance_of_destruction"); + goto retry; + } + } else { + pi_new = polling_island_create(exec_ctx, NULL, &error); + } + + GRPC_POLLING_TRACE( + "add_poll_object: Created new polling island. pi_new: %p (%s: %p, " + "%s: %p)", + (void *)pi_new, poll_obj_string(item_type), (void *)item, + poll_obj_string(bag_type), (void *)bag); + } else { + GRPC_POLLING_TRACE( + "add_poll_object: Same polling island. pi: %p (%s, %s)", + (void *)pi_new, poll_obj_string(item_type), + poll_obj_string(bag_type)); + } + } else if (item->pi == NULL) { + /* GPR_ASSERT(bag->pi != NULL) */ + /* Make pi_new point to latest pi*/ + pi_new = polling_island_lock(bag->pi); + + if (item_type == POLL_OBJ_FD) { + grpc_fd *fd = FD_FROM_PO(item); + polling_island_add_fds_locked(pi_new, &fd, 1, true, &error); + } + + gpr_mu_unlock(&pi_new->mu); + GRPC_POLLING_TRACE( + "add_poll_obj: item->pi was NULL. pi_new: %p (item(%s): %p, " + "bag(%s): %p)", + (void *)pi_new, poll_obj_string(item_type), (void *)item, + poll_obj_string(bag_type), (void *)bag); + } else if (bag->pi == NULL) { + /* GPR_ASSERT(item->pi != NULL) */ + /* Make pi_new to point to latest pi */ + pi_new = polling_island_lock(item->pi); + gpr_mu_unlock(&pi_new->mu); + GRPC_POLLING_TRACE( + "add_poll_obj: bag->pi was NULL. pi_new: %p (item(%s): %p, " + "bag(%s): %p)", + (void *)pi_new, poll_obj_string(item_type), (void *)item, + poll_obj_string(bag_type), (void *)bag); + } else { + pi_new = polling_island_merge(item->pi, bag->pi, &error); + GRPC_POLLING_TRACE( + "add_poll_obj: polling islands merged. pi_new: %p (item(%s): %p, " + "bag(%s): %p)", + (void *)pi_new, poll_obj_string(item_type), (void *)item, + poll_obj_string(bag_type), (void *)bag); + } + + /* At this point, pi_new is the polling island that both item->pi and bag->pi + MUST be pointing to */ + + if (item->pi != pi_new) { + PI_ADD_REF(pi_new, poll_obj_string(item_type)); + if (item->pi != NULL) { + PI_UNREF(exec_ctx, item->pi, poll_obj_string(item_type)); + } + item->pi = pi_new; + } + + if (bag->pi != pi_new) { + PI_ADD_REF(pi_new, poll_obj_string(bag_type)); + if (bag->pi != NULL) { + PI_UNREF(exec_ctx, bag->pi, poll_obj_string(bag_type)); + } + bag->pi = pi_new; + } + + gpr_mu_unlock(&item->mu); + gpr_mu_unlock(&bag->mu); + + GRPC_LOG_IF_ERROR("add_poll_object", error); + GPR_TIMER_END("add_poll_object", 0); +} + +static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) { + add_poll_object(exec_ctx, &pollset->po, POLL_OBJ_POLLSET, &fd->po, + POLL_OBJ_FD); +} + +/******************************************************************************* + * Pollset-set Definitions + */ + +static grpc_pollset_set *pollset_set_create(void) { + grpc_pollset_set *pss = (grpc_pollset_set *)gpr_malloc(sizeof(*pss)); + gpr_mu_init(&pss->po.mu); + pss->po.pi = NULL; +#ifndef NDEBUG + pss->po.obj_type = POLL_OBJ_POLLSET_SET; +#endif + return pss; +} + +static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss) { + gpr_mu_destroy(&pss->po.mu); + + if (pss->po.pi != NULL) { + PI_UNREF(exec_ctx, pss->po.pi, "pss_destroy"); + } + + gpr_free(pss); +} + +static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, + grpc_fd *fd) { + add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &fd->po, + POLL_OBJ_FD); +} + +static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, grpc_pollset_set *pss, + grpc_fd *fd) { + /* Nothing to do */ +} + +static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss, grpc_pollset *ps) { + add_poll_object(exec_ctx, &pss->po, POLL_OBJ_POLLSET_SET, &ps->po, + POLL_OBJ_POLLSET); +} + +static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pss, grpc_pollset *ps) { + /* Nothing to do */ +} + +static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + add_poll_object(exec_ctx, &bag->po, POLL_OBJ_POLLSET_SET, &item->po, + POLL_OBJ_POLLSET_SET); +} + +static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + /* Nothing to do */ +} + +/* Test helper functions + * */ +void *grpc_fd_get_polling_island(grpc_fd *fd) { + polling_island *pi; + + gpr_mu_lock(&fd->po.mu); + pi = fd->po.pi; + gpr_mu_unlock(&fd->po.mu); + + return pi; +} + +void *grpc_pollset_get_polling_island(grpc_pollset *ps) { + polling_island *pi; + + gpr_mu_lock(&ps->po.mu); + pi = ps->po.pi; + gpr_mu_unlock(&ps->po.mu); + + return pi; +} + +bool grpc_are_polling_islands_equal(void *p, void *q) { + polling_island *p1 = (polling_island *)p; + polling_island *p2 = (polling_island *)q; + + /* Note: polling_island_lock_pair() may change p1 and p2 to point to the + latest polling islands in their respective linked lists */ + polling_island_lock_pair(&p1, &p2); + polling_island_unlock_pair(p1, p2); + + return p1 == p2; +} + +/******************************************************************************* + * Event engine binding + */ + +static void shutdown_engine(void) { + fd_global_shutdown(); + pollset_global_shutdown(); + polling_island_global_shutdown(); +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_is_shutdown, + fd_get_read_notifier_pollset, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_destroy, + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_engine, +}; + +/* It is possible that GLIBC has epoll but the underlying kernel doesn't. + * Create a dummy epoll_fd to make sure epoll support is available */ +static bool is_epoll_available() { + int fd = epoll_create1(EPOLL_CLOEXEC); + if (fd < 0) { + gpr_log( + GPR_ERROR, + "epoll_create1 failed with error: %d. Not using epoll polling engine", + fd); + return false; + } + close(fd); + return true; +} + +const grpc_event_engine_vtable *grpc_init_epollsig_linux( + bool explicit_request) { + /* If use of signals is disabled, we cannot use epoll engine*/ + if (is_grpc_wakeup_signal_initialized && grpc_wakeup_signal < 0) { + return NULL; + } + + if (!grpc_has_wakeup_fd()) { + return NULL; + } + + if (!is_epoll_available()) { + return NULL; + } + + if (!is_grpc_wakeup_signal_initialized) { + if (explicit_request) { + grpc_use_signal(SIGRTMIN + 6); + } else { + return NULL; + } + } + + fd_global_init(); + + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + return NULL; + } + + if (!GRPC_LOG_IF_ERROR("polling_island_global_init", + polling_island_global_init())) { + return NULL; + } + + return &vtable; +} + +#else /* defined(GRPC_LINUX_EPOLL) */ +#if defined(GRPC_POSIX_SOCKET) +#include "src/core/lib/iomgr/ev_posix.h" +/* If GRPC_LINUX_EPOLL is not defined, it means epoll is not available. Return + * NULL */ +const grpc_event_engine_vtable *grpc_init_epollsig_linux( + bool explicit_request) { + return NULL; +} +#endif /* defined(GRPC_POSIX_SOCKET) */ + +void grpc_use_signal(int signum) {} +#endif /* !defined(GRPC_LINUX_EPOLL) */ diff --git a/src/core/lib/iomgr/ev_poll_posix.c b/src/core/lib/iomgr/ev_poll_posix.c deleted file mode 100644 index e170702dca..0000000000 --- a/src/core/lib/iomgr/ev_poll_posix.c +++ /dev/null @@ -1,1746 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/ev_poll_posix.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/wakeup_fd_cv.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/murmur_hash.h" - -#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) - -/******************************************************************************* - * FD declarations - */ - -typedef struct grpc_fd_watcher { - struct grpc_fd_watcher *next; - struct grpc_fd_watcher *prev; - grpc_pollset *pollset; - grpc_pollset_worker *worker; - grpc_fd *fd; -} grpc_fd_watcher; - -struct grpc_fd { - int fd; - /* refst format: - bit0: 1=active/0=orphaned - bit1-n: refcount - meaning that mostly we ref by two to avoid altering the orphaned bit, - and just unref by 1 when we're ready to flag the object as orphaned */ - gpr_atm refst; - - gpr_mu mu; - int shutdown; - int closed; - int released; - grpc_error *shutdown_error; - - /* The watcher list. - - The following watcher related fields are protected by watcher_mu. - - An fd_watcher is an ephemeral object created when an fd wants to - begin polling, and destroyed after the poll. - - It denotes the fd's interest in whether to read poll or write poll - or both or neither on this fd. - - If a watcher is asked to poll for reads or writes, the read_watcher - or write_watcher fields are set respectively. A watcher may be asked - to poll for both, in which case both fields will be set. - - read_watcher and write_watcher may be NULL if no watcher has been - asked to poll for reads or writes. - - If an fd_watcher is not asked to poll for reads or writes, it's added - to a linked list of inactive watchers, rooted at inactive_watcher_root. - If at a later time there becomes need of a poller to poll, one of - the inactive pollers may be kicked out of their poll loops to take - that responsibility. */ - grpc_fd_watcher inactive_watcher_root; - grpc_fd_watcher *read_watcher; - grpc_fd_watcher *write_watcher; - - grpc_closure *read_closure; - grpc_closure *write_closure; - - grpc_closure *on_done_closure; - - grpc_iomgr_object iomgr_object; - - /* The pollset that last noticed and notified that the fd is readable */ - grpc_pollset *read_notifier_pollset; -}; - -/* Begin polling on an fd. - Registers that the given pollset is interested in this fd - so that if read - or writability interest changes, the pollset can be kicked to pick up that - new interest. - Return value is: - (fd_needs_read? read_mask : 0) | (fd_needs_write? write_mask : 0) - i.e. a combination of read_mask and write_mask determined by the fd's current - interest in said events. - Polling strategies that do not need to alter their behavior depending on the - fd's current interest (such as epoll) do not need to call this function. - MUST NOT be called with a pollset lock taken */ -static uint32_t fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, - grpc_pollset_worker *worker, uint32_t read_mask, - uint32_t write_mask, grpc_fd_watcher *rec); -/* Complete polling previously started with fd_begin_poll - MUST NOT be called with a pollset lock taken - if got_read or got_write are 1, also does the become_{readable,writable} as - appropriate. */ -static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *rec, - int got_read, int got_write, - grpc_pollset *read_notifier_pollset); - -/* Return 1 if this fd is orphaned, 0 otherwise */ -static bool fd_is_orphaned(grpc_fd *fd); - -#ifndef NDEBUG -static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); -static void fd_unref(grpc_fd *fd, const char *reason, const char *file, - int line); -#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__) -#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__) -#else -static void fd_ref(grpc_fd *fd); -static void fd_unref(grpc_fd *fd); -#define GRPC_FD_REF(fd, reason) fd_ref(fd) -#define GRPC_FD_UNREF(fd, reason) fd_unref(fd) -#endif - -#define CLOSURE_NOT_READY ((grpc_closure *)0) -#define CLOSURE_READY ((grpc_closure *)1) - -/******************************************************************************* - * pollset declarations - */ - -typedef struct grpc_cached_wakeup_fd { - grpc_wakeup_fd fd; - struct grpc_cached_wakeup_fd *next; -} grpc_cached_wakeup_fd; - -struct grpc_pollset_worker { - grpc_cached_wakeup_fd *wakeup_fd; - int reevaluate_polling_on_wakeup; - int kicked_specifically; - struct grpc_pollset_worker *next; - struct grpc_pollset_worker *prev; -}; - -struct grpc_pollset { - gpr_mu mu; - grpc_pollset_worker root_worker; - int shutting_down; - int called_shutdown; - int kicked_without_pollers; - grpc_closure *shutdown_done; - grpc_closure_list idle_jobs; - int pollset_set_count; - /* all polled fds */ - size_t fd_count; - size_t fd_capacity; - grpc_fd **fds; - /* Local cache of eventfds for workers */ - grpc_cached_wakeup_fd *local_wakeup_cache; -}; - -/* Add an fd to a pollset */ -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - struct grpc_fd *fd); - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd); - -/* Convert a timespec to milliseconds: - - very small or negative poll times are clamped to zero to do a - non-blocking poll (which becomes spin polling) - - other small values are rounded up to one millisecond - - longer than a millisecond polls are rounded up to the next nearest - millisecond to avoid spinning - - infinite timeouts are converted to -1 */ -static int poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now); - -/* Allow kick to wakeup the currently polling worker */ -#define GRPC_POLLSET_CAN_KICK_SELF 1 -/* Force the wakee to repoll when awoken */ -#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2 -/* As per pollset_kick, with an extended set of flags (defined above) - -- mostly for fd_posix's use. */ -static grpc_error *pollset_kick_ext(grpc_exec_ctx *exec_ctx, grpc_pollset *p, - grpc_pollset_worker *specific_worker, - uint32_t flags) GRPC_MUST_USE_RESULT; - -/* Return 1 if the pollset has active threads in pollset_work (pollset must - * be locked) */ -static bool pollset_has_workers(grpc_pollset *pollset); - -/******************************************************************************* - * pollset_set definitions - */ - -struct grpc_pollset_set { - gpr_mu mu; - - size_t pollset_count; - size_t pollset_capacity; - grpc_pollset **pollsets; - - size_t pollset_set_count; - size_t pollset_set_capacity; - struct grpc_pollset_set **pollset_sets; - - size_t fd_count; - size_t fd_capacity; - grpc_fd **fds; -}; - -/******************************************************************************* - * condition variable polling definitions - */ - -#define POLLCV_THREAD_GRACE_MS 1000 -#define CV_POLL_PERIOD_MS 1000 -#define CV_DEFAULT_TABLE_SIZE 16 - -typedef struct poll_result { - gpr_refcount refcount; - cv_node *watchers; - int watchcount; - struct pollfd *fds; - nfds_t nfds; - int retval; - int err; - int completed; -} poll_result; - -typedef struct poll_args { - gpr_cv trigger; - int trigger_set; - struct pollfd *fds; - nfds_t nfds; - poll_result *result; - struct poll_args *next; - struct poll_args *prev; -} poll_args; - -// This is a 2-tiered cache, we mantain a hash table -// of active poll calls, so we can wait on the result -// of that call. We also maintain a freelist of inactive -// poll threads. -typedef struct poll_hash_table { - poll_args *free_pollers; - poll_args **active_pollers; - unsigned int size; - unsigned int count; -} poll_hash_table; - -poll_hash_table poll_cache; -cv_fd_table g_cvfds; - -/******************************************************************************* - * fd_posix.c - */ - -#ifndef NDEBUG -#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) -#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__) -static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, - "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", - fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), - gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); - } -#else -#define REF_BY(fd, n, reason) ref_by(fd, n) -#define UNREF_BY(fd, n, reason) unref_by(fd, n) -static void ref_by(grpc_fd *fd, int n) { -#endif - GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); -} - -#ifndef NDEBUG -static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { - gpr_log(GPR_DEBUG, - "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", - fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), - gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); - } -#else -static void unref_by(grpc_fd *fd, int n) { -#endif - gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); - if (old == n) { - gpr_mu_destroy(&fd->mu); - grpc_iomgr_unregister_object(&fd->iomgr_object); - if (fd->shutdown) GRPC_ERROR_UNREF(fd->shutdown_error); - gpr_free(fd); - } else { - GPR_ASSERT(old > n); - } -} - -static grpc_fd *fd_create(int fd, const char *name) { - grpc_fd *r = (grpc_fd *)gpr_malloc(sizeof(*r)); - gpr_mu_init(&r->mu); - gpr_atm_rel_store(&r->refst, 1); - r->shutdown = 0; - r->read_closure = CLOSURE_NOT_READY; - r->write_closure = CLOSURE_NOT_READY; - r->fd = fd; - r->inactive_watcher_root.next = r->inactive_watcher_root.prev = - &r->inactive_watcher_root; - r->read_watcher = r->write_watcher = NULL; - r->on_done_closure = NULL; - r->closed = 0; - r->released = 0; - r->read_notifier_pollset = NULL; - - char *name2; - gpr_asprintf(&name2, "%s fd=%d", name, fd); - grpc_iomgr_register_object(&r->iomgr_object, name2); - gpr_free(name2); - return r; -} - -static bool fd_is_orphaned(grpc_fd *fd) { - return (gpr_atm_acq_load(&fd->refst) & 1) == 0; -} - -/* Return the read-notifier pollset */ -static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - grpc_pollset *notifier = NULL; - - gpr_mu_lock(&fd->mu); - notifier = fd->read_notifier_pollset; - gpr_mu_unlock(&fd->mu); - - return notifier; -} - -static grpc_error *pollset_kick_locked(grpc_exec_ctx *exec_ctx, - grpc_fd_watcher *watcher) { - gpr_mu_lock(&watcher->pollset->mu); - GPR_ASSERT(watcher->worker); - grpc_error *err = - pollset_kick_ext(exec_ctx, watcher->pollset, watcher->worker, - GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP); - gpr_mu_unlock(&watcher->pollset->mu); - return err; -} - -static void maybe_wake_one_watcher_locked(grpc_exec_ctx *exec_ctx, - grpc_fd *fd) { - if (fd->inactive_watcher_root.next != &fd->inactive_watcher_root) { - pollset_kick_locked(exec_ctx, fd->inactive_watcher_root.next); - } else if (fd->read_watcher) { - pollset_kick_locked(exec_ctx, fd->read_watcher); - } else if (fd->write_watcher) { - pollset_kick_locked(exec_ctx, fd->write_watcher); - } -} - -static void wake_all_watchers_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - grpc_fd_watcher *watcher; - for (watcher = fd->inactive_watcher_root.next; - watcher != &fd->inactive_watcher_root; watcher = watcher->next) { - pollset_kick_locked(exec_ctx, watcher); - } - if (fd->read_watcher) { - pollset_kick_locked(exec_ctx, fd->read_watcher); - } - if (fd->write_watcher && fd->write_watcher != fd->read_watcher) { - pollset_kick_locked(exec_ctx, fd->write_watcher); - } -} - -static int has_watchers(grpc_fd *fd) { - return fd->read_watcher != NULL || fd->write_watcher != NULL || - fd->inactive_watcher_root.next != &fd->inactive_watcher_root; -} - -static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { - fd->closed = 1; - if (!fd->released) { - close(fd->fd); - } - GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE); -} - -static int fd_wrapped_fd(grpc_fd *fd) { - if (fd->released || fd->closed) { - return -1; - } else { - return fd->fd; - } -} - -static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *on_done, int *release_fd, - bool already_closed, const char *reason) { - fd->on_done_closure = on_done; - fd->released = release_fd != NULL; - if (release_fd != NULL) { - *release_fd = fd->fd; - fd->released = true; - } else if (already_closed) { - fd->released = true; - } - gpr_mu_lock(&fd->mu); - REF_BY(fd, 1, reason); /* remove active status, but keep referenced */ - if (!has_watchers(fd)) { - close_fd_locked(exec_ctx, fd); - } else { - wake_all_watchers_locked(exec_ctx, fd); - } - gpr_mu_unlock(&fd->mu); - UNREF_BY(fd, 2, reason); /* drop the reference */ -} - -/* increment refcount by two to avoid changing the orphan bit */ -#ifndef NDEBUG -static void fd_ref(grpc_fd *fd, const char *reason, const char *file, - int line) { - ref_by(fd, 2, reason, file, line); -} - -static void fd_unref(grpc_fd *fd, const char *reason, const char *file, - int line) { - unref_by(fd, 2, reason, file, line); -} -#else -static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); } - -static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); } -#endif - -static grpc_error *fd_shutdown_error(grpc_fd *fd) { - if (!fd->shutdown) { - return GRPC_ERROR_NONE; - } else { - return GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "FD shutdown", &fd->shutdown_error, 1); - } -} - -static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure **st, grpc_closure *closure) { - if (fd->shutdown) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("FD shutdown")); - } else if (*st == CLOSURE_NOT_READY) { - /* not ready ==> switch to a waiting state by setting the closure */ - *st = closure; - } else if (*st == CLOSURE_READY) { - /* already ready ==> queue the closure to run immediately */ - *st = CLOSURE_NOT_READY; - GRPC_CLOSURE_SCHED(exec_ctx, closure, fd_shutdown_error(fd)); - maybe_wake_one_watcher_locked(exec_ctx, fd); - } else { - /* upcallptr was set to a different closure. This is an error! */ - gpr_log(GPR_ERROR, - "User called a notify_on function with a previous callback still " - "pending"); - abort(); - } -} - -/* returns 1 if state becomes not ready */ -static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure **st) { - if (*st == CLOSURE_READY) { - /* duplicate ready ==> ignore */ - return 0; - } else if (*st == CLOSURE_NOT_READY) { - /* not ready, and not waiting ==> flag ready */ - *st = CLOSURE_READY; - return 0; - } else { - /* waiting ==> queue closure */ - GRPC_CLOSURE_SCHED(exec_ctx, *st, fd_shutdown_error(fd)); - *st = CLOSURE_NOT_READY; - return 1; - } -} - -static void set_read_notifier_pollset_locked( - grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_pollset *read_notifier_pollset) { - fd->read_notifier_pollset = read_notifier_pollset; -} - -static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - gpr_mu_lock(&fd->mu); - /* only shutdown once */ - if (!fd->shutdown) { - fd->shutdown = 1; - fd->shutdown_error = why; - /* signal read/write closed to OS so that future operations fail */ - shutdown(fd->fd, SHUT_RDWR); - set_ready_locked(exec_ctx, fd, &fd->read_closure); - set_ready_locked(exec_ctx, fd, &fd->write_closure); - } else { - GRPC_ERROR_UNREF(why); - } - gpr_mu_unlock(&fd->mu); -} - -static bool fd_is_shutdown(grpc_fd *fd) { - gpr_mu_lock(&fd->mu); - bool r = fd->shutdown; - gpr_mu_unlock(&fd->mu); - return r; -} - -static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - gpr_mu_lock(&fd->mu); - notify_on_locked(exec_ctx, fd, &fd->read_closure, closure); - gpr_mu_unlock(&fd->mu); -} - -static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - gpr_mu_lock(&fd->mu); - notify_on_locked(exec_ctx, fd, &fd->write_closure, closure); - gpr_mu_unlock(&fd->mu); -} - -static uint32_t fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, - grpc_pollset_worker *worker, uint32_t read_mask, - uint32_t write_mask, grpc_fd_watcher *watcher) { - uint32_t mask = 0; - grpc_closure *cur; - int requested; - /* keep track of pollers that have requested our events, in case they change - */ - GRPC_FD_REF(fd, "poll"); - - gpr_mu_lock(&fd->mu); - - /* if we are shutdown, then don't add to the watcher set */ - if (fd->shutdown) { - watcher->fd = NULL; - watcher->pollset = NULL; - watcher->worker = NULL; - gpr_mu_unlock(&fd->mu); - GRPC_FD_UNREF(fd, "poll"); - return 0; - } - - /* if there is nobody polling for read, but we need to, then start doing so */ - cur = fd->read_closure; - requested = cur != CLOSURE_READY; - if (read_mask && fd->read_watcher == NULL && requested) { - fd->read_watcher = watcher; - mask |= read_mask; - } - /* if there is nobody polling for write, but we need to, then start doing so - */ - cur = fd->write_closure; - requested = cur != CLOSURE_READY; - if (write_mask && fd->write_watcher == NULL && requested) { - fd->write_watcher = watcher; - mask |= write_mask; - } - /* if not polling, remember this watcher in case we need someone to later */ - if (mask == 0 && worker != NULL) { - watcher->next = &fd->inactive_watcher_root; - watcher->prev = watcher->next->prev; - watcher->next->prev = watcher->prev->next = watcher; - } - watcher->pollset = pollset; - watcher->worker = worker; - watcher->fd = fd; - gpr_mu_unlock(&fd->mu); - - return mask; -} - -static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *watcher, - int got_read, int got_write, - grpc_pollset *read_notifier_pollset) { - int was_polling = 0; - int kick = 0; - grpc_fd *fd = watcher->fd; - - if (fd == NULL) { - return; - } - - gpr_mu_lock(&fd->mu); - - if (watcher == fd->read_watcher) { - /* remove read watcher, kick if we still need a read */ - was_polling = 1; - if (!got_read) { - kick = 1; - } - fd->read_watcher = NULL; - } - if (watcher == fd->write_watcher) { - /* remove write watcher, kick if we still need a write */ - was_polling = 1; - if (!got_write) { - kick = 1; - } - fd->write_watcher = NULL; - } - if (!was_polling && watcher->worker != NULL) { - /* remove from inactive list */ - watcher->next->prev = watcher->prev; - watcher->prev->next = watcher->next; - } - if (got_read) { - if (set_ready_locked(exec_ctx, fd, &fd->read_closure)) { - kick = 1; - } - if (read_notifier_pollset != NULL) { - set_read_notifier_pollset_locked(exec_ctx, fd, read_notifier_pollset); - } - } - if (got_write) { - if (set_ready_locked(exec_ctx, fd, &fd->write_closure)) { - kick = 1; - } - } - if (kick) { - maybe_wake_one_watcher_locked(exec_ctx, fd); - } - if (fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) { - close_fd_locked(exec_ctx, fd); - } - gpr_mu_unlock(&fd->mu); - - GRPC_FD_UNREF(fd, "poll"); -} - -/******************************************************************************* - * pollset_posix.c - */ - -GPR_TLS_DECL(g_current_thread_poller); -GPR_TLS_DECL(g_current_thread_worker); - -static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev->next = worker->next; - worker->next->prev = worker->prev; -} - -static bool pollset_has_workers(grpc_pollset *p) { - return p->root_worker.next != &p->root_worker; -} - -static bool pollset_in_pollset_sets(grpc_pollset *p) { - return p->pollset_set_count; -} - -static bool pollset_has_observers(grpc_pollset *p) { - return pollset_has_workers(p) || pollset_in_pollset_sets(p); -} - -static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { - if (pollset_has_workers(p)) { - grpc_pollset_worker *w = p->root_worker.next; - remove_worker(p, w); - return w; - } else { - return NULL; - } -} - -static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->next = &p->root_worker; - worker->prev = worker->next->prev; - worker->prev->next = worker->next->prev = worker; -} - -static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { - worker->prev = &p->root_worker; - worker->next = worker->prev->next; - worker->prev->next = worker->next->prev = worker; -} - -static void kick_append_error(grpc_error **composite, grpc_error *error) { - if (error == GRPC_ERROR_NONE) return; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Kick Failure"); - } - *composite = grpc_error_add_child(*composite, error); -} - -static grpc_error *pollset_kick_ext(grpc_exec_ctx *exec_ctx, grpc_pollset *p, - grpc_pollset_worker *specific_worker, - uint32_t flags) { - GPR_TIMER_BEGIN("pollset_kick_ext", 0); - grpc_error *error = GRPC_ERROR_NONE; - GRPC_STATS_INC_POLLSET_KICK(exec_ctx); - - /* pollset->mu already held */ - if (specific_worker != NULL) { - if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { - GPR_TIMER_BEGIN("pollset_kick_ext.broadcast", 0); - GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); - for (specific_worker = p->root_worker.next; - specific_worker != &p->root_worker; - specific_worker = specific_worker->next) { - kick_append_error( - &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); - } - p->kicked_without_pollers = true; - GPR_TIMER_END("pollset_kick_ext.broadcast", 0); - } else if (gpr_tls_get(&g_current_thread_worker) != - (intptr_t)specific_worker) { - GPR_TIMER_MARK("different_thread_worker", 0); - if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { - specific_worker->reevaluate_polling_on_wakeup = true; - } - specific_worker->kicked_specifically = true; - kick_append_error(&error, - grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); - } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) { - GPR_TIMER_MARK("kick_yoself", 0); - if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { - specific_worker->reevaluate_polling_on_wakeup = true; - } - specific_worker->kicked_specifically = true; - kick_append_error(&error, - grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); - } - } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) { - GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); - GPR_TIMER_MARK("kick_anonymous", 0); - specific_worker = pop_front_worker(p); - if (specific_worker != NULL) { - if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) { - GPR_TIMER_MARK("kick_anonymous_not_self", 0); - push_back_worker(p, specific_worker); - specific_worker = pop_front_worker(p); - if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 && - gpr_tls_get(&g_current_thread_worker) == - (intptr_t)specific_worker) { - push_back_worker(p, specific_worker); - specific_worker = NULL; - } - } - if (specific_worker != NULL) { - GPR_TIMER_MARK("finally_kick", 0); - push_back_worker(p, specific_worker); - kick_append_error( - &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); - } - } else { - GPR_TIMER_MARK("kicked_no_pollers", 0); - p->kicked_without_pollers = true; - } - } - - GPR_TIMER_END("pollset_kick_ext", 0); - GRPC_LOG_IF_ERROR("pollset_kick_ext", GRPC_ERROR_REF(error)); - return error; -} - -static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *p, - grpc_pollset_worker *specific_worker) { - return pollset_kick_ext(exec_ctx, p, specific_worker, 0); -} - -/* global state management */ - -static grpc_error *pollset_global_init(void) { - gpr_tls_init(&g_current_thread_poller); - gpr_tls_init(&g_current_thread_worker); - return GRPC_ERROR_NONE; -} - -static void pollset_global_shutdown(void) { - gpr_tls_destroy(&g_current_thread_poller); - gpr_tls_destroy(&g_current_thread_worker); -} - -/* main interface */ - -static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - gpr_mu_init(&pollset->mu); - *mu = &pollset->mu; - pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; - pollset->shutting_down = 0; - pollset->called_shutdown = 0; - pollset->kicked_without_pollers = 0; - pollset->idle_jobs.head = pollset->idle_jobs.tail = NULL; - pollset->local_wakeup_cache = NULL; - pollset->kicked_without_pollers = 0; - pollset->fd_count = 0; - pollset->fd_capacity = 0; - pollset->fds = NULL; - pollset->pollset_set_count = 0; -} - -static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GPR_ASSERT(!pollset_has_workers(pollset)); - GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail); - while (pollset->local_wakeup_cache) { - grpc_cached_wakeup_fd *next = pollset->local_wakeup_cache->next; - grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd); - gpr_free(pollset->local_wakeup_cache); - pollset->local_wakeup_cache = next; - } - gpr_free(pollset->fds); - gpr_mu_destroy(&pollset->mu); -} - -static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_fd *fd) { - gpr_mu_lock(&pollset->mu); - size_t i; - /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */ - for (i = 0; i < pollset->fd_count; i++) { - if (pollset->fds[i] == fd) goto exit; - } - if (pollset->fd_count == pollset->fd_capacity) { - pollset->fd_capacity = - GPR_MAX(pollset->fd_capacity + 8, pollset->fd_count * 3 / 2); - pollset->fds = (grpc_fd **)gpr_realloc( - pollset->fds, sizeof(grpc_fd *) * pollset->fd_capacity); - } - pollset->fds[pollset->fd_count++] = fd; - GRPC_FD_REF(fd, "multipoller"); - pollset_kick(exec_ctx, pollset, NULL); -exit: - gpr_mu_unlock(&pollset->mu); -} - -static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GPR_ASSERT(grpc_closure_list_empty(pollset->idle_jobs)); - size_t i; - for (i = 0; i < pollset->fd_count; i++) { - GRPC_FD_UNREF(pollset->fds[i], "multipoller"); - } - pollset->fd_count = 0; - GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE); -} - -static void work_combine_error(grpc_error **composite, grpc_error *error) { - if (error == GRPC_ERROR_NONE) return; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("pollset_work"); - } - *composite = grpc_error_add_child(*composite, error); -} - -static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - grpc_pollset_worker worker; - if (worker_hdl) *worker_hdl = &worker; - grpc_error *error = GRPC_ERROR_NONE; - - /* Avoid malloc for small number of elements. */ - enum { inline_elements = 96 }; - struct pollfd pollfd_space[inline_elements]; - struct grpc_fd_watcher watcher_space[inline_elements]; - - /* pollset->mu already held */ - int added_worker = 0; - int locked = 1; - int queued_work = 0; - int keep_polling = 0; - GPR_TIMER_BEGIN("pollset_work", 0); - /* this must happen before we (potentially) drop pollset->mu */ - worker.next = worker.prev = NULL; - worker.reevaluate_polling_on_wakeup = 0; - if (pollset->local_wakeup_cache != NULL) { - worker.wakeup_fd = pollset->local_wakeup_cache; - pollset->local_wakeup_cache = worker.wakeup_fd->next; - } else { - worker.wakeup_fd = - (grpc_cached_wakeup_fd *)gpr_malloc(sizeof(*worker.wakeup_fd)); - error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd); - if (error != GRPC_ERROR_NONE) { - GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); - return error; - } - } - worker.kicked_specifically = 0; - /* If there's work waiting for the pollset to be idle, and the - pollset is idle, then do that work */ - if (!pollset_has_workers(pollset) && - !grpc_closure_list_empty(pollset->idle_jobs)) { - GPR_TIMER_MARK("pollset_work.idle_jobs", 0); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pollset->idle_jobs); - goto done; - } - /* If we're shutting down then we don't execute any extended work */ - if (pollset->shutting_down) { - GPR_TIMER_MARK("pollset_work.shutting_down", 0); - goto done; - } - /* Start polling, and keep doing so while we're being asked to - re-evaluate our pollers (this allows poll() based pollers to - ensure they don't miss wakeups) */ - keep_polling = 1; - gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset); - while (keep_polling) { - keep_polling = 0; - if (!pollset->kicked_without_pollers) { - if (!added_worker) { - push_front_worker(pollset, &worker); - added_worker = 1; - gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); - } - GPR_TIMER_BEGIN("maybe_work_and_unlock", 0); -#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) -#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) - - int timeout; - int r; - size_t i, fd_count; - nfds_t pfd_count; - grpc_fd_watcher *watchers; - struct pollfd *pfds; - - timeout = poll_deadline_to_millis_timeout(deadline, now); - - if (pollset->fd_count + 2 <= inline_elements) { - pfds = pollfd_space; - watchers = watcher_space; - } else { - /* Allocate one buffer to hold both pfds and watchers arrays */ - const size_t pfd_size = sizeof(*pfds) * (pollset->fd_count + 2); - const size_t watch_size = sizeof(*watchers) * (pollset->fd_count + 2); - void *buf = gpr_malloc(pfd_size + watch_size); - pfds = (struct pollfd *)buf; - watchers = (grpc_fd_watcher *)(void *)((char *)buf + pfd_size); - } - - fd_count = 0; - pfd_count = 1; - pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker.wakeup_fd->fd); - pfds[0].events = POLLIN; - pfds[0].revents = 0; - for (i = 0; i < pollset->fd_count; i++) { - if (fd_is_orphaned(pollset->fds[i])) { - GRPC_FD_UNREF(pollset->fds[i], "multipoller"); - } else { - pollset->fds[fd_count++] = pollset->fds[i]; - watchers[pfd_count].fd = pollset->fds[i]; - GRPC_FD_REF(watchers[pfd_count].fd, "multipoller_start"); - pfds[pfd_count].fd = pollset->fds[i]->fd; - pfds[pfd_count].revents = 0; - pfd_count++; - } - } - pollset->fd_count = fd_count; - gpr_mu_unlock(&pollset->mu); - - for (i = 1; i < pfd_count; i++) { - grpc_fd *fd = watchers[i].fd; - pfds[i].events = (short)fd_begin_poll(fd, pollset, &worker, POLLIN, - POLLOUT, &watchers[i]); - GRPC_FD_UNREF(fd, "multipoller_start"); - } - - /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid - even going into the blocking annotation if possible */ - GRPC_SCHEDULING_START_BLOCKING_REGION; - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - r = grpc_poll_function(pfds, pfd_count, timeout); - GRPC_SCHEDULING_END_BLOCKING_REGION; - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "%p poll=%d", pollset, r); - } - - if (r < 0) { - if (errno != EINTR) { - work_combine_error(&error, GRPC_OS_ERROR(errno, "poll")); - } - - for (i = 1; i < pfd_count; i++) { - if (watchers[i].fd == NULL) { - fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL); - } else { - // Wake up all the file descriptors, if we have an invalid one - // we can identify it on the next pollset_work() - fd_end_poll(exec_ctx, &watchers[i], 1, 1, pollset); - } - } - } else if (r == 0) { - for (i = 1; i < pfd_count; i++) { - fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL); - } - } else { - if (pfds[0].revents & POLLIN_CHECK) { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "%p: got_wakeup", pollset); - } - work_combine_error( - &error, grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd)); - } - for (i = 1; i < pfd_count; i++) { - if (watchers[i].fd == NULL) { - fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL); - } else { - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_DEBUG, "%p got_event: %d r:%d w:%d [%d]", pollset, - pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0, - (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents); - } - fd_end_poll(exec_ctx, &watchers[i], pfds[i].revents & POLLIN_CHECK, - pfds[i].revents & POLLOUT_CHECK, pollset); - } - } - } - - if (pfds != pollfd_space) { - /* pfds and watchers are in the same memory block pointed to by pfds */ - gpr_free(pfds); - } - - GPR_TIMER_END("maybe_work_and_unlock", 0); - locked = 0; - } else { - GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); - pollset->kicked_without_pollers = 0; - } - /* Finished execution - start cleaning up. - Note that we may arrive here from outside the enclosing while() loop. - In that case we won't loop though as we haven't added worker to the - worker list, which means nobody could ask us to re-evaluate polling). */ - done: - if (!locked) { - queued_work |= grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - locked = 1; - } - /* If we're forced to re-evaluate polling (via pollset_kick with - GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force - a loop */ - if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) { - worker.reevaluate_polling_on_wakeup = 0; - pollset->kicked_without_pollers = 0; - if (queued_work || worker.kicked_specifically) { - /* If there's queued work on the list, then set the deadline to be - immediate so we get back out of the polling loop quickly */ - deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC); - } - keep_polling = 1; - } - if (keep_polling) { - now = gpr_now(now.clock_type); - } - } - gpr_tls_set(&g_current_thread_poller, 0); - if (added_worker) { - remove_worker(pollset, &worker); - gpr_tls_set(&g_current_thread_worker, 0); - } - /* release wakeup fd to the local pool */ - worker.wakeup_fd->next = pollset->local_wakeup_cache; - pollset->local_wakeup_cache = worker.wakeup_fd; - /* check shutdown conditions */ - if (pollset->shutting_down) { - if (pollset_has_workers(pollset)) { - pollset_kick(exec_ctx, pollset, NULL); - } else if (!pollset->called_shutdown && !pollset_has_observers(pollset)) { - pollset->called_shutdown = 1; - gpr_mu_unlock(&pollset->mu); - finish_shutdown(exec_ctx, pollset); - grpc_exec_ctx_flush(exec_ctx); - /* Continuing to access pollset here is safe -- it is the caller's - * responsibility to not destroy when it has outstanding calls to - * pollset_work. - * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */ - gpr_mu_lock(&pollset->mu); - } else if (!grpc_closure_list_empty(pollset->idle_jobs)) { - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pollset->idle_jobs); - gpr_mu_unlock(&pollset->mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&pollset->mu); - } - } - if (worker_hdl) *worker_hdl = NULL; - GPR_TIMER_END("pollset_work", 0); - GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); - return error; -} - -static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_ASSERT(!pollset->shutting_down); - pollset->shutting_down = 1; - pollset->shutdown_done = closure; - pollset_kick(exec_ctx, pollset, GRPC_POLLSET_KICK_BROADCAST); - if (!pollset_has_workers(pollset)) { - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pollset->idle_jobs); - } - if (!pollset->called_shutdown && !pollset_has_observers(pollset)) { - pollset->called_shutdown = 1; - finish_shutdown(exec_ctx, pollset); - } -} - -static int poll_deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - static const int64_t max_spin_polling_us = 10; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return -1; - } - if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( - max_spin_polling_us, - GPR_TIMESPAN))) <= 0) { - return 0; - } - timeout = gpr_time_sub(deadline, now); - return gpr_time_to_millis(gpr_time_add( - timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); -} - -/******************************************************************************* - * pollset_set_posix.c - */ - -static grpc_pollset_set *pollset_set_create(void) { - grpc_pollset_set *pollset_set = - (grpc_pollset_set *)gpr_zalloc(sizeof(*pollset_set)); - gpr_mu_init(&pollset_set->mu); - return pollset_set; -} - -static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set) { - size_t i; - gpr_mu_destroy(&pollset_set->mu); - for (i = 0; i < pollset_set->fd_count; i++) { - GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); - } - for (i = 0; i < pollset_set->pollset_count; i++) { - grpc_pollset *pollset = pollset_set->pollsets[i]; - gpr_mu_lock(&pollset->mu); - pollset->pollset_set_count--; - /* check shutdown */ - if (pollset->shutting_down && !pollset->called_shutdown && - !pollset_has_observers(pollset)) { - pollset->called_shutdown = 1; - gpr_mu_unlock(&pollset->mu); - finish_shutdown(exec_ctx, pollset); - } else { - gpr_mu_unlock(&pollset->mu); - } - } - gpr_free(pollset_set->pollsets); - gpr_free(pollset_set->pollset_sets); - gpr_free(pollset_set->fds); - gpr_free(pollset_set); -} - -static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, - grpc_pollset *pollset) { - size_t i, j; - gpr_mu_lock(&pollset->mu); - pollset->pollset_set_count++; - gpr_mu_unlock(&pollset->mu); - gpr_mu_lock(&pollset_set->mu); - if (pollset_set->pollset_count == pollset_set->pollset_capacity) { - pollset_set->pollset_capacity = - GPR_MAX(8, 2 * pollset_set->pollset_capacity); - pollset_set->pollsets = (grpc_pollset **)gpr_realloc( - pollset_set->pollsets, - pollset_set->pollset_capacity * sizeof(*pollset_set->pollsets)); - } - pollset_set->pollsets[pollset_set->pollset_count++] = pollset; - for (i = 0, j = 0; i < pollset_set->fd_count; i++) { - if (fd_is_orphaned(pollset_set->fds[i])) { - GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); - } else { - pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]); - pollset_set->fds[j++] = pollset_set->fds[i]; - } - } - pollset_set->fd_count = j; - gpr_mu_unlock(&pollset_set->mu); -} - -static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, - grpc_pollset *pollset) { - size_t i; - gpr_mu_lock(&pollset_set->mu); - for (i = 0; i < pollset_set->pollset_count; i++) { - if (pollset_set->pollsets[i] == pollset) { - pollset_set->pollset_count--; - GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i], - pollset_set->pollsets[pollset_set->pollset_count]); - break; - } - } - gpr_mu_unlock(&pollset_set->mu); - gpr_mu_lock(&pollset->mu); - pollset->pollset_set_count--; - /* check shutdown */ - if (pollset->shutting_down && !pollset->called_shutdown && - !pollset_has_observers(pollset)) { - pollset->called_shutdown = 1; - gpr_mu_unlock(&pollset->mu); - finish_shutdown(exec_ctx, pollset); - } else { - gpr_mu_unlock(&pollset->mu); - } -} - -static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - size_t i, j; - gpr_mu_lock(&bag->mu); - if (bag->pollset_set_count == bag->pollset_set_capacity) { - bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity); - bag->pollset_sets = (grpc_pollset_set **)gpr_realloc( - bag->pollset_sets, - bag->pollset_set_capacity * sizeof(*bag->pollset_sets)); - } - bag->pollset_sets[bag->pollset_set_count++] = item; - for (i = 0, j = 0; i < bag->fd_count; i++) { - if (fd_is_orphaned(bag->fds[i])) { - GRPC_FD_UNREF(bag->fds[i], "pollset_set"); - } else { - pollset_set_add_fd(exec_ctx, item, bag->fds[i]); - bag->fds[j++] = bag->fds[i]; - } - } - bag->fd_count = j; - gpr_mu_unlock(&bag->mu); -} - -static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - size_t i; - gpr_mu_lock(&bag->mu); - for (i = 0; i < bag->pollset_set_count; i++) { - if (bag->pollset_sets[i] == item) { - bag->pollset_set_count--; - GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i], - bag->pollset_sets[bag->pollset_set_count]); - break; - } - } - gpr_mu_unlock(&bag->mu); -} - -static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd) { - size_t i; - gpr_mu_lock(&pollset_set->mu); - if (pollset_set->fd_count == pollset_set->fd_capacity) { - pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity); - pollset_set->fds = (grpc_fd **)gpr_realloc( - pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds)); - } - GRPC_FD_REF(fd, "pollset_set"); - pollset_set->fds[pollset_set->fd_count++] = fd; - for (i = 0; i < pollset_set->pollset_count; i++) { - pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd); - } - for (i = 0; i < pollset_set->pollset_set_count; i++) { - pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd); - } - gpr_mu_unlock(&pollset_set->mu); -} - -static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd) { - size_t i; - gpr_mu_lock(&pollset_set->mu); - for (i = 0; i < pollset_set->fd_count; i++) { - if (pollset_set->fds[i] == fd) { - pollset_set->fd_count--; - GPR_SWAP(grpc_fd *, pollset_set->fds[i], - pollset_set->fds[pollset_set->fd_count]); - GRPC_FD_UNREF(fd, "pollset_set"); - break; - } - } - for (i = 0; i < pollset_set->pollset_set_count; i++) { - pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd); - } - gpr_mu_unlock(&pollset_set->mu); -} - -/******************************************************************************* - * Condition Variable polling extensions - */ - -static void run_poll(void *args); -static void cache_poller_locked(poll_args *args); - -static void cache_insert_locked(poll_args *args) { - uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd), - 0xDEADBEEF); - key = key % poll_cache.size; - if (poll_cache.active_pollers[key]) { - poll_cache.active_pollers[key]->prev = args; - } - args->next = poll_cache.active_pollers[key]; - args->prev = NULL; - poll_cache.active_pollers[key] = args; - poll_cache.count++; -} - -static void init_result(poll_args *pargs) { - pargs->result = (poll_result *)gpr_malloc(sizeof(poll_result)); - gpr_ref_init(&pargs->result->refcount, 1); - pargs->result->watchers = NULL; - pargs->result->watchcount = 0; - pargs->result->fds = - (struct pollfd *)gpr_malloc(sizeof(struct pollfd) * pargs->nfds); - memcpy(pargs->result->fds, pargs->fds, sizeof(struct pollfd) * pargs->nfds); - pargs->result->nfds = pargs->nfds; - pargs->result->retval = 0; - pargs->result->err = 0; - pargs->result->completed = 0; -} - -// Creates a poll_args object for a given arguments to poll(). -// This object may return a poll_args in the cache. -static poll_args *get_poller_locked(struct pollfd *fds, nfds_t count) { - uint32_t key = - gpr_murmur_hash3(fds, count * sizeof(struct pollfd), 0xDEADBEEF); - key = key % poll_cache.size; - poll_args *curr = poll_cache.active_pollers[key]; - while (curr) { - if (curr->nfds == count && - memcmp(curr->fds, fds, count * sizeof(struct pollfd)) == 0) { - gpr_free(fds); - return curr; - } - curr = curr->next; - } - - if (poll_cache.free_pollers) { - poll_args *pargs = poll_cache.free_pollers; - poll_cache.free_pollers = pargs->next; - if (poll_cache.free_pollers) { - poll_cache.free_pollers->prev = NULL; - } - pargs->fds = fds; - pargs->nfds = count; - pargs->next = NULL; - pargs->prev = NULL; - init_result(pargs); - cache_poller_locked(pargs); - return pargs; - } - - poll_args *pargs = (poll_args *)gpr_malloc(sizeof(struct poll_args)); - gpr_cv_init(&pargs->trigger); - pargs->fds = fds; - pargs->nfds = count; - pargs->next = NULL; - pargs->prev = NULL; - pargs->trigger_set = 0; - init_result(pargs); - cache_poller_locked(pargs); - gpr_thd_id t_id; - gpr_thd_options opt = gpr_thd_options_default(); - gpr_ref(&g_cvfds.pollcount); - gpr_thd_options_set_detached(&opt); - GPR_ASSERT(gpr_thd_new(&t_id, &run_poll, pargs, &opt)); - return pargs; -} - -static void cache_delete_locked(poll_args *args) { - if (!args->prev) { - uint32_t key = gpr_murmur_hash3( - args->fds, args->nfds * sizeof(struct pollfd), 0xDEADBEEF); - key = key % poll_cache.size; - GPR_ASSERT(poll_cache.active_pollers[key] == args); - poll_cache.active_pollers[key] = args->next; - } else { - args->prev->next = args->next; - } - - if (args->next) { - args->next->prev = args->prev; - } - - poll_cache.count--; - if (poll_cache.free_pollers) { - poll_cache.free_pollers->prev = args; - } - args->prev = NULL; - args->next = poll_cache.free_pollers; - gpr_free(args->fds); - poll_cache.free_pollers = args; -} - -static void cache_poller_locked(poll_args *args) { - if (poll_cache.count + 1 > poll_cache.size / 2) { - poll_args **old_active_pollers = poll_cache.active_pollers; - poll_cache.size = poll_cache.size * 2; - poll_cache.count = 0; - poll_cache.active_pollers = - (poll_args **)gpr_malloc(sizeof(void *) * poll_cache.size); - for (unsigned int i = 0; i < poll_cache.size; i++) { - poll_cache.active_pollers[i] = NULL; - } - for (unsigned int i = 0; i < poll_cache.size / 2; i++) { - poll_args *curr = old_active_pollers[i]; - poll_args *next = NULL; - while (curr) { - next = curr->next; - cache_insert_locked(curr); - curr = next; - } - } - gpr_free(old_active_pollers); - } - - cache_insert_locked(args); -} - -static void cache_destroy_locked(poll_args *args) { - if (args->next) { - args->next->prev = args->prev; - } - - if (args->prev) { - args->prev->next = args->next; - } else { - poll_cache.free_pollers = args->next; - } - - gpr_free(args); -} - -static void decref_poll_result(poll_result *res) { - if (gpr_unref(&res->refcount)) { - GPR_ASSERT(!res->watchers); - gpr_free(res->fds); - gpr_free(res); - } -} - -void remove_cvn(cv_node **head, cv_node *target) { - if (target->next) { - target->next->prev = target->prev; - } - - if (target->prev) { - target->prev->next = target->next; - } else { - *head = target->next; - } -} - -gpr_timespec thread_grace; - -// Poll in a background thread -static void run_poll(void *args) { - poll_args *pargs = (poll_args *)args; - while (1) { - poll_result *result = pargs->result; - int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS); - gpr_mu_lock(&g_cvfds.mu); - if (retval != 0) { - result->completed = 1; - result->retval = retval; - result->err = errno; - cv_node *watcher = result->watchers; - while (watcher) { - gpr_cv_signal(watcher->cv); - watcher = watcher->next; - } - } - if (result->watchcount == 0 || result->completed) { - cache_delete_locked(pargs); - decref_poll_result(result); - // Leave this polling thread alive for a grace period to do another poll() - // op - gpr_timespec deadline = gpr_now(GPR_CLOCK_REALTIME); - deadline = gpr_time_add(deadline, thread_grace); - pargs->trigger_set = 0; - gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline); - if (!pargs->trigger_set) { - cache_destroy_locked(pargs); - break; - } - } - gpr_mu_unlock(&g_cvfds.mu); - } - - // We still have the lock here - if (gpr_unref(&g_cvfds.pollcount)) { - gpr_cv_signal(&g_cvfds.shutdown_cv); - } - gpr_mu_unlock(&g_cvfds.mu); -} - -// This function overrides poll() to handle condition variable wakeup fds -static int cvfd_poll(struct pollfd *fds, nfds_t nfds, int timeout) { - unsigned int i; - int res, idx; - cv_node *pollcv; - int skip_poll = 0; - nfds_t nsockfds = 0; - poll_result *result = NULL; - gpr_mu_lock(&g_cvfds.mu); - pollcv = (cv_node *)gpr_malloc(sizeof(cv_node)); - pollcv->next = NULL; - gpr_cv pollcv_cv; - gpr_cv_init(&pollcv_cv); - pollcv->cv = &pollcv_cv; - cv_node *fd_cvs = (cv_node *)gpr_malloc(nfds * sizeof(cv_node)); - - for (i = 0; i < nfds; i++) { - fds[i].revents = 0; - if (fds[i].fd < 0 && (fds[i].events & POLLIN)) { - idx = GRPC_FD_TO_IDX(fds[i].fd); - fd_cvs[i].cv = &pollcv_cv; - fd_cvs[i].prev = NULL; - fd_cvs[i].next = g_cvfds.cvfds[idx].cvs; - if (g_cvfds.cvfds[idx].cvs) { - g_cvfds.cvfds[idx].cvs->prev = &(fd_cvs[i]); - } - g_cvfds.cvfds[idx].cvs = &(fd_cvs[i]); - // Don't bother polling if a wakeup fd is ready - if (g_cvfds.cvfds[idx].is_set) { - skip_poll = 1; - } - } else if (fds[i].fd >= 0) { - nsockfds++; - } - } - - gpr_timespec deadline = gpr_now(GPR_CLOCK_REALTIME); - if (timeout < 0) { - deadline = gpr_inf_future(GPR_CLOCK_REALTIME); - } else { - deadline = - gpr_time_add(deadline, gpr_time_from_millis(timeout, GPR_TIMESPAN)); - } - - res = 0; - if (!skip_poll && nsockfds > 0) { - struct pollfd *pollfds = - (struct pollfd *)gpr_malloc(sizeof(struct pollfd) * nsockfds); - idx = 0; - for (i = 0; i < nfds; i++) { - if (fds[i].fd >= 0) { - pollfds[idx].fd = fds[i].fd; - pollfds[idx].events = fds[i].events; - pollfds[idx].revents = 0; - idx++; - } - } - poll_args *pargs = get_poller_locked(pollfds, nsockfds); - result = pargs->result; - pollcv->next = result->watchers; - pollcv->prev = NULL; - if (result->watchers) { - result->watchers->prev = pollcv; - } - result->watchers = pollcv; - result->watchcount++; - gpr_ref(&result->refcount); - - pargs->trigger_set = 1; - gpr_cv_signal(&pargs->trigger); - gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); - res = result->retval; - errno = result->err; - result->watchcount--; - remove_cvn(&result->watchers, pollcv); - } else if (!skip_poll) { - gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); - } - - idx = 0; - for (i = 0; i < nfds; i++) { - if (fds[i].fd < 0 && (fds[i].events & POLLIN)) { - remove_cvn(&g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].cvs, &(fd_cvs[i])); - if (g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].is_set) { - fds[i].revents = POLLIN; - if (res >= 0) res++; - } - } else if (!skip_poll && fds[i].fd >= 0 && result->completed) { - fds[i].revents = result->fds[idx].revents; - idx++; - } - } - - gpr_free(fd_cvs); - gpr_free(pollcv); - if (result) { - decref_poll_result(result); - } - - gpr_mu_unlock(&g_cvfds.mu); - - return res; -} - -static void global_cv_fd_table_init() { - gpr_mu_init(&g_cvfds.mu); - gpr_mu_lock(&g_cvfds.mu); - gpr_cv_init(&g_cvfds.shutdown_cv); - gpr_ref_init(&g_cvfds.pollcount, 1); - g_cvfds.size = CV_DEFAULT_TABLE_SIZE; - g_cvfds.cvfds = - (fd_node *)gpr_malloc(sizeof(fd_node) * CV_DEFAULT_TABLE_SIZE); - g_cvfds.free_fds = NULL; - thread_grace = gpr_time_from_millis(POLLCV_THREAD_GRACE_MS, GPR_TIMESPAN); - for (int i = 0; i < CV_DEFAULT_TABLE_SIZE; i++) { - g_cvfds.cvfds[i].is_set = 0; - g_cvfds.cvfds[i].cvs = NULL; - g_cvfds.cvfds[i].next_free = g_cvfds.free_fds; - g_cvfds.free_fds = &g_cvfds.cvfds[i]; - } - // Override the poll function with one that supports cvfds - g_cvfds.poll = grpc_poll_function; - grpc_poll_function = &cvfd_poll; - - // Initialize the cache - poll_cache.size = 32; - poll_cache.count = 0; - poll_cache.free_pollers = NULL; - poll_cache.active_pollers = (poll_args **)gpr_malloc(sizeof(void *) * 32); - for (unsigned int i = 0; i < poll_cache.size; i++) { - poll_cache.active_pollers[i] = NULL; - } - - gpr_mu_unlock(&g_cvfds.mu); -} - -static void global_cv_fd_table_shutdown() { - gpr_mu_lock(&g_cvfds.mu); - // Attempt to wait for all abandoned poll() threads to terminate - // Not doing so will result in reported memory leaks - if (!gpr_unref(&g_cvfds.pollcount)) { - int res = gpr_cv_wait(&g_cvfds.shutdown_cv, &g_cvfds.mu, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), - gpr_time_from_seconds(3, GPR_TIMESPAN))); - GPR_ASSERT(res == 0); - } - gpr_cv_destroy(&g_cvfds.shutdown_cv); - grpc_poll_function = g_cvfds.poll; - gpr_free(g_cvfds.cvfds); - - gpr_free(poll_cache.active_pollers); - - gpr_mu_unlock(&g_cvfds.mu); - gpr_mu_destroy(&g_cvfds.mu); -} - -/******************************************************************************* - * event engine binding - */ - -static void shutdown_engine(void) { - pollset_global_shutdown(); - if (grpc_cv_wakeup_fds_enabled()) { - global_cv_fd_table_shutdown(); - } -} - -static const grpc_event_engine_vtable vtable = { - sizeof(grpc_pollset), - - fd_create, - fd_wrapped_fd, - fd_orphan, - fd_shutdown, - fd_notify_on_read, - fd_notify_on_write, - fd_is_shutdown, - fd_get_read_notifier_pollset, - - pollset_init, - pollset_shutdown, - pollset_destroy, - pollset_work, - pollset_kick, - pollset_add_fd, - - pollset_set_create, - pollset_set_destroy, - pollset_set_add_pollset, - pollset_set_del_pollset, - pollset_set_add_pollset_set, - pollset_set_del_pollset_set, - pollset_set_add_fd, - pollset_set_del_fd, - - shutdown_engine, -}; - -const grpc_event_engine_vtable *grpc_init_poll_posix(bool explicit_request) { - if (!grpc_has_wakeup_fd()) { - return NULL; - } - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - return NULL; - } - return &vtable; -} - -const grpc_event_engine_vtable *grpc_init_poll_cv_posix(bool explicit_request) { - global_cv_fd_table_init(); - grpc_enable_cv_wakeup_fds(1); - if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { - global_cv_fd_table_shutdown(); - grpc_enable_cv_wakeup_fds(0); - return NULL; - } - return &vtable; -} - -#endif diff --git a/src/core/lib/iomgr/ev_poll_posix.cc b/src/core/lib/iomgr/ev_poll_posix.cc new file mode 100644 index 0000000000..e170702dca --- /dev/null +++ b/src/core/lib/iomgr/ev_poll_posix.cc @@ -0,0 +1,1746 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/ev_poll_posix.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/wakeup_fd_cv.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/murmur_hash.h" + +#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) + +/******************************************************************************* + * FD declarations + */ + +typedef struct grpc_fd_watcher { + struct grpc_fd_watcher *next; + struct grpc_fd_watcher *prev; + grpc_pollset *pollset; + grpc_pollset_worker *worker; + grpc_fd *fd; +} grpc_fd_watcher; + +struct grpc_fd { + int fd; + /* refst format: + bit0: 1=active/0=orphaned + bit1-n: refcount + meaning that mostly we ref by two to avoid altering the orphaned bit, + and just unref by 1 when we're ready to flag the object as orphaned */ + gpr_atm refst; + + gpr_mu mu; + int shutdown; + int closed; + int released; + grpc_error *shutdown_error; + + /* The watcher list. + + The following watcher related fields are protected by watcher_mu. + + An fd_watcher is an ephemeral object created when an fd wants to + begin polling, and destroyed after the poll. + + It denotes the fd's interest in whether to read poll or write poll + or both or neither on this fd. + + If a watcher is asked to poll for reads or writes, the read_watcher + or write_watcher fields are set respectively. A watcher may be asked + to poll for both, in which case both fields will be set. + + read_watcher and write_watcher may be NULL if no watcher has been + asked to poll for reads or writes. + + If an fd_watcher is not asked to poll for reads or writes, it's added + to a linked list of inactive watchers, rooted at inactive_watcher_root. + If at a later time there becomes need of a poller to poll, one of + the inactive pollers may be kicked out of their poll loops to take + that responsibility. */ + grpc_fd_watcher inactive_watcher_root; + grpc_fd_watcher *read_watcher; + grpc_fd_watcher *write_watcher; + + grpc_closure *read_closure; + grpc_closure *write_closure; + + grpc_closure *on_done_closure; + + grpc_iomgr_object iomgr_object; + + /* The pollset that last noticed and notified that the fd is readable */ + grpc_pollset *read_notifier_pollset; +}; + +/* Begin polling on an fd. + Registers that the given pollset is interested in this fd - so that if read + or writability interest changes, the pollset can be kicked to pick up that + new interest. + Return value is: + (fd_needs_read? read_mask : 0) | (fd_needs_write? write_mask : 0) + i.e. a combination of read_mask and write_mask determined by the fd's current + interest in said events. + Polling strategies that do not need to alter their behavior depending on the + fd's current interest (such as epoll) do not need to call this function. + MUST NOT be called with a pollset lock taken */ +static uint32_t fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, + grpc_pollset_worker *worker, uint32_t read_mask, + uint32_t write_mask, grpc_fd_watcher *rec); +/* Complete polling previously started with fd_begin_poll + MUST NOT be called with a pollset lock taken + if got_read or got_write are 1, also does the become_{readable,writable} as + appropriate. */ +static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *rec, + int got_read, int got_write, + grpc_pollset *read_notifier_pollset); + +/* Return 1 if this fd is orphaned, 0 otherwise */ +static bool fd_is_orphaned(grpc_fd *fd); + +#ifndef NDEBUG +static void fd_ref(grpc_fd *fd, const char *reason, const char *file, int line); +static void fd_unref(grpc_fd *fd, const char *reason, const char *file, + int line); +#define GRPC_FD_REF(fd, reason) fd_ref(fd, reason, __FILE__, __LINE__) +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd, reason, __FILE__, __LINE__) +#else +static void fd_ref(grpc_fd *fd); +static void fd_unref(grpc_fd *fd); +#define GRPC_FD_REF(fd, reason) fd_ref(fd) +#define GRPC_FD_UNREF(fd, reason) fd_unref(fd) +#endif + +#define CLOSURE_NOT_READY ((grpc_closure *)0) +#define CLOSURE_READY ((grpc_closure *)1) + +/******************************************************************************* + * pollset declarations + */ + +typedef struct grpc_cached_wakeup_fd { + grpc_wakeup_fd fd; + struct grpc_cached_wakeup_fd *next; +} grpc_cached_wakeup_fd; + +struct grpc_pollset_worker { + grpc_cached_wakeup_fd *wakeup_fd; + int reevaluate_polling_on_wakeup; + int kicked_specifically; + struct grpc_pollset_worker *next; + struct grpc_pollset_worker *prev; +}; + +struct grpc_pollset { + gpr_mu mu; + grpc_pollset_worker root_worker; + int shutting_down; + int called_shutdown; + int kicked_without_pollers; + grpc_closure *shutdown_done; + grpc_closure_list idle_jobs; + int pollset_set_count; + /* all polled fds */ + size_t fd_count; + size_t fd_capacity; + grpc_fd **fds; + /* Local cache of eventfds for workers */ + grpc_cached_wakeup_fd *local_wakeup_cache; +}; + +/* Add an fd to a pollset */ +static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + struct grpc_fd *fd); + +static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd); + +/* Convert a timespec to milliseconds: + - very small or negative poll times are clamped to zero to do a + non-blocking poll (which becomes spin polling) + - other small values are rounded up to one millisecond + - longer than a millisecond polls are rounded up to the next nearest + millisecond to avoid spinning + - infinite timeouts are converted to -1 */ +static int poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now); + +/* Allow kick to wakeup the currently polling worker */ +#define GRPC_POLLSET_CAN_KICK_SELF 1 +/* Force the wakee to repoll when awoken */ +#define GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP 2 +/* As per pollset_kick, with an extended set of flags (defined above) + -- mostly for fd_posix's use. */ +static grpc_error *pollset_kick_ext(grpc_exec_ctx *exec_ctx, grpc_pollset *p, + grpc_pollset_worker *specific_worker, + uint32_t flags) GRPC_MUST_USE_RESULT; + +/* Return 1 if the pollset has active threads in pollset_work (pollset must + * be locked) */ +static bool pollset_has_workers(grpc_pollset *pollset); + +/******************************************************************************* + * pollset_set definitions + */ + +struct grpc_pollset_set { + gpr_mu mu; + + size_t pollset_count; + size_t pollset_capacity; + grpc_pollset **pollsets; + + size_t pollset_set_count; + size_t pollset_set_capacity; + struct grpc_pollset_set **pollset_sets; + + size_t fd_count; + size_t fd_capacity; + grpc_fd **fds; +}; + +/******************************************************************************* + * condition variable polling definitions + */ + +#define POLLCV_THREAD_GRACE_MS 1000 +#define CV_POLL_PERIOD_MS 1000 +#define CV_DEFAULT_TABLE_SIZE 16 + +typedef struct poll_result { + gpr_refcount refcount; + cv_node *watchers; + int watchcount; + struct pollfd *fds; + nfds_t nfds; + int retval; + int err; + int completed; +} poll_result; + +typedef struct poll_args { + gpr_cv trigger; + int trigger_set; + struct pollfd *fds; + nfds_t nfds; + poll_result *result; + struct poll_args *next; + struct poll_args *prev; +} poll_args; + +// This is a 2-tiered cache, we mantain a hash table +// of active poll calls, so we can wait on the result +// of that call. We also maintain a freelist of inactive +// poll threads. +typedef struct poll_hash_table { + poll_args *free_pollers; + poll_args **active_pollers; + unsigned int size; + unsigned int count; +} poll_hash_table; + +poll_hash_table poll_cache; +cv_fd_table g_cvfds; + +/******************************************************************************* + * fd_posix.c + */ + +#ifndef NDEBUG +#define REF_BY(fd, n, reason) ref_by(fd, n, reason, __FILE__, __LINE__) +#define UNREF_BY(fd, n, reason) unref_by(fd, n, reason, __FILE__, __LINE__) +static void ref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, + "FD %d %p ref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", + fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) + n, reason, file, line); + } +#else +#define REF_BY(fd, n, reason) ref_by(fd, n) +#define UNREF_BY(fd, n, reason) unref_by(fd, n) +static void ref_by(grpc_fd *fd, int n) { +#endif + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&fd->refst, n) > 0); +} + +#ifndef NDEBUG +static void unref_by(grpc_fd *fd, int n, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_trace_fd_refcount)) { + gpr_log(GPR_DEBUG, + "FD %d %p unref %d %" PRIdPTR " -> %" PRIdPTR " [%s; %s:%d]", + fd->fd, fd, n, gpr_atm_no_barrier_load(&fd->refst), + gpr_atm_no_barrier_load(&fd->refst) - n, reason, file, line); + } +#else +static void unref_by(grpc_fd *fd, int n) { +#endif + gpr_atm old = gpr_atm_full_fetch_add(&fd->refst, -n); + if (old == n) { + gpr_mu_destroy(&fd->mu); + grpc_iomgr_unregister_object(&fd->iomgr_object); + if (fd->shutdown) GRPC_ERROR_UNREF(fd->shutdown_error); + gpr_free(fd); + } else { + GPR_ASSERT(old > n); + } +} + +static grpc_fd *fd_create(int fd, const char *name) { + grpc_fd *r = (grpc_fd *)gpr_malloc(sizeof(*r)); + gpr_mu_init(&r->mu); + gpr_atm_rel_store(&r->refst, 1); + r->shutdown = 0; + r->read_closure = CLOSURE_NOT_READY; + r->write_closure = CLOSURE_NOT_READY; + r->fd = fd; + r->inactive_watcher_root.next = r->inactive_watcher_root.prev = + &r->inactive_watcher_root; + r->read_watcher = r->write_watcher = NULL; + r->on_done_closure = NULL; + r->closed = 0; + r->released = 0; + r->read_notifier_pollset = NULL; + + char *name2; + gpr_asprintf(&name2, "%s fd=%d", name, fd); + grpc_iomgr_register_object(&r->iomgr_object, name2); + gpr_free(name2); + return r; +} + +static bool fd_is_orphaned(grpc_fd *fd) { + return (gpr_atm_acq_load(&fd->refst) & 1) == 0; +} + +/* Return the read-notifier pollset */ +static grpc_pollset *fd_get_read_notifier_pollset(grpc_exec_ctx *exec_ctx, + grpc_fd *fd) { + grpc_pollset *notifier = NULL; + + gpr_mu_lock(&fd->mu); + notifier = fd->read_notifier_pollset; + gpr_mu_unlock(&fd->mu); + + return notifier; +} + +static grpc_error *pollset_kick_locked(grpc_exec_ctx *exec_ctx, + grpc_fd_watcher *watcher) { + gpr_mu_lock(&watcher->pollset->mu); + GPR_ASSERT(watcher->worker); + grpc_error *err = + pollset_kick_ext(exec_ctx, watcher->pollset, watcher->worker, + GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP); + gpr_mu_unlock(&watcher->pollset->mu); + return err; +} + +static void maybe_wake_one_watcher_locked(grpc_exec_ctx *exec_ctx, + grpc_fd *fd) { + if (fd->inactive_watcher_root.next != &fd->inactive_watcher_root) { + pollset_kick_locked(exec_ctx, fd->inactive_watcher_root.next); + } else if (fd->read_watcher) { + pollset_kick_locked(exec_ctx, fd->read_watcher); + } else if (fd->write_watcher) { + pollset_kick_locked(exec_ctx, fd->write_watcher); + } +} + +static void wake_all_watchers_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + grpc_fd_watcher *watcher; + for (watcher = fd->inactive_watcher_root.next; + watcher != &fd->inactive_watcher_root; watcher = watcher->next) { + pollset_kick_locked(exec_ctx, watcher); + } + if (fd->read_watcher) { + pollset_kick_locked(exec_ctx, fd->read_watcher); + } + if (fd->write_watcher && fd->write_watcher != fd->read_watcher) { + pollset_kick_locked(exec_ctx, fd->write_watcher); + } +} + +static int has_watchers(grpc_fd *fd) { + return fd->read_watcher != NULL || fd->write_watcher != NULL || + fd->inactive_watcher_root.next != &fd->inactive_watcher_root; +} + +static void close_fd_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd) { + fd->closed = 1; + if (!fd->released) { + close(fd->fd); + } + GRPC_CLOSURE_SCHED(exec_ctx, fd->on_done_closure, GRPC_ERROR_NONE); +} + +static int fd_wrapped_fd(grpc_fd *fd) { + if (fd->released || fd->closed) { + return -1; + } else { + return fd->fd; + } +} + +static void fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *on_done, int *release_fd, + bool already_closed, const char *reason) { + fd->on_done_closure = on_done; + fd->released = release_fd != NULL; + if (release_fd != NULL) { + *release_fd = fd->fd; + fd->released = true; + } else if (already_closed) { + fd->released = true; + } + gpr_mu_lock(&fd->mu); + REF_BY(fd, 1, reason); /* remove active status, but keep referenced */ + if (!has_watchers(fd)) { + close_fd_locked(exec_ctx, fd); + } else { + wake_all_watchers_locked(exec_ctx, fd); + } + gpr_mu_unlock(&fd->mu); + UNREF_BY(fd, 2, reason); /* drop the reference */ +} + +/* increment refcount by two to avoid changing the orphan bit */ +#ifndef NDEBUG +static void fd_ref(grpc_fd *fd, const char *reason, const char *file, + int line) { + ref_by(fd, 2, reason, file, line); +} + +static void fd_unref(grpc_fd *fd, const char *reason, const char *file, + int line) { + unref_by(fd, 2, reason, file, line); +} +#else +static void fd_ref(grpc_fd *fd) { ref_by(fd, 2); } + +static void fd_unref(grpc_fd *fd) { unref_by(fd, 2); } +#endif + +static grpc_error *fd_shutdown_error(grpc_fd *fd) { + if (!fd->shutdown) { + return GRPC_ERROR_NONE; + } else { + return GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "FD shutdown", &fd->shutdown_error, 1); + } +} + +static void notify_on_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure **st, grpc_closure *closure) { + if (fd->shutdown) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("FD shutdown")); + } else if (*st == CLOSURE_NOT_READY) { + /* not ready ==> switch to a waiting state by setting the closure */ + *st = closure; + } else if (*st == CLOSURE_READY) { + /* already ready ==> queue the closure to run immediately */ + *st = CLOSURE_NOT_READY; + GRPC_CLOSURE_SCHED(exec_ctx, closure, fd_shutdown_error(fd)); + maybe_wake_one_watcher_locked(exec_ctx, fd); + } else { + /* upcallptr was set to a different closure. This is an error! */ + gpr_log(GPR_ERROR, + "User called a notify_on function with a previous callback still " + "pending"); + abort(); + } +} + +/* returns 1 if state becomes not ready */ +static int set_ready_locked(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure **st) { + if (*st == CLOSURE_READY) { + /* duplicate ready ==> ignore */ + return 0; + } else if (*st == CLOSURE_NOT_READY) { + /* not ready, and not waiting ==> flag ready */ + *st = CLOSURE_READY; + return 0; + } else { + /* waiting ==> queue closure */ + GRPC_CLOSURE_SCHED(exec_ctx, *st, fd_shutdown_error(fd)); + *st = CLOSURE_NOT_READY; + return 1; + } +} + +static void set_read_notifier_pollset_locked( + grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_pollset *read_notifier_pollset) { + fd->read_notifier_pollset = read_notifier_pollset; +} + +static void fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { + gpr_mu_lock(&fd->mu); + /* only shutdown once */ + if (!fd->shutdown) { + fd->shutdown = 1; + fd->shutdown_error = why; + /* signal read/write closed to OS so that future operations fail */ + shutdown(fd->fd, SHUT_RDWR); + set_ready_locked(exec_ctx, fd, &fd->read_closure); + set_ready_locked(exec_ctx, fd, &fd->write_closure); + } else { + GRPC_ERROR_UNREF(why); + } + gpr_mu_unlock(&fd->mu); +} + +static bool fd_is_shutdown(grpc_fd *fd) { + gpr_mu_lock(&fd->mu); + bool r = fd->shutdown; + gpr_mu_unlock(&fd->mu); + return r; +} + +static void fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + gpr_mu_lock(&fd->mu); + notify_on_locked(exec_ctx, fd, &fd->read_closure, closure); + gpr_mu_unlock(&fd->mu); +} + +static void fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + gpr_mu_lock(&fd->mu); + notify_on_locked(exec_ctx, fd, &fd->write_closure, closure); + gpr_mu_unlock(&fd->mu); +} + +static uint32_t fd_begin_poll(grpc_fd *fd, grpc_pollset *pollset, + grpc_pollset_worker *worker, uint32_t read_mask, + uint32_t write_mask, grpc_fd_watcher *watcher) { + uint32_t mask = 0; + grpc_closure *cur; + int requested; + /* keep track of pollers that have requested our events, in case they change + */ + GRPC_FD_REF(fd, "poll"); + + gpr_mu_lock(&fd->mu); + + /* if we are shutdown, then don't add to the watcher set */ + if (fd->shutdown) { + watcher->fd = NULL; + watcher->pollset = NULL; + watcher->worker = NULL; + gpr_mu_unlock(&fd->mu); + GRPC_FD_UNREF(fd, "poll"); + return 0; + } + + /* if there is nobody polling for read, but we need to, then start doing so */ + cur = fd->read_closure; + requested = cur != CLOSURE_READY; + if (read_mask && fd->read_watcher == NULL && requested) { + fd->read_watcher = watcher; + mask |= read_mask; + } + /* if there is nobody polling for write, but we need to, then start doing so + */ + cur = fd->write_closure; + requested = cur != CLOSURE_READY; + if (write_mask && fd->write_watcher == NULL && requested) { + fd->write_watcher = watcher; + mask |= write_mask; + } + /* if not polling, remember this watcher in case we need someone to later */ + if (mask == 0 && worker != NULL) { + watcher->next = &fd->inactive_watcher_root; + watcher->prev = watcher->next->prev; + watcher->next->prev = watcher->prev->next = watcher; + } + watcher->pollset = pollset; + watcher->worker = worker; + watcher->fd = fd; + gpr_mu_unlock(&fd->mu); + + return mask; +} + +static void fd_end_poll(grpc_exec_ctx *exec_ctx, grpc_fd_watcher *watcher, + int got_read, int got_write, + grpc_pollset *read_notifier_pollset) { + int was_polling = 0; + int kick = 0; + grpc_fd *fd = watcher->fd; + + if (fd == NULL) { + return; + } + + gpr_mu_lock(&fd->mu); + + if (watcher == fd->read_watcher) { + /* remove read watcher, kick if we still need a read */ + was_polling = 1; + if (!got_read) { + kick = 1; + } + fd->read_watcher = NULL; + } + if (watcher == fd->write_watcher) { + /* remove write watcher, kick if we still need a write */ + was_polling = 1; + if (!got_write) { + kick = 1; + } + fd->write_watcher = NULL; + } + if (!was_polling && watcher->worker != NULL) { + /* remove from inactive list */ + watcher->next->prev = watcher->prev; + watcher->prev->next = watcher->next; + } + if (got_read) { + if (set_ready_locked(exec_ctx, fd, &fd->read_closure)) { + kick = 1; + } + if (read_notifier_pollset != NULL) { + set_read_notifier_pollset_locked(exec_ctx, fd, read_notifier_pollset); + } + } + if (got_write) { + if (set_ready_locked(exec_ctx, fd, &fd->write_closure)) { + kick = 1; + } + } + if (kick) { + maybe_wake_one_watcher_locked(exec_ctx, fd); + } + if (fd_is_orphaned(fd) && !has_watchers(fd) && !fd->closed) { + close_fd_locked(exec_ctx, fd); + } + gpr_mu_unlock(&fd->mu); + + GRPC_FD_UNREF(fd, "poll"); +} + +/******************************************************************************* + * pollset_posix.c + */ + +GPR_TLS_DECL(g_current_thread_poller); +GPR_TLS_DECL(g_current_thread_worker); + +static void remove_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev->next = worker->next; + worker->next->prev = worker->prev; +} + +static bool pollset_has_workers(grpc_pollset *p) { + return p->root_worker.next != &p->root_worker; +} + +static bool pollset_in_pollset_sets(grpc_pollset *p) { + return p->pollset_set_count; +} + +static bool pollset_has_observers(grpc_pollset *p) { + return pollset_has_workers(p) || pollset_in_pollset_sets(p); +} + +static grpc_pollset_worker *pop_front_worker(grpc_pollset *p) { + if (pollset_has_workers(p)) { + grpc_pollset_worker *w = p->root_worker.next; + remove_worker(p, w); + return w; + } else { + return NULL; + } +} + +static void push_back_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->next = &p->root_worker; + worker->prev = worker->next->prev; + worker->prev->next = worker->next->prev = worker; +} + +static void push_front_worker(grpc_pollset *p, grpc_pollset_worker *worker) { + worker->prev = &p->root_worker; + worker->next = worker->prev->next; + worker->prev->next = worker->next->prev = worker; +} + +static void kick_append_error(grpc_error **composite, grpc_error *error) { + if (error == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Kick Failure"); + } + *composite = grpc_error_add_child(*composite, error); +} + +static grpc_error *pollset_kick_ext(grpc_exec_ctx *exec_ctx, grpc_pollset *p, + grpc_pollset_worker *specific_worker, + uint32_t flags) { + GPR_TIMER_BEGIN("pollset_kick_ext", 0); + grpc_error *error = GRPC_ERROR_NONE; + GRPC_STATS_INC_POLLSET_KICK(exec_ctx); + + /* pollset->mu already held */ + if (specific_worker != NULL) { + if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { + GPR_TIMER_BEGIN("pollset_kick_ext.broadcast", 0); + GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); + for (specific_worker = p->root_worker.next; + specific_worker != &p->root_worker; + specific_worker = specific_worker->next) { + kick_append_error( + &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } + p->kicked_without_pollers = true; + GPR_TIMER_END("pollset_kick_ext.broadcast", 0); + } else if (gpr_tls_get(&g_current_thread_worker) != + (intptr_t)specific_worker) { + GPR_TIMER_MARK("different_thread_worker", 0); + if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { + specific_worker->reevaluate_polling_on_wakeup = true; + } + specific_worker->kicked_specifically = true; + kick_append_error(&error, + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } else if ((flags & GRPC_POLLSET_CAN_KICK_SELF) != 0) { + GPR_TIMER_MARK("kick_yoself", 0); + if ((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) != 0) { + specific_worker->reevaluate_polling_on_wakeup = true; + } + specific_worker->kicked_specifically = true; + kick_append_error(&error, + grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } + } else if (gpr_tls_get(&g_current_thread_poller) != (intptr_t)p) { + GPR_ASSERT((flags & GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) == 0); + GPR_TIMER_MARK("kick_anonymous", 0); + specific_worker = pop_front_worker(p); + if (specific_worker != NULL) { + if (gpr_tls_get(&g_current_thread_worker) == (intptr_t)specific_worker) { + GPR_TIMER_MARK("kick_anonymous_not_self", 0); + push_back_worker(p, specific_worker); + specific_worker = pop_front_worker(p); + if ((flags & GRPC_POLLSET_CAN_KICK_SELF) == 0 && + gpr_tls_get(&g_current_thread_worker) == + (intptr_t)specific_worker) { + push_back_worker(p, specific_worker); + specific_worker = NULL; + } + } + if (specific_worker != NULL) { + GPR_TIMER_MARK("finally_kick", 0); + push_back_worker(p, specific_worker); + kick_append_error( + &error, grpc_wakeup_fd_wakeup(&specific_worker->wakeup_fd->fd)); + } + } else { + GPR_TIMER_MARK("kicked_no_pollers", 0); + p->kicked_without_pollers = true; + } + } + + GPR_TIMER_END("pollset_kick_ext", 0); + GRPC_LOG_IF_ERROR("pollset_kick_ext", GRPC_ERROR_REF(error)); + return error; +} + +static grpc_error *pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *p, + grpc_pollset_worker *specific_worker) { + return pollset_kick_ext(exec_ctx, p, specific_worker, 0); +} + +/* global state management */ + +static grpc_error *pollset_global_init(void) { + gpr_tls_init(&g_current_thread_poller); + gpr_tls_init(&g_current_thread_worker); + return GRPC_ERROR_NONE; +} + +static void pollset_global_shutdown(void) { + gpr_tls_destroy(&g_current_thread_poller); + gpr_tls_destroy(&g_current_thread_worker); +} + +/* main interface */ + +static void pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + gpr_mu_init(&pollset->mu); + *mu = &pollset->mu; + pollset->root_worker.next = pollset->root_worker.prev = &pollset->root_worker; + pollset->shutting_down = 0; + pollset->called_shutdown = 0; + pollset->kicked_without_pollers = 0; + pollset->idle_jobs.head = pollset->idle_jobs.tail = NULL; + pollset->local_wakeup_cache = NULL; + pollset->kicked_without_pollers = 0; + pollset->fd_count = 0; + pollset->fd_capacity = 0; + pollset->fds = NULL; + pollset->pollset_set_count = 0; +} + +static void pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + GPR_ASSERT(!pollset_has_workers(pollset)); + GPR_ASSERT(pollset->idle_jobs.head == pollset->idle_jobs.tail); + while (pollset->local_wakeup_cache) { + grpc_cached_wakeup_fd *next = pollset->local_wakeup_cache->next; + grpc_wakeup_fd_destroy(&pollset->local_wakeup_cache->fd); + gpr_free(pollset->local_wakeup_cache); + pollset->local_wakeup_cache = next; + } + gpr_free(pollset->fds); + gpr_mu_destroy(&pollset->mu); +} + +static void pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_fd *fd) { + gpr_mu_lock(&pollset->mu); + size_t i; + /* TODO(ctiller): this is O(num_fds^2); maybe switch to a hash set here */ + for (i = 0; i < pollset->fd_count; i++) { + if (pollset->fds[i] == fd) goto exit; + } + if (pollset->fd_count == pollset->fd_capacity) { + pollset->fd_capacity = + GPR_MAX(pollset->fd_capacity + 8, pollset->fd_count * 3 / 2); + pollset->fds = (grpc_fd **)gpr_realloc( + pollset->fds, sizeof(grpc_fd *) * pollset->fd_capacity); + } + pollset->fds[pollset->fd_count++] = fd; + GRPC_FD_REF(fd, "multipoller"); + pollset_kick(exec_ctx, pollset, NULL); +exit: + gpr_mu_unlock(&pollset->mu); +} + +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + GPR_ASSERT(grpc_closure_list_empty(pollset->idle_jobs)); + size_t i; + for (i = 0; i < pollset->fd_count; i++) { + GRPC_FD_UNREF(pollset->fds[i], "multipoller"); + } + pollset->fd_count = 0; + GRPC_CLOSURE_SCHED(exec_ctx, pollset->shutdown_done, GRPC_ERROR_NONE); +} + +static void work_combine_error(grpc_error **composite, grpc_error *error) { + if (error == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("pollset_work"); + } + *composite = grpc_error_add_child(*composite, error); +} + +static grpc_error *pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + grpc_pollset_worker worker; + if (worker_hdl) *worker_hdl = &worker; + grpc_error *error = GRPC_ERROR_NONE; + + /* Avoid malloc for small number of elements. */ + enum { inline_elements = 96 }; + struct pollfd pollfd_space[inline_elements]; + struct grpc_fd_watcher watcher_space[inline_elements]; + + /* pollset->mu already held */ + int added_worker = 0; + int locked = 1; + int queued_work = 0; + int keep_polling = 0; + GPR_TIMER_BEGIN("pollset_work", 0); + /* this must happen before we (potentially) drop pollset->mu */ + worker.next = worker.prev = NULL; + worker.reevaluate_polling_on_wakeup = 0; + if (pollset->local_wakeup_cache != NULL) { + worker.wakeup_fd = pollset->local_wakeup_cache; + pollset->local_wakeup_cache = worker.wakeup_fd->next; + } else { + worker.wakeup_fd = + (grpc_cached_wakeup_fd *)gpr_malloc(sizeof(*worker.wakeup_fd)); + error = grpc_wakeup_fd_init(&worker.wakeup_fd->fd); + if (error != GRPC_ERROR_NONE) { + GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); + return error; + } + } + worker.kicked_specifically = 0; + /* If there's work waiting for the pollset to be idle, and the + pollset is idle, then do that work */ + if (!pollset_has_workers(pollset) && + !grpc_closure_list_empty(pollset->idle_jobs)) { + GPR_TIMER_MARK("pollset_work.idle_jobs", 0); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pollset->idle_jobs); + goto done; + } + /* If we're shutting down then we don't execute any extended work */ + if (pollset->shutting_down) { + GPR_TIMER_MARK("pollset_work.shutting_down", 0); + goto done; + } + /* Start polling, and keep doing so while we're being asked to + re-evaluate our pollers (this allows poll() based pollers to + ensure they don't miss wakeups) */ + keep_polling = 1; + gpr_tls_set(&g_current_thread_poller, (intptr_t)pollset); + while (keep_polling) { + keep_polling = 0; + if (!pollset->kicked_without_pollers) { + if (!added_worker) { + push_front_worker(pollset, &worker); + added_worker = 1; + gpr_tls_set(&g_current_thread_worker, (intptr_t)&worker); + } + GPR_TIMER_BEGIN("maybe_work_and_unlock", 0); +#define POLLOUT_CHECK (POLLOUT | POLLHUP | POLLERR) +#define POLLIN_CHECK (POLLIN | POLLHUP | POLLERR) + + int timeout; + int r; + size_t i, fd_count; + nfds_t pfd_count; + grpc_fd_watcher *watchers; + struct pollfd *pfds; + + timeout = poll_deadline_to_millis_timeout(deadline, now); + + if (pollset->fd_count + 2 <= inline_elements) { + pfds = pollfd_space; + watchers = watcher_space; + } else { + /* Allocate one buffer to hold both pfds and watchers arrays */ + const size_t pfd_size = sizeof(*pfds) * (pollset->fd_count + 2); + const size_t watch_size = sizeof(*watchers) * (pollset->fd_count + 2); + void *buf = gpr_malloc(pfd_size + watch_size); + pfds = (struct pollfd *)buf; + watchers = (grpc_fd_watcher *)(void *)((char *)buf + pfd_size); + } + + fd_count = 0; + pfd_count = 1; + pfds[0].fd = GRPC_WAKEUP_FD_GET_READ_FD(&worker.wakeup_fd->fd); + pfds[0].events = POLLIN; + pfds[0].revents = 0; + for (i = 0; i < pollset->fd_count; i++) { + if (fd_is_orphaned(pollset->fds[i])) { + GRPC_FD_UNREF(pollset->fds[i], "multipoller"); + } else { + pollset->fds[fd_count++] = pollset->fds[i]; + watchers[pfd_count].fd = pollset->fds[i]; + GRPC_FD_REF(watchers[pfd_count].fd, "multipoller_start"); + pfds[pfd_count].fd = pollset->fds[i]->fd; + pfds[pfd_count].revents = 0; + pfd_count++; + } + } + pollset->fd_count = fd_count; + gpr_mu_unlock(&pollset->mu); + + for (i = 1; i < pfd_count; i++) { + grpc_fd *fd = watchers[i].fd; + pfds[i].events = (short)fd_begin_poll(fd, pollset, &worker, POLLIN, + POLLOUT, &watchers[i]); + GRPC_FD_UNREF(fd, "multipoller_start"); + } + + /* TODO(vpai): Consider first doing a 0 timeout poll here to avoid + even going into the blocking annotation if possible */ + GRPC_SCHEDULING_START_BLOCKING_REGION; + GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); + r = grpc_poll_function(pfds, pfd_count, timeout); + GRPC_SCHEDULING_END_BLOCKING_REGION; + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "%p poll=%d", pollset, r); + } + + if (r < 0) { + if (errno != EINTR) { + work_combine_error(&error, GRPC_OS_ERROR(errno, "poll")); + } + + for (i = 1; i < pfd_count; i++) { + if (watchers[i].fd == NULL) { + fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL); + } else { + // Wake up all the file descriptors, if we have an invalid one + // we can identify it on the next pollset_work() + fd_end_poll(exec_ctx, &watchers[i], 1, 1, pollset); + } + } + } else if (r == 0) { + for (i = 1; i < pfd_count; i++) { + fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL); + } + } else { + if (pfds[0].revents & POLLIN_CHECK) { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "%p: got_wakeup", pollset); + } + work_combine_error( + &error, grpc_wakeup_fd_consume_wakeup(&worker.wakeup_fd->fd)); + } + for (i = 1; i < pfd_count; i++) { + if (watchers[i].fd == NULL) { + fd_end_poll(exec_ctx, &watchers[i], 0, 0, NULL); + } else { + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_DEBUG, "%p got_event: %d r:%d w:%d [%d]", pollset, + pfds[i].fd, (pfds[i].revents & POLLIN_CHECK) != 0, + (pfds[i].revents & POLLOUT_CHECK) != 0, pfds[i].revents); + } + fd_end_poll(exec_ctx, &watchers[i], pfds[i].revents & POLLIN_CHECK, + pfds[i].revents & POLLOUT_CHECK, pollset); + } + } + } + + if (pfds != pollfd_space) { + /* pfds and watchers are in the same memory block pointed to by pfds */ + gpr_free(pfds); + } + + GPR_TIMER_END("maybe_work_and_unlock", 0); + locked = 0; + } else { + GPR_TIMER_MARK("pollset_work.kicked_without_pollers", 0); + pollset->kicked_without_pollers = 0; + } + /* Finished execution - start cleaning up. + Note that we may arrive here from outside the enclosing while() loop. + In that case we won't loop though as we haven't added worker to the + worker list, which means nobody could ask us to re-evaluate polling). */ + done: + if (!locked) { + queued_work |= grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); + locked = 1; + } + /* If we're forced to re-evaluate polling (via pollset_kick with + GRPC_POLLSET_REEVALUATE_POLLING_ON_WAKEUP) then we land here and force + a loop */ + if (worker.reevaluate_polling_on_wakeup && error == GRPC_ERROR_NONE) { + worker.reevaluate_polling_on_wakeup = 0; + pollset->kicked_without_pollers = 0; + if (queued_work || worker.kicked_specifically) { + /* If there's queued work on the list, then set the deadline to be + immediate so we get back out of the polling loop quickly */ + deadline = gpr_inf_past(GPR_CLOCK_MONOTONIC); + } + keep_polling = 1; + } + if (keep_polling) { + now = gpr_now(now.clock_type); + } + } + gpr_tls_set(&g_current_thread_poller, 0); + if (added_worker) { + remove_worker(pollset, &worker); + gpr_tls_set(&g_current_thread_worker, 0); + } + /* release wakeup fd to the local pool */ + worker.wakeup_fd->next = pollset->local_wakeup_cache; + pollset->local_wakeup_cache = worker.wakeup_fd; + /* check shutdown conditions */ + if (pollset->shutting_down) { + if (pollset_has_workers(pollset)) { + pollset_kick(exec_ctx, pollset, NULL); + } else if (!pollset->called_shutdown && !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(exec_ctx, pollset); + grpc_exec_ctx_flush(exec_ctx); + /* Continuing to access pollset here is safe -- it is the caller's + * responsibility to not destroy when it has outstanding calls to + * pollset_work. + * TODO(dklempner): Can we refactor the shutdown logic to avoid this? */ + gpr_mu_lock(&pollset->mu); + } else if (!grpc_closure_list_empty(pollset->idle_jobs)) { + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pollset->idle_jobs); + gpr_mu_unlock(&pollset->mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&pollset->mu); + } + } + if (worker_hdl) *worker_hdl = NULL; + GPR_TIMER_END("pollset_work", 0); + GRPC_LOG_IF_ERROR("pollset_work", GRPC_ERROR_REF(error)); + return error; +} + +static void pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + GPR_ASSERT(!pollset->shutting_down); + pollset->shutting_down = 1; + pollset->shutdown_done = closure; + pollset_kick(exec_ctx, pollset, GRPC_POLLSET_KICK_BROADCAST); + if (!pollset_has_workers(pollset)) { + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &pollset->idle_jobs); + } + if (!pollset->called_shutdown && !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + finish_shutdown(exec_ctx, pollset); + } +} + +static int poll_deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + static const int64_t max_spin_polling_us = 10; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return -1; + } + if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( + max_spin_polling_us, + GPR_TIMESPAN))) <= 0) { + return 0; + } + timeout = gpr_time_sub(deadline, now); + return gpr_time_to_millis(gpr_time_add( + timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); +} + +/******************************************************************************* + * pollset_set_posix.c + */ + +static grpc_pollset_set *pollset_set_create(void) { + grpc_pollset_set *pollset_set = + (grpc_pollset_set *)gpr_zalloc(sizeof(*pollset_set)); + gpr_mu_init(&pollset_set->mu); + return pollset_set; +} + +static void pollset_set_destroy(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set) { + size_t i; + gpr_mu_destroy(&pollset_set->mu); + for (i = 0; i < pollset_set->fd_count; i++) { + GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); + } + for (i = 0; i < pollset_set->pollset_count; i++) { + grpc_pollset *pollset = pollset_set->pollsets[i]; + gpr_mu_lock(&pollset->mu); + pollset->pollset_set_count--; + /* check shutdown */ + if (pollset->shutting_down && !pollset->called_shutdown && + !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(exec_ctx, pollset); + } else { + gpr_mu_unlock(&pollset->mu); + } + } + gpr_free(pollset_set->pollsets); + gpr_free(pollset_set->pollset_sets); + gpr_free(pollset_set->fds); + gpr_free(pollset_set); +} + +static void pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, + grpc_pollset *pollset) { + size_t i, j; + gpr_mu_lock(&pollset->mu); + pollset->pollset_set_count++; + gpr_mu_unlock(&pollset->mu); + gpr_mu_lock(&pollset_set->mu); + if (pollset_set->pollset_count == pollset_set->pollset_capacity) { + pollset_set->pollset_capacity = + GPR_MAX(8, 2 * pollset_set->pollset_capacity); + pollset_set->pollsets = (grpc_pollset **)gpr_realloc( + pollset_set->pollsets, + pollset_set->pollset_capacity * sizeof(*pollset_set->pollsets)); + } + pollset_set->pollsets[pollset_set->pollset_count++] = pollset; + for (i = 0, j = 0; i < pollset_set->fd_count; i++) { + if (fd_is_orphaned(pollset_set->fds[i])) { + GRPC_FD_UNREF(pollset_set->fds[i], "pollset_set"); + } else { + pollset_add_fd(exec_ctx, pollset, pollset_set->fds[i]); + pollset_set->fds[j++] = pollset_set->fds[i]; + } + } + pollset_set->fd_count = j; + gpr_mu_unlock(&pollset_set->mu); +} + +static void pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, + grpc_pollset *pollset) { + size_t i; + gpr_mu_lock(&pollset_set->mu); + for (i = 0; i < pollset_set->pollset_count; i++) { + if (pollset_set->pollsets[i] == pollset) { + pollset_set->pollset_count--; + GPR_SWAP(grpc_pollset *, pollset_set->pollsets[i], + pollset_set->pollsets[pollset_set->pollset_count]); + break; + } + } + gpr_mu_unlock(&pollset_set->mu); + gpr_mu_lock(&pollset->mu); + pollset->pollset_set_count--; + /* check shutdown */ + if (pollset->shutting_down && !pollset->called_shutdown && + !pollset_has_observers(pollset)) { + pollset->called_shutdown = 1; + gpr_mu_unlock(&pollset->mu); + finish_shutdown(exec_ctx, pollset); + } else { + gpr_mu_unlock(&pollset->mu); + } +} + +static void pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + size_t i, j; + gpr_mu_lock(&bag->mu); + if (bag->pollset_set_count == bag->pollset_set_capacity) { + bag->pollset_set_capacity = GPR_MAX(8, 2 * bag->pollset_set_capacity); + bag->pollset_sets = (grpc_pollset_set **)gpr_realloc( + bag->pollset_sets, + bag->pollset_set_capacity * sizeof(*bag->pollset_sets)); + } + bag->pollset_sets[bag->pollset_set_count++] = item; + for (i = 0, j = 0; i < bag->fd_count; i++) { + if (fd_is_orphaned(bag->fds[i])) { + GRPC_FD_UNREF(bag->fds[i], "pollset_set"); + } else { + pollset_set_add_fd(exec_ctx, item, bag->fds[i]); + bag->fds[j++] = bag->fds[i]; + } + } + bag->fd_count = j; + gpr_mu_unlock(&bag->mu); +} + +static void pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + size_t i; + gpr_mu_lock(&bag->mu); + for (i = 0; i < bag->pollset_set_count; i++) { + if (bag->pollset_sets[i] == item) { + bag->pollset_set_count--; + GPR_SWAP(grpc_pollset_set *, bag->pollset_sets[i], + bag->pollset_sets[bag->pollset_set_count]); + break; + } + } + gpr_mu_unlock(&bag->mu); +} + +static void pollset_set_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd) { + size_t i; + gpr_mu_lock(&pollset_set->mu); + if (pollset_set->fd_count == pollset_set->fd_capacity) { + pollset_set->fd_capacity = GPR_MAX(8, 2 * pollset_set->fd_capacity); + pollset_set->fds = (grpc_fd **)gpr_realloc( + pollset_set->fds, pollset_set->fd_capacity * sizeof(*pollset_set->fds)); + } + GRPC_FD_REF(fd, "pollset_set"); + pollset_set->fds[pollset_set->fd_count++] = fd; + for (i = 0; i < pollset_set->pollset_count; i++) { + pollset_add_fd(exec_ctx, pollset_set->pollsets[i], fd); + } + for (i = 0; i < pollset_set->pollset_set_count; i++) { + pollset_set_add_fd(exec_ctx, pollset_set->pollset_sets[i], fd); + } + gpr_mu_unlock(&pollset_set->mu); +} + +static void pollset_set_del_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd) { + size_t i; + gpr_mu_lock(&pollset_set->mu); + for (i = 0; i < pollset_set->fd_count; i++) { + if (pollset_set->fds[i] == fd) { + pollset_set->fd_count--; + GPR_SWAP(grpc_fd *, pollset_set->fds[i], + pollset_set->fds[pollset_set->fd_count]); + GRPC_FD_UNREF(fd, "pollset_set"); + break; + } + } + for (i = 0; i < pollset_set->pollset_set_count; i++) { + pollset_set_del_fd(exec_ctx, pollset_set->pollset_sets[i], fd); + } + gpr_mu_unlock(&pollset_set->mu); +} + +/******************************************************************************* + * Condition Variable polling extensions + */ + +static void run_poll(void *args); +static void cache_poller_locked(poll_args *args); + +static void cache_insert_locked(poll_args *args) { + uint32_t key = gpr_murmur_hash3(args->fds, args->nfds * sizeof(struct pollfd), + 0xDEADBEEF); + key = key % poll_cache.size; + if (poll_cache.active_pollers[key]) { + poll_cache.active_pollers[key]->prev = args; + } + args->next = poll_cache.active_pollers[key]; + args->prev = NULL; + poll_cache.active_pollers[key] = args; + poll_cache.count++; +} + +static void init_result(poll_args *pargs) { + pargs->result = (poll_result *)gpr_malloc(sizeof(poll_result)); + gpr_ref_init(&pargs->result->refcount, 1); + pargs->result->watchers = NULL; + pargs->result->watchcount = 0; + pargs->result->fds = + (struct pollfd *)gpr_malloc(sizeof(struct pollfd) * pargs->nfds); + memcpy(pargs->result->fds, pargs->fds, sizeof(struct pollfd) * pargs->nfds); + pargs->result->nfds = pargs->nfds; + pargs->result->retval = 0; + pargs->result->err = 0; + pargs->result->completed = 0; +} + +// Creates a poll_args object for a given arguments to poll(). +// This object may return a poll_args in the cache. +static poll_args *get_poller_locked(struct pollfd *fds, nfds_t count) { + uint32_t key = + gpr_murmur_hash3(fds, count * sizeof(struct pollfd), 0xDEADBEEF); + key = key % poll_cache.size; + poll_args *curr = poll_cache.active_pollers[key]; + while (curr) { + if (curr->nfds == count && + memcmp(curr->fds, fds, count * sizeof(struct pollfd)) == 0) { + gpr_free(fds); + return curr; + } + curr = curr->next; + } + + if (poll_cache.free_pollers) { + poll_args *pargs = poll_cache.free_pollers; + poll_cache.free_pollers = pargs->next; + if (poll_cache.free_pollers) { + poll_cache.free_pollers->prev = NULL; + } + pargs->fds = fds; + pargs->nfds = count; + pargs->next = NULL; + pargs->prev = NULL; + init_result(pargs); + cache_poller_locked(pargs); + return pargs; + } + + poll_args *pargs = (poll_args *)gpr_malloc(sizeof(struct poll_args)); + gpr_cv_init(&pargs->trigger); + pargs->fds = fds; + pargs->nfds = count; + pargs->next = NULL; + pargs->prev = NULL; + pargs->trigger_set = 0; + init_result(pargs); + cache_poller_locked(pargs); + gpr_thd_id t_id; + gpr_thd_options opt = gpr_thd_options_default(); + gpr_ref(&g_cvfds.pollcount); + gpr_thd_options_set_detached(&opt); + GPR_ASSERT(gpr_thd_new(&t_id, &run_poll, pargs, &opt)); + return pargs; +} + +static void cache_delete_locked(poll_args *args) { + if (!args->prev) { + uint32_t key = gpr_murmur_hash3( + args->fds, args->nfds * sizeof(struct pollfd), 0xDEADBEEF); + key = key % poll_cache.size; + GPR_ASSERT(poll_cache.active_pollers[key] == args); + poll_cache.active_pollers[key] = args->next; + } else { + args->prev->next = args->next; + } + + if (args->next) { + args->next->prev = args->prev; + } + + poll_cache.count--; + if (poll_cache.free_pollers) { + poll_cache.free_pollers->prev = args; + } + args->prev = NULL; + args->next = poll_cache.free_pollers; + gpr_free(args->fds); + poll_cache.free_pollers = args; +} + +static void cache_poller_locked(poll_args *args) { + if (poll_cache.count + 1 > poll_cache.size / 2) { + poll_args **old_active_pollers = poll_cache.active_pollers; + poll_cache.size = poll_cache.size * 2; + poll_cache.count = 0; + poll_cache.active_pollers = + (poll_args **)gpr_malloc(sizeof(void *) * poll_cache.size); + for (unsigned int i = 0; i < poll_cache.size; i++) { + poll_cache.active_pollers[i] = NULL; + } + for (unsigned int i = 0; i < poll_cache.size / 2; i++) { + poll_args *curr = old_active_pollers[i]; + poll_args *next = NULL; + while (curr) { + next = curr->next; + cache_insert_locked(curr); + curr = next; + } + } + gpr_free(old_active_pollers); + } + + cache_insert_locked(args); +} + +static void cache_destroy_locked(poll_args *args) { + if (args->next) { + args->next->prev = args->prev; + } + + if (args->prev) { + args->prev->next = args->next; + } else { + poll_cache.free_pollers = args->next; + } + + gpr_free(args); +} + +static void decref_poll_result(poll_result *res) { + if (gpr_unref(&res->refcount)) { + GPR_ASSERT(!res->watchers); + gpr_free(res->fds); + gpr_free(res); + } +} + +void remove_cvn(cv_node **head, cv_node *target) { + if (target->next) { + target->next->prev = target->prev; + } + + if (target->prev) { + target->prev->next = target->next; + } else { + *head = target->next; + } +} + +gpr_timespec thread_grace; + +// Poll in a background thread +static void run_poll(void *args) { + poll_args *pargs = (poll_args *)args; + while (1) { + poll_result *result = pargs->result; + int retval = g_cvfds.poll(result->fds, result->nfds, CV_POLL_PERIOD_MS); + gpr_mu_lock(&g_cvfds.mu); + if (retval != 0) { + result->completed = 1; + result->retval = retval; + result->err = errno; + cv_node *watcher = result->watchers; + while (watcher) { + gpr_cv_signal(watcher->cv); + watcher = watcher->next; + } + } + if (result->watchcount == 0 || result->completed) { + cache_delete_locked(pargs); + decref_poll_result(result); + // Leave this polling thread alive for a grace period to do another poll() + // op + gpr_timespec deadline = gpr_now(GPR_CLOCK_REALTIME); + deadline = gpr_time_add(deadline, thread_grace); + pargs->trigger_set = 0; + gpr_cv_wait(&pargs->trigger, &g_cvfds.mu, deadline); + if (!pargs->trigger_set) { + cache_destroy_locked(pargs); + break; + } + } + gpr_mu_unlock(&g_cvfds.mu); + } + + // We still have the lock here + if (gpr_unref(&g_cvfds.pollcount)) { + gpr_cv_signal(&g_cvfds.shutdown_cv); + } + gpr_mu_unlock(&g_cvfds.mu); +} + +// This function overrides poll() to handle condition variable wakeup fds +static int cvfd_poll(struct pollfd *fds, nfds_t nfds, int timeout) { + unsigned int i; + int res, idx; + cv_node *pollcv; + int skip_poll = 0; + nfds_t nsockfds = 0; + poll_result *result = NULL; + gpr_mu_lock(&g_cvfds.mu); + pollcv = (cv_node *)gpr_malloc(sizeof(cv_node)); + pollcv->next = NULL; + gpr_cv pollcv_cv; + gpr_cv_init(&pollcv_cv); + pollcv->cv = &pollcv_cv; + cv_node *fd_cvs = (cv_node *)gpr_malloc(nfds * sizeof(cv_node)); + + for (i = 0; i < nfds; i++) { + fds[i].revents = 0; + if (fds[i].fd < 0 && (fds[i].events & POLLIN)) { + idx = GRPC_FD_TO_IDX(fds[i].fd); + fd_cvs[i].cv = &pollcv_cv; + fd_cvs[i].prev = NULL; + fd_cvs[i].next = g_cvfds.cvfds[idx].cvs; + if (g_cvfds.cvfds[idx].cvs) { + g_cvfds.cvfds[idx].cvs->prev = &(fd_cvs[i]); + } + g_cvfds.cvfds[idx].cvs = &(fd_cvs[i]); + // Don't bother polling if a wakeup fd is ready + if (g_cvfds.cvfds[idx].is_set) { + skip_poll = 1; + } + } else if (fds[i].fd >= 0) { + nsockfds++; + } + } + + gpr_timespec deadline = gpr_now(GPR_CLOCK_REALTIME); + if (timeout < 0) { + deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + } else { + deadline = + gpr_time_add(deadline, gpr_time_from_millis(timeout, GPR_TIMESPAN)); + } + + res = 0; + if (!skip_poll && nsockfds > 0) { + struct pollfd *pollfds = + (struct pollfd *)gpr_malloc(sizeof(struct pollfd) * nsockfds); + idx = 0; + for (i = 0; i < nfds; i++) { + if (fds[i].fd >= 0) { + pollfds[idx].fd = fds[i].fd; + pollfds[idx].events = fds[i].events; + pollfds[idx].revents = 0; + idx++; + } + } + poll_args *pargs = get_poller_locked(pollfds, nsockfds); + result = pargs->result; + pollcv->next = result->watchers; + pollcv->prev = NULL; + if (result->watchers) { + result->watchers->prev = pollcv; + } + result->watchers = pollcv; + result->watchcount++; + gpr_ref(&result->refcount); + + pargs->trigger_set = 1; + gpr_cv_signal(&pargs->trigger); + gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + res = result->retval; + errno = result->err; + result->watchcount--; + remove_cvn(&result->watchers, pollcv); + } else if (!skip_poll) { + gpr_cv_wait(&pollcv_cv, &g_cvfds.mu, deadline); + } + + idx = 0; + for (i = 0; i < nfds; i++) { + if (fds[i].fd < 0 && (fds[i].events & POLLIN)) { + remove_cvn(&g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].cvs, &(fd_cvs[i])); + if (g_cvfds.cvfds[GRPC_FD_TO_IDX(fds[i].fd)].is_set) { + fds[i].revents = POLLIN; + if (res >= 0) res++; + } + } else if (!skip_poll && fds[i].fd >= 0 && result->completed) { + fds[i].revents = result->fds[idx].revents; + idx++; + } + } + + gpr_free(fd_cvs); + gpr_free(pollcv); + if (result) { + decref_poll_result(result); + } + + gpr_mu_unlock(&g_cvfds.mu); + + return res; +} + +static void global_cv_fd_table_init() { + gpr_mu_init(&g_cvfds.mu); + gpr_mu_lock(&g_cvfds.mu); + gpr_cv_init(&g_cvfds.shutdown_cv); + gpr_ref_init(&g_cvfds.pollcount, 1); + g_cvfds.size = CV_DEFAULT_TABLE_SIZE; + g_cvfds.cvfds = + (fd_node *)gpr_malloc(sizeof(fd_node) * CV_DEFAULT_TABLE_SIZE); + g_cvfds.free_fds = NULL; + thread_grace = gpr_time_from_millis(POLLCV_THREAD_GRACE_MS, GPR_TIMESPAN); + for (int i = 0; i < CV_DEFAULT_TABLE_SIZE; i++) { + g_cvfds.cvfds[i].is_set = 0; + g_cvfds.cvfds[i].cvs = NULL; + g_cvfds.cvfds[i].next_free = g_cvfds.free_fds; + g_cvfds.free_fds = &g_cvfds.cvfds[i]; + } + // Override the poll function with one that supports cvfds + g_cvfds.poll = grpc_poll_function; + grpc_poll_function = &cvfd_poll; + + // Initialize the cache + poll_cache.size = 32; + poll_cache.count = 0; + poll_cache.free_pollers = NULL; + poll_cache.active_pollers = (poll_args **)gpr_malloc(sizeof(void *) * 32); + for (unsigned int i = 0; i < poll_cache.size; i++) { + poll_cache.active_pollers[i] = NULL; + } + + gpr_mu_unlock(&g_cvfds.mu); +} + +static void global_cv_fd_table_shutdown() { + gpr_mu_lock(&g_cvfds.mu); + // Attempt to wait for all abandoned poll() threads to terminate + // Not doing so will result in reported memory leaks + if (!gpr_unref(&g_cvfds.pollcount)) { + int res = gpr_cv_wait(&g_cvfds.shutdown_cv, &g_cvfds.mu, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_seconds(3, GPR_TIMESPAN))); + GPR_ASSERT(res == 0); + } + gpr_cv_destroy(&g_cvfds.shutdown_cv); + grpc_poll_function = g_cvfds.poll; + gpr_free(g_cvfds.cvfds); + + gpr_free(poll_cache.active_pollers); + + gpr_mu_unlock(&g_cvfds.mu); + gpr_mu_destroy(&g_cvfds.mu); +} + +/******************************************************************************* + * event engine binding + */ + +static void shutdown_engine(void) { + pollset_global_shutdown(); + if (grpc_cv_wakeup_fds_enabled()) { + global_cv_fd_table_shutdown(); + } +} + +static const grpc_event_engine_vtable vtable = { + sizeof(grpc_pollset), + + fd_create, + fd_wrapped_fd, + fd_orphan, + fd_shutdown, + fd_notify_on_read, + fd_notify_on_write, + fd_is_shutdown, + fd_get_read_notifier_pollset, + + pollset_init, + pollset_shutdown, + pollset_destroy, + pollset_work, + pollset_kick, + pollset_add_fd, + + pollset_set_create, + pollset_set_destroy, + pollset_set_add_pollset, + pollset_set_del_pollset, + pollset_set_add_pollset_set, + pollset_set_del_pollset_set, + pollset_set_add_fd, + pollset_set_del_fd, + + shutdown_engine, +}; + +const grpc_event_engine_vtable *grpc_init_poll_posix(bool explicit_request) { + if (!grpc_has_wakeup_fd()) { + return NULL; + } + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + return NULL; + } + return &vtable; +} + +const grpc_event_engine_vtable *grpc_init_poll_cv_posix(bool explicit_request) { + global_cv_fd_table_init(); + grpc_enable_cv_wakeup_fds(1); + if (!GRPC_LOG_IF_ERROR("pollset_global_init", pollset_global_init())) { + global_cv_fd_table_shutdown(); + grpc_enable_cv_wakeup_fds(0); + return NULL; + } + return &vtable; +} + +#endif diff --git a/src/core/lib/iomgr/ev_posix.c b/src/core/lib/iomgr/ev_posix.c deleted file mode 100644 index 4d3ae2228e..0000000000 --- a/src/core/lib/iomgr/ev_posix.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/ev_posix.h" - -#include - -#include -#include -#include -#include - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/ev_epoll1_linux.h" -#include "src/core/lib/iomgr/ev_epollex_linux.h" -#include "src/core/lib/iomgr/ev_epollsig_linux.h" -#include "src/core/lib/iomgr/ev_poll_posix.h" -#include "src/core/lib/support/env.h" - -grpc_tracer_flag grpc_polling_trace = - GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */ - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_fd_refcount = - GRPC_TRACER_INITIALIZER(false, "fd_refcount"); -#endif - -/** Default poll() function - a pointer so that it can be overridden by some - * tests */ -grpc_poll_function_type grpc_poll_function = poll; - -grpc_wakeup_fd grpc_global_wakeup_fd; - -static const grpc_event_engine_vtable *g_event_engine; -static const char *g_poll_strategy_name = NULL; - -typedef const grpc_event_engine_vtable *(*event_engine_factory_fn)( - bool explicit_request); - -typedef struct { - const char *name; - event_engine_factory_fn factory; -} event_engine_factory; - -static const event_engine_factory g_factories[] = { - {"epoll1", grpc_init_epoll1_linux}, - {"epollsig", grpc_init_epollsig_linux}, - {"poll", grpc_init_poll_posix}, - {"poll-cv", grpc_init_poll_cv_posix}, - {"epollex", grpc_init_epollex_linux}, -}; - -static void add(const char *beg, const char *end, char ***ss, size_t *ns) { - size_t n = *ns; - size_t np = n + 1; - char *s; - size_t len; - GPR_ASSERT(end >= beg); - len = (size_t)(end - beg); - s = (char *)gpr_malloc(len + 1); - memcpy(s, beg, len); - s[len] = 0; - *ss = (char **)gpr_realloc(*ss, sizeof(char **) * np); - (*ss)[n] = s; - *ns = np; -} - -static void split(const char *s, char ***ss, size_t *ns) { - const char *c = strchr(s, ','); - if (c == NULL) { - add(s, s + strlen(s), ss, ns); - } else { - add(s, c, ss, ns); - split(c + 1, ss, ns); - } -} - -static bool is(const char *want, const char *have) { - return 0 == strcmp(want, "all") || 0 == strcmp(want, have); -} - -static void try_engine(const char *engine) { - for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) { - if (is(engine, g_factories[i].name)) { - if ((g_event_engine = g_factories[i].factory( - 0 == strcmp(engine, g_factories[i].name)))) { - g_poll_strategy_name = g_factories[i].name; - gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name); - return; - } - } - } -} - -/* This should be used for testing purposes ONLY */ -void grpc_set_event_engine_test_only( - const grpc_event_engine_vtable *ev_engine) { - g_event_engine = ev_engine; -} - -const grpc_event_engine_vtable *grpc_get_event_engine_test_only() { - return g_event_engine; -} - -/* Call this only after calling grpc_event_engine_init() */ -const char *grpc_get_poll_strategy_name() { return g_poll_strategy_name; } - -void grpc_event_engine_init(void) { - grpc_register_tracer(&grpc_polling_trace); - - char *s = gpr_getenv("GRPC_POLL_STRATEGY"); - if (s == NULL) { - s = gpr_strdup("all"); - } - - char **strings = NULL; - size_t nstrings = 0; - split(s, &strings, &nstrings); - - for (size_t i = 0; g_event_engine == NULL && i < nstrings; i++) { - try_engine(strings[i]); - } - - for (size_t i = 0; i < nstrings; i++) { - gpr_free(strings[i]); - } - gpr_free(strings); - gpr_free(s); - - if (g_event_engine == NULL) { - gpr_log(GPR_ERROR, "No event engine could be initialized"); - abort(); - } -} - -void grpc_event_engine_shutdown(void) { - g_event_engine->shutdown_engine(); - g_event_engine = NULL; -} - -grpc_fd *grpc_fd_create(int fd, const char *name) { - return g_event_engine->fd_create(fd, name); -} - -int grpc_fd_wrapped_fd(grpc_fd *fd) { - return g_event_engine->fd_wrapped_fd(fd); -} - -void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done, - int *release_fd, bool already_closed, const char *reason) { - g_event_engine->fd_orphan(exec_ctx, fd, on_done, release_fd, already_closed, - reason); -} - -void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { - g_event_engine->fd_shutdown(exec_ctx, fd, why); -} - -bool grpc_fd_is_shutdown(grpc_fd *fd) { - return g_event_engine->fd_is_shutdown(fd); -} - -void grpc_fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - g_event_engine->fd_notify_on_read(exec_ctx, fd, closure); -} - -void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, - grpc_closure *closure) { - g_event_engine->fd_notify_on_write(exec_ctx, fd, closure); -} - -size_t grpc_pollset_size(void) { return g_event_engine->pollset_size; } - -void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - g_event_engine->pollset_init(pollset, mu); -} - -void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - g_event_engine->pollset_shutdown(exec_ctx, pollset, closure); -} - -void grpc_pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - g_event_engine->pollset_destroy(exec_ctx, pollset); -} - -grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker, gpr_timespec now, - gpr_timespec deadline) { - return g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline); -} - -grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *specific_worker) { - return g_event_engine->pollset_kick(exec_ctx, pollset, specific_worker); -} - -void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - struct grpc_fd *fd) { - g_event_engine->pollset_add_fd(exec_ctx, pollset, fd); -} - -grpc_pollset_set *grpc_pollset_set_create(void) { - return g_event_engine->pollset_set_create(); -} - -void grpc_pollset_set_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set) { - g_event_engine->pollset_set_destroy(exec_ctx, pollset_set); -} - -void grpc_pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, - grpc_pollset *pollset) { - g_event_engine->pollset_set_add_pollset(exec_ctx, pollset_set, pollset); -} - -void grpc_pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, - grpc_pollset *pollset) { - g_event_engine->pollset_set_del_pollset(exec_ctx, pollset_set, pollset); -} - -void grpc_pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - g_event_engine->pollset_set_add_pollset_set(exec_ctx, bag, item); -} - -void grpc_pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *bag, - grpc_pollset_set *item) { - g_event_engine->pollset_set_del_pollset_set(exec_ctx, bag, item); -} - -void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd) { - g_event_engine->pollset_set_add_fd(exec_ctx, pollset_set, fd); -} - -void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, - grpc_pollset_set *pollset_set, grpc_fd *fd) { - g_event_engine->pollset_set_del_fd(exec_ctx, pollset_set, fd); -} - -#endif // GRPC_POSIX_SOCKET diff --git a/src/core/lib/iomgr/ev_posix.cc b/src/core/lib/iomgr/ev_posix.cc new file mode 100644 index 0000000000..4d3ae2228e --- /dev/null +++ b/src/core/lib/iomgr/ev_posix.cc @@ -0,0 +1,266 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/ev_posix.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/ev_epoll1_linux.h" +#include "src/core/lib/iomgr/ev_epollex_linux.h" +#include "src/core/lib/iomgr/ev_epollsig_linux.h" +#include "src/core/lib/iomgr/ev_poll_posix.h" +#include "src/core/lib/support/env.h" + +grpc_tracer_flag grpc_polling_trace = + GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */ + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_fd_refcount = + GRPC_TRACER_INITIALIZER(false, "fd_refcount"); +#endif + +/** Default poll() function - a pointer so that it can be overridden by some + * tests */ +grpc_poll_function_type grpc_poll_function = poll; + +grpc_wakeup_fd grpc_global_wakeup_fd; + +static const grpc_event_engine_vtable *g_event_engine; +static const char *g_poll_strategy_name = NULL; + +typedef const grpc_event_engine_vtable *(*event_engine_factory_fn)( + bool explicit_request); + +typedef struct { + const char *name; + event_engine_factory_fn factory; +} event_engine_factory; + +static const event_engine_factory g_factories[] = { + {"epoll1", grpc_init_epoll1_linux}, + {"epollsig", grpc_init_epollsig_linux}, + {"poll", grpc_init_poll_posix}, + {"poll-cv", grpc_init_poll_cv_posix}, + {"epollex", grpc_init_epollex_linux}, +}; + +static void add(const char *beg, const char *end, char ***ss, size_t *ns) { + size_t n = *ns; + size_t np = n + 1; + char *s; + size_t len; + GPR_ASSERT(end >= beg); + len = (size_t)(end - beg); + s = (char *)gpr_malloc(len + 1); + memcpy(s, beg, len); + s[len] = 0; + *ss = (char **)gpr_realloc(*ss, sizeof(char **) * np); + (*ss)[n] = s; + *ns = np; +} + +static void split(const char *s, char ***ss, size_t *ns) { + const char *c = strchr(s, ','); + if (c == NULL) { + add(s, s + strlen(s), ss, ns); + } else { + add(s, c, ss, ns); + split(c + 1, ss, ns); + } +} + +static bool is(const char *want, const char *have) { + return 0 == strcmp(want, "all") || 0 == strcmp(want, have); +} + +static void try_engine(const char *engine) { + for (size_t i = 0; i < GPR_ARRAY_SIZE(g_factories); i++) { + if (is(engine, g_factories[i].name)) { + if ((g_event_engine = g_factories[i].factory( + 0 == strcmp(engine, g_factories[i].name)))) { + g_poll_strategy_name = g_factories[i].name; + gpr_log(GPR_DEBUG, "Using polling engine: %s", g_factories[i].name); + return; + } + } + } +} + +/* This should be used for testing purposes ONLY */ +void grpc_set_event_engine_test_only( + const grpc_event_engine_vtable *ev_engine) { + g_event_engine = ev_engine; +} + +const grpc_event_engine_vtable *grpc_get_event_engine_test_only() { + return g_event_engine; +} + +/* Call this only after calling grpc_event_engine_init() */ +const char *grpc_get_poll_strategy_name() { return g_poll_strategy_name; } + +void grpc_event_engine_init(void) { + grpc_register_tracer(&grpc_polling_trace); + + char *s = gpr_getenv("GRPC_POLL_STRATEGY"); + if (s == NULL) { + s = gpr_strdup("all"); + } + + char **strings = NULL; + size_t nstrings = 0; + split(s, &strings, &nstrings); + + for (size_t i = 0; g_event_engine == NULL && i < nstrings; i++) { + try_engine(strings[i]); + } + + for (size_t i = 0; i < nstrings; i++) { + gpr_free(strings[i]); + } + gpr_free(strings); + gpr_free(s); + + if (g_event_engine == NULL) { + gpr_log(GPR_ERROR, "No event engine could be initialized"); + abort(); + } +} + +void grpc_event_engine_shutdown(void) { + g_event_engine->shutdown_engine(); + g_event_engine = NULL; +} + +grpc_fd *grpc_fd_create(int fd, const char *name) { + return g_event_engine->fd_create(fd, name); +} + +int grpc_fd_wrapped_fd(grpc_fd *fd) { + return g_event_engine->fd_wrapped_fd(fd); +} + +void grpc_fd_orphan(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_closure *on_done, + int *release_fd, bool already_closed, const char *reason) { + g_event_engine->fd_orphan(exec_ctx, fd, on_done, release_fd, already_closed, + reason); +} + +void grpc_fd_shutdown(grpc_exec_ctx *exec_ctx, grpc_fd *fd, grpc_error *why) { + g_event_engine->fd_shutdown(exec_ctx, fd, why); +} + +bool grpc_fd_is_shutdown(grpc_fd *fd) { + return g_event_engine->fd_is_shutdown(fd); +} + +void grpc_fd_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + g_event_engine->fd_notify_on_read(exec_ctx, fd, closure); +} + +void grpc_fd_notify_on_write(grpc_exec_ctx *exec_ctx, grpc_fd *fd, + grpc_closure *closure) { + g_event_engine->fd_notify_on_write(exec_ctx, fd, closure); +} + +size_t grpc_pollset_size(void) { return g_event_engine->pollset_size; } + +void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + g_event_engine->pollset_init(pollset, mu); +} + +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + g_event_engine->pollset_shutdown(exec_ctx, pollset, closure); +} + +void grpc_pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + g_event_engine->pollset_destroy(exec_ctx, pollset); +} + +grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker, gpr_timespec now, + gpr_timespec deadline) { + return g_event_engine->pollset_work(exec_ctx, pollset, worker, now, deadline); +} + +grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *specific_worker) { + return g_event_engine->pollset_kick(exec_ctx, pollset, specific_worker); +} + +void grpc_pollset_add_fd(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + struct grpc_fd *fd) { + g_event_engine->pollset_add_fd(exec_ctx, pollset, fd); +} + +grpc_pollset_set *grpc_pollset_set_create(void) { + return g_event_engine->pollset_set_create(); +} + +void grpc_pollset_set_destroy(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set) { + g_event_engine->pollset_set_destroy(exec_ctx, pollset_set); +} + +void grpc_pollset_set_add_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, + grpc_pollset *pollset) { + g_event_engine->pollset_set_add_pollset(exec_ctx, pollset_set, pollset); +} + +void grpc_pollset_set_del_pollset(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, + grpc_pollset *pollset) { + g_event_engine->pollset_set_del_pollset(exec_ctx, pollset_set, pollset); +} + +void grpc_pollset_set_add_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + g_event_engine->pollset_set_add_pollset_set(exec_ctx, bag, item); +} + +void grpc_pollset_set_del_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *bag, + grpc_pollset_set *item) { + g_event_engine->pollset_set_del_pollset_set(exec_ctx, bag, item); +} + +void grpc_pollset_set_add_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd) { + g_event_engine->pollset_set_add_fd(exec_ctx, pollset_set, fd); +} + +void grpc_pollset_set_del_fd(grpc_exec_ctx *exec_ctx, + grpc_pollset_set *pollset_set, grpc_fd *fd) { + g_event_engine->pollset_set_del_fd(exec_ctx, pollset_set, fd); +} + +#endif // GRPC_POSIX_SOCKET diff --git a/src/core/lib/iomgr/ev_windows.c b/src/core/lib/iomgr/ev_windows.c deleted file mode 100644 index c24dfaeaf7..0000000000 --- a/src/core/lib/iomgr/ev_windows.c +++ /dev/null @@ -1,28 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include "src/core/lib/debug/trace.h" - -grpc_tracer_flag grpc_polling_trace = - GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */ - -#endif // GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/ev_windows.cc b/src/core/lib/iomgr/ev_windows.cc new file mode 100644 index 0000000000..c24dfaeaf7 --- /dev/null +++ b/src/core/lib/iomgr/ev_windows.cc @@ -0,0 +1,28 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/debug/trace.h" + +grpc_tracer_flag grpc_polling_trace = + GRPC_TRACER_INITIALIZER(false, "polling"); /* Disabled by default */ + +#endif // GRPC_WINSOCK_SOCKET diff --git a/src/core/lib/iomgr/exec_ctx.c b/src/core/lib/iomgr/exec_ctx.c deleted file mode 100644 index 41c69add17..0000000000 --- a/src/core/lib/iomgr/exec_ctx.c +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/exec_ctx.h" - -#include -#include -#include - -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/profiling/timers.h" - -bool grpc_exec_ctx_ready_to_finish(grpc_exec_ctx *exec_ctx) { - if ((exec_ctx->flags & GRPC_EXEC_CTX_FLAG_IS_FINISHED) == 0) { - if (exec_ctx->check_ready_to_finish(exec_ctx, - exec_ctx->check_ready_to_finish_arg)) { - exec_ctx->flags |= GRPC_EXEC_CTX_FLAG_IS_FINISHED; - return true; - } - return false; - } else { - return true; - } -} - -bool grpc_never_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored) { - return false; -} - -bool grpc_always_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored) { - return true; -} - -bool grpc_exec_ctx_has_work(grpc_exec_ctx *exec_ctx) { - return exec_ctx->active_combiner != NULL || - !grpc_closure_list_empty(exec_ctx->closure_list); -} - -void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) { - exec_ctx->flags |= GRPC_EXEC_CTX_FLAG_IS_FINISHED; - grpc_exec_ctx_flush(exec_ctx); -} - -static void exec_ctx_run(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_error *error) { -#ifndef NDEBUG - closure->scheduled = false; - if (GRPC_TRACER_ON(grpc_trace_closure)) { - gpr_log(GPR_DEBUG, "running closure %p: created [%s:%d]: %s [%s:%d]", - closure, closure->file_created, closure->line_created, - closure->run ? "run" : "scheduled", closure->file_initiated, - closure->line_initiated); - } -#endif - closure->cb(exec_ctx, closure->cb_arg, error); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_closure)) { - gpr_log(GPR_DEBUG, "closure %p finished", closure); - } -#endif - GRPC_ERROR_UNREF(error); -} - -bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) { - bool did_something = 0; - GPR_TIMER_BEGIN("grpc_exec_ctx_flush", 0); - for (;;) { - if (!grpc_closure_list_empty(exec_ctx->closure_list)) { - grpc_closure *c = exec_ctx->closure_list.head; - exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL; - while (c != NULL) { - grpc_closure *next = c->next_data.next; - grpc_error *error = c->error_data.error; - did_something = true; - exec_ctx_run(exec_ctx, c, error); - c = next; - } - } else if (!grpc_combiner_continue_exec_ctx(exec_ctx)) { - break; - } - } - GPR_ASSERT(exec_ctx->active_combiner == NULL); - GPR_TIMER_END("grpc_exec_ctx_flush", 0); - return did_something; -} - -static void exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_error *error) { - grpc_closure_list_append(&exec_ctx->closure_list, closure, error); -} - -void grpc_exec_ctx_global_init(void) {} -void grpc_exec_ctx_global_shutdown(void) {} - -static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = { - exec_ctx_run, exec_ctx_sched, "exec_ctx"}; -static grpc_closure_scheduler exec_ctx_scheduler = {&exec_ctx_scheduler_vtable}; -grpc_closure_scheduler *grpc_schedule_on_exec_ctx = &exec_ctx_scheduler; diff --git a/src/core/lib/iomgr/exec_ctx.cc b/src/core/lib/iomgr/exec_ctx.cc new file mode 100644 index 0000000000..41c69add17 --- /dev/null +++ b/src/core/lib/iomgr/exec_ctx.cc @@ -0,0 +1,113 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/exec_ctx.h" + +#include +#include +#include + +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/profiling/timers.h" + +bool grpc_exec_ctx_ready_to_finish(grpc_exec_ctx *exec_ctx) { + if ((exec_ctx->flags & GRPC_EXEC_CTX_FLAG_IS_FINISHED) == 0) { + if (exec_ctx->check_ready_to_finish(exec_ctx, + exec_ctx->check_ready_to_finish_arg)) { + exec_ctx->flags |= GRPC_EXEC_CTX_FLAG_IS_FINISHED; + return true; + } + return false; + } else { + return true; + } +} + +bool grpc_never_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored) { + return false; +} + +bool grpc_always_ready_to_finish(grpc_exec_ctx *exec_ctx, void *arg_ignored) { + return true; +} + +bool grpc_exec_ctx_has_work(grpc_exec_ctx *exec_ctx) { + return exec_ctx->active_combiner != NULL || + !grpc_closure_list_empty(exec_ctx->closure_list); +} + +void grpc_exec_ctx_finish(grpc_exec_ctx *exec_ctx) { + exec_ctx->flags |= GRPC_EXEC_CTX_FLAG_IS_FINISHED; + grpc_exec_ctx_flush(exec_ctx); +} + +static void exec_ctx_run(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_error *error) { +#ifndef NDEBUG + closure->scheduled = false; + if (GRPC_TRACER_ON(grpc_trace_closure)) { + gpr_log(GPR_DEBUG, "running closure %p: created [%s:%d]: %s [%s:%d]", + closure, closure->file_created, closure->line_created, + closure->run ? "run" : "scheduled", closure->file_initiated, + closure->line_initiated); + } +#endif + closure->cb(exec_ctx, closure->cb_arg, error); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_closure)) { + gpr_log(GPR_DEBUG, "closure %p finished", closure); + } +#endif + GRPC_ERROR_UNREF(error); +} + +bool grpc_exec_ctx_flush(grpc_exec_ctx *exec_ctx) { + bool did_something = 0; + GPR_TIMER_BEGIN("grpc_exec_ctx_flush", 0); + for (;;) { + if (!grpc_closure_list_empty(exec_ctx->closure_list)) { + grpc_closure *c = exec_ctx->closure_list.head; + exec_ctx->closure_list.head = exec_ctx->closure_list.tail = NULL; + while (c != NULL) { + grpc_closure *next = c->next_data.next; + grpc_error *error = c->error_data.error; + did_something = true; + exec_ctx_run(exec_ctx, c, error); + c = next; + } + } else if (!grpc_combiner_continue_exec_ctx(exec_ctx)) { + break; + } + } + GPR_ASSERT(exec_ctx->active_combiner == NULL); + GPR_TIMER_END("grpc_exec_ctx_flush", 0); + return did_something; +} + +static void exec_ctx_sched(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_error *error) { + grpc_closure_list_append(&exec_ctx->closure_list, closure, error); +} + +void grpc_exec_ctx_global_init(void) {} +void grpc_exec_ctx_global_shutdown(void) {} + +static const grpc_closure_scheduler_vtable exec_ctx_scheduler_vtable = { + exec_ctx_run, exec_ctx_sched, "exec_ctx"}; +static grpc_closure_scheduler exec_ctx_scheduler = {&exec_ctx_scheduler_vtable}; +grpc_closure_scheduler *grpc_schedule_on_exec_ctx = &exec_ctx_scheduler; diff --git a/src/core/lib/iomgr/executor.c b/src/core/lib/iomgr/executor.c deleted file mode 100644 index 892385d7d7..0000000000 --- a/src/core/lib/iomgr/executor.c +++ /dev/null @@ -1,301 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/executor.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/support/spinlock.h" - -#define MAX_DEPTH 2 - -typedef struct { - gpr_mu mu; - gpr_cv cv; - grpc_closure_list elems; - size_t depth; - bool shutdown; - bool queued_long_job; - gpr_thd_id id; -} thread_state; - -static thread_state *g_thread_state; -static size_t g_max_threads; -static gpr_atm g_cur_threads; -static gpr_spinlock g_adding_thread_lock = GPR_SPINLOCK_STATIC_INITIALIZER; - -GPR_TLS_DECL(g_this_thread_state); - -static grpc_tracer_flag executor_trace = - GRPC_TRACER_INITIALIZER(false, "executor"); - -static void executor_thread(void *arg); - -static size_t run_closures(grpc_exec_ctx *exec_ctx, grpc_closure_list list) { - size_t n = 0; - - grpc_closure *c = list.head; - while (c != NULL) { - grpc_closure *next = c->next_data.next; - grpc_error *error = c->error_data.error; - if (GRPC_TRACER_ON(executor_trace)) { -#ifndef NDEBUG - gpr_log(GPR_DEBUG, "EXECUTOR: run %p [created by %s:%d]", c, - c->file_created, c->line_created); -#else - gpr_log(GPR_DEBUG, "EXECUTOR: run %p", c); -#endif - } -#ifndef NDEBUG - c->scheduled = false; -#endif - c->cb(exec_ctx, c->cb_arg, error); - GRPC_ERROR_UNREF(error); - c = next; - n++; - grpc_exec_ctx_flush(exec_ctx); - } - - return n; -} - -bool grpc_executor_is_threaded() { - return gpr_atm_no_barrier_load(&g_cur_threads) > 0; -} - -void grpc_executor_set_threading(grpc_exec_ctx *exec_ctx, bool threading) { - gpr_atm cur_threads = gpr_atm_no_barrier_load(&g_cur_threads); - if (threading) { - if (cur_threads > 0) return; - g_max_threads = GPR_MAX(1, 2 * gpr_cpu_num_cores()); - gpr_atm_no_barrier_store(&g_cur_threads, 1); - gpr_tls_init(&g_this_thread_state); - g_thread_state = - (thread_state *)gpr_zalloc(sizeof(thread_state) * g_max_threads); - for (size_t i = 0; i < g_max_threads; i++) { - gpr_mu_init(&g_thread_state[i].mu); - gpr_cv_init(&g_thread_state[i].cv); - g_thread_state[i].elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; - } - - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - gpr_thd_new(&g_thread_state[0].id, executor_thread, &g_thread_state[0], - &opt); - } else { - if (cur_threads == 0) return; - for (size_t i = 0; i < g_max_threads; i++) { - gpr_mu_lock(&g_thread_state[i].mu); - g_thread_state[i].shutdown = true; - gpr_cv_signal(&g_thread_state[i].cv); - gpr_mu_unlock(&g_thread_state[i].mu); - } - /* ensure no thread is adding a new thread... once this is past, then - no thread will try to add a new one either (since shutdown is true) */ - gpr_spinlock_lock(&g_adding_thread_lock); - gpr_spinlock_unlock(&g_adding_thread_lock); - for (gpr_atm i = 0; i < g_cur_threads; i++) { - gpr_thd_join(g_thread_state[i].id); - } - gpr_atm_no_barrier_store(&g_cur_threads, 0); - for (size_t i = 0; i < g_max_threads; i++) { - gpr_mu_destroy(&g_thread_state[i].mu); - gpr_cv_destroy(&g_thread_state[i].cv); - run_closures(exec_ctx, g_thread_state[i].elems); - } - gpr_free(g_thread_state); - gpr_tls_destroy(&g_this_thread_state); - } -} - -void grpc_executor_init(grpc_exec_ctx *exec_ctx) { - grpc_register_tracer(&executor_trace); - gpr_atm_no_barrier_store(&g_cur_threads, 0); - grpc_executor_set_threading(exec_ctx, true); -} - -void grpc_executor_shutdown(grpc_exec_ctx *exec_ctx) { - grpc_executor_set_threading(exec_ctx, false); -} - -static void executor_thread(void *arg) { - thread_state *ts = (thread_state *)arg; - gpr_tls_set(&g_this_thread_state, (intptr_t)ts); - - grpc_exec_ctx exec_ctx = - GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL); - - size_t subtract_depth = 0; - for (;;) { - if (GRPC_TRACER_ON(executor_trace)) { - gpr_log(GPR_DEBUG, "EXECUTOR[%d]: step (sub_depth=%" PRIdPTR ")", - (int)(ts - g_thread_state), subtract_depth); - } - gpr_mu_lock(&ts->mu); - ts->depth -= subtract_depth; - while (grpc_closure_list_empty(ts->elems) && !ts->shutdown) { - ts->queued_long_job = false; - gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_REALTIME)); - } - if (ts->shutdown) { - if (GRPC_TRACER_ON(executor_trace)) { - gpr_log(GPR_DEBUG, "EXECUTOR[%d]: shutdown", - (int)(ts - g_thread_state)); - } - gpr_mu_unlock(&ts->mu); - break; - } - GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED(&exec_ctx); - grpc_closure_list exec = ts->elems; - ts->elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; - gpr_mu_unlock(&ts->mu); - if (GRPC_TRACER_ON(executor_trace)) { - gpr_log(GPR_DEBUG, "EXECUTOR[%d]: execute", (int)(ts - g_thread_state)); - } - - subtract_depth = run_closures(&exec_ctx, exec); - } - grpc_exec_ctx_finish(&exec_ctx); -} - -static void executor_push(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_error *error, bool is_short) { - bool retry_push; - if (is_short) { - GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS(exec_ctx); - } else { - GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS(exec_ctx); - } - do { - retry_push = false; - size_t cur_thread_count = (size_t)gpr_atm_no_barrier_load(&g_cur_threads); - if (cur_thread_count == 0) { - if (GRPC_TRACER_ON(executor_trace)) { -#ifndef NDEBUG - gpr_log(GPR_DEBUG, "EXECUTOR: schedule %p (created %s:%d) inline", - closure, closure->file_created, closure->line_created); -#else - gpr_log(GPR_DEBUG, "EXECUTOR: schedule %p inline", closure); -#endif - } - grpc_closure_list_append(&exec_ctx->closure_list, closure, error); - return; - } - thread_state *ts = (thread_state *)gpr_tls_get(&g_this_thread_state); - if (ts == NULL) { - ts = &g_thread_state[GPR_HASH_POINTER(exec_ctx, cur_thread_count)]; - } else { - GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF(exec_ctx); - } - thread_state *orig_ts = ts; - - bool try_new_thread; - for (;;) { - if (GRPC_TRACER_ON(executor_trace)) { -#ifndef NDEBUG - gpr_log( - GPR_DEBUG, - "EXECUTOR: try to schedule %p (%s) (created %s:%d) to thread %d", - closure, is_short ? "short" : "long", closure->file_created, - closure->line_created, (int)(ts - g_thread_state)); -#else - gpr_log(GPR_DEBUG, "EXECUTOR: try to schedule %p (%s) to thread %d", - closure, is_short ? "short" : "long", - (int)(ts - g_thread_state)); -#endif - } - gpr_mu_lock(&ts->mu); - if (ts->queued_long_job) { - // if there's a long job queued, we never queue anything else to this - // queue (since long jobs can take 'infinite' time and we need to - // guarantee no starvation) - // ... spin through queues and try again - gpr_mu_unlock(&ts->mu); - size_t idx = (size_t)(ts - g_thread_state); - ts = &g_thread_state[(idx + 1) % cur_thread_count]; - if (ts == orig_ts) { - retry_push = true; - try_new_thread = true; - break; - } - continue; - } - if (grpc_closure_list_empty(ts->elems)) { - GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED(exec_ctx); - gpr_cv_signal(&ts->cv); - } - grpc_closure_list_append(&ts->elems, closure, error); - ts->depth++; - try_new_thread = ts->depth > MAX_DEPTH && - cur_thread_count < g_max_threads && !ts->shutdown; - if (!is_short) ts->queued_long_job = true; - gpr_mu_unlock(&ts->mu); - break; - } - if (try_new_thread && gpr_spinlock_trylock(&g_adding_thread_lock)) { - cur_thread_count = (size_t)gpr_atm_no_barrier_load(&g_cur_threads); - if (cur_thread_count < g_max_threads) { - gpr_atm_no_barrier_store(&g_cur_threads, cur_thread_count + 1); - - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - gpr_thd_new(&g_thread_state[cur_thread_count].id, executor_thread, - &g_thread_state[cur_thread_count], &opt); - } - gpr_spinlock_unlock(&g_adding_thread_lock); - } - if (retry_push) { - GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES(exec_ctx); - } - } while (retry_push); -} - -static void executor_push_short(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_error *error) { - executor_push(exec_ctx, closure, error, true); -} - -static void executor_push_long(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_error *error) { - executor_push(exec_ctx, closure, error, false); -} - -static const grpc_closure_scheduler_vtable executor_vtable_short = { - executor_push_short, executor_push_short, "executor"}; -static grpc_closure_scheduler executor_scheduler_short = { - &executor_vtable_short}; - -static const grpc_closure_scheduler_vtable executor_vtable_long = { - executor_push_long, executor_push_long, "executor"}; -static grpc_closure_scheduler executor_scheduler_long = {&executor_vtable_long}; - -grpc_closure_scheduler *grpc_executor_scheduler( - grpc_executor_job_length length) { - return length == GRPC_EXECUTOR_SHORT ? &executor_scheduler_short - : &executor_scheduler_long; -} diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc new file mode 100644 index 0000000000..892385d7d7 --- /dev/null +++ b/src/core/lib/iomgr/executor.cc @@ -0,0 +1,301 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/executor.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/support/spinlock.h" + +#define MAX_DEPTH 2 + +typedef struct { + gpr_mu mu; + gpr_cv cv; + grpc_closure_list elems; + size_t depth; + bool shutdown; + bool queued_long_job; + gpr_thd_id id; +} thread_state; + +static thread_state *g_thread_state; +static size_t g_max_threads; +static gpr_atm g_cur_threads; +static gpr_spinlock g_adding_thread_lock = GPR_SPINLOCK_STATIC_INITIALIZER; + +GPR_TLS_DECL(g_this_thread_state); + +static grpc_tracer_flag executor_trace = + GRPC_TRACER_INITIALIZER(false, "executor"); + +static void executor_thread(void *arg); + +static size_t run_closures(grpc_exec_ctx *exec_ctx, grpc_closure_list list) { + size_t n = 0; + + grpc_closure *c = list.head; + while (c != NULL) { + grpc_closure *next = c->next_data.next; + grpc_error *error = c->error_data.error; + if (GRPC_TRACER_ON(executor_trace)) { +#ifndef NDEBUG + gpr_log(GPR_DEBUG, "EXECUTOR: run %p [created by %s:%d]", c, + c->file_created, c->line_created); +#else + gpr_log(GPR_DEBUG, "EXECUTOR: run %p", c); +#endif + } +#ifndef NDEBUG + c->scheduled = false; +#endif + c->cb(exec_ctx, c->cb_arg, error); + GRPC_ERROR_UNREF(error); + c = next; + n++; + grpc_exec_ctx_flush(exec_ctx); + } + + return n; +} + +bool grpc_executor_is_threaded() { + return gpr_atm_no_barrier_load(&g_cur_threads) > 0; +} + +void grpc_executor_set_threading(grpc_exec_ctx *exec_ctx, bool threading) { + gpr_atm cur_threads = gpr_atm_no_barrier_load(&g_cur_threads); + if (threading) { + if (cur_threads > 0) return; + g_max_threads = GPR_MAX(1, 2 * gpr_cpu_num_cores()); + gpr_atm_no_barrier_store(&g_cur_threads, 1); + gpr_tls_init(&g_this_thread_state); + g_thread_state = + (thread_state *)gpr_zalloc(sizeof(thread_state) * g_max_threads); + for (size_t i = 0; i < g_max_threads; i++) { + gpr_mu_init(&g_thread_state[i].mu); + gpr_cv_init(&g_thread_state[i].cv); + g_thread_state[i].elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; + } + + gpr_thd_options opt = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&opt); + gpr_thd_new(&g_thread_state[0].id, executor_thread, &g_thread_state[0], + &opt); + } else { + if (cur_threads == 0) return; + for (size_t i = 0; i < g_max_threads; i++) { + gpr_mu_lock(&g_thread_state[i].mu); + g_thread_state[i].shutdown = true; + gpr_cv_signal(&g_thread_state[i].cv); + gpr_mu_unlock(&g_thread_state[i].mu); + } + /* ensure no thread is adding a new thread... once this is past, then + no thread will try to add a new one either (since shutdown is true) */ + gpr_spinlock_lock(&g_adding_thread_lock); + gpr_spinlock_unlock(&g_adding_thread_lock); + for (gpr_atm i = 0; i < g_cur_threads; i++) { + gpr_thd_join(g_thread_state[i].id); + } + gpr_atm_no_barrier_store(&g_cur_threads, 0); + for (size_t i = 0; i < g_max_threads; i++) { + gpr_mu_destroy(&g_thread_state[i].mu); + gpr_cv_destroy(&g_thread_state[i].cv); + run_closures(exec_ctx, g_thread_state[i].elems); + } + gpr_free(g_thread_state); + gpr_tls_destroy(&g_this_thread_state); + } +} + +void grpc_executor_init(grpc_exec_ctx *exec_ctx) { + grpc_register_tracer(&executor_trace); + gpr_atm_no_barrier_store(&g_cur_threads, 0); + grpc_executor_set_threading(exec_ctx, true); +} + +void grpc_executor_shutdown(grpc_exec_ctx *exec_ctx) { + grpc_executor_set_threading(exec_ctx, false); +} + +static void executor_thread(void *arg) { + thread_state *ts = (thread_state *)arg; + gpr_tls_set(&g_this_thread_state, (intptr_t)ts); + + grpc_exec_ctx exec_ctx = + GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL); + + size_t subtract_depth = 0; + for (;;) { + if (GRPC_TRACER_ON(executor_trace)) { + gpr_log(GPR_DEBUG, "EXECUTOR[%d]: step (sub_depth=%" PRIdPTR ")", + (int)(ts - g_thread_state), subtract_depth); + } + gpr_mu_lock(&ts->mu); + ts->depth -= subtract_depth; + while (grpc_closure_list_empty(ts->elems) && !ts->shutdown) { + ts->queued_long_job = false; + gpr_cv_wait(&ts->cv, &ts->mu, gpr_inf_future(GPR_CLOCK_REALTIME)); + } + if (ts->shutdown) { + if (GRPC_TRACER_ON(executor_trace)) { + gpr_log(GPR_DEBUG, "EXECUTOR[%d]: shutdown", + (int)(ts - g_thread_state)); + } + gpr_mu_unlock(&ts->mu); + break; + } + GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED(&exec_ctx); + grpc_closure_list exec = ts->elems; + ts->elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; + gpr_mu_unlock(&ts->mu); + if (GRPC_TRACER_ON(executor_trace)) { + gpr_log(GPR_DEBUG, "EXECUTOR[%d]: execute", (int)(ts - g_thread_state)); + } + + subtract_depth = run_closures(&exec_ctx, exec); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +static void executor_push(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_error *error, bool is_short) { + bool retry_push; + if (is_short) { + GRPC_STATS_INC_EXECUTOR_SCHEDULED_SHORT_ITEMS(exec_ctx); + } else { + GRPC_STATS_INC_EXECUTOR_SCHEDULED_LONG_ITEMS(exec_ctx); + } + do { + retry_push = false; + size_t cur_thread_count = (size_t)gpr_atm_no_barrier_load(&g_cur_threads); + if (cur_thread_count == 0) { + if (GRPC_TRACER_ON(executor_trace)) { +#ifndef NDEBUG + gpr_log(GPR_DEBUG, "EXECUTOR: schedule %p (created %s:%d) inline", + closure, closure->file_created, closure->line_created); +#else + gpr_log(GPR_DEBUG, "EXECUTOR: schedule %p inline", closure); +#endif + } + grpc_closure_list_append(&exec_ctx->closure_list, closure, error); + return; + } + thread_state *ts = (thread_state *)gpr_tls_get(&g_this_thread_state); + if (ts == NULL) { + ts = &g_thread_state[GPR_HASH_POINTER(exec_ctx, cur_thread_count)]; + } else { + GRPC_STATS_INC_EXECUTOR_SCHEDULED_TO_SELF(exec_ctx); + } + thread_state *orig_ts = ts; + + bool try_new_thread; + for (;;) { + if (GRPC_TRACER_ON(executor_trace)) { +#ifndef NDEBUG + gpr_log( + GPR_DEBUG, + "EXECUTOR: try to schedule %p (%s) (created %s:%d) to thread %d", + closure, is_short ? "short" : "long", closure->file_created, + closure->line_created, (int)(ts - g_thread_state)); +#else + gpr_log(GPR_DEBUG, "EXECUTOR: try to schedule %p (%s) to thread %d", + closure, is_short ? "short" : "long", + (int)(ts - g_thread_state)); +#endif + } + gpr_mu_lock(&ts->mu); + if (ts->queued_long_job) { + // if there's a long job queued, we never queue anything else to this + // queue (since long jobs can take 'infinite' time and we need to + // guarantee no starvation) + // ... spin through queues and try again + gpr_mu_unlock(&ts->mu); + size_t idx = (size_t)(ts - g_thread_state); + ts = &g_thread_state[(idx + 1) % cur_thread_count]; + if (ts == orig_ts) { + retry_push = true; + try_new_thread = true; + break; + } + continue; + } + if (grpc_closure_list_empty(ts->elems)) { + GRPC_STATS_INC_EXECUTOR_WAKEUP_INITIATED(exec_ctx); + gpr_cv_signal(&ts->cv); + } + grpc_closure_list_append(&ts->elems, closure, error); + ts->depth++; + try_new_thread = ts->depth > MAX_DEPTH && + cur_thread_count < g_max_threads && !ts->shutdown; + if (!is_short) ts->queued_long_job = true; + gpr_mu_unlock(&ts->mu); + break; + } + if (try_new_thread && gpr_spinlock_trylock(&g_adding_thread_lock)) { + cur_thread_count = (size_t)gpr_atm_no_barrier_load(&g_cur_threads); + if (cur_thread_count < g_max_threads) { + gpr_atm_no_barrier_store(&g_cur_threads, cur_thread_count + 1); + + gpr_thd_options opt = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&opt); + gpr_thd_new(&g_thread_state[cur_thread_count].id, executor_thread, + &g_thread_state[cur_thread_count], &opt); + } + gpr_spinlock_unlock(&g_adding_thread_lock); + } + if (retry_push) { + GRPC_STATS_INC_EXECUTOR_PUSH_RETRIES(exec_ctx); + } + } while (retry_push); +} + +static void executor_push_short(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_error *error) { + executor_push(exec_ctx, closure, error, true); +} + +static void executor_push_long(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_error *error) { + executor_push(exec_ctx, closure, error, false); +} + +static const grpc_closure_scheduler_vtable executor_vtable_short = { + executor_push_short, executor_push_short, "executor"}; +static grpc_closure_scheduler executor_scheduler_short = { + &executor_vtable_short}; + +static const grpc_closure_scheduler_vtable executor_vtable_long = { + executor_push_long, executor_push_long, "executor"}; +static grpc_closure_scheduler executor_scheduler_long = {&executor_vtable_long}; + +grpc_closure_scheduler *grpc_executor_scheduler( + grpc_executor_job_length length) { + return length == GRPC_EXECUTOR_SHORT ? &executor_scheduler_short + : &executor_scheduler_long; +} diff --git a/src/core/lib/iomgr/gethostname_fallback.c b/src/core/lib/iomgr/gethostname_fallback.c deleted file mode 100644 index 6229461568..0000000000 --- a/src/core/lib/iomgr/gethostname_fallback.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_GETHOSTNAME_FALLBACK - -#include - -char *grpc_gethostname() { return NULL; } - -#endif // GRPC_GETHOSTNAME_FALLBACK diff --git a/src/core/lib/iomgr/gethostname_fallback.cc b/src/core/lib/iomgr/gethostname_fallback.cc new file mode 100644 index 0000000000..6229461568 --- /dev/null +++ b/src/core/lib/iomgr/gethostname_fallback.cc @@ -0,0 +1,27 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_GETHOSTNAME_FALLBACK + +#include + +char *grpc_gethostname() { return NULL; } + +#endif // GRPC_GETHOSTNAME_FALLBACK diff --git a/src/core/lib/iomgr/gethostname_host_name_max.c b/src/core/lib/iomgr/gethostname_host_name_max.c deleted file mode 100644 index 4d0511412e..0000000000 --- a/src/core/lib/iomgr/gethostname_host_name_max.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_HOST_NAME_MAX - -#include -#include - -#include - -char *grpc_gethostname() { - char *hostname = (char *)gpr_malloc(HOST_NAME_MAX); - if (gethostname(hostname, HOST_NAME_MAX) != 0) { - gpr_free(hostname); - return NULL; - } - return hostname; -} - -#endif // GRPC_POSIX_HOST_NAME_MAX diff --git a/src/core/lib/iomgr/gethostname_host_name_max.cc b/src/core/lib/iomgr/gethostname_host_name_max.cc new file mode 100644 index 0000000000..4d0511412e --- /dev/null +++ b/src/core/lib/iomgr/gethostname_host_name_max.cc @@ -0,0 +1,37 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_HOST_NAME_MAX + +#include +#include + +#include + +char *grpc_gethostname() { + char *hostname = (char *)gpr_malloc(HOST_NAME_MAX); + if (gethostname(hostname, HOST_NAME_MAX) != 0) { + gpr_free(hostname); + return NULL; + } + return hostname; +} + +#endif // GRPC_POSIX_HOST_NAME_MAX diff --git a/src/core/lib/iomgr/gethostname_sysconf.c b/src/core/lib/iomgr/gethostname_sysconf.c deleted file mode 100644 index 51bac5d69d..0000000000 --- a/src/core/lib/iomgr/gethostname_sysconf.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SYSCONF - -#include - -#include - -char *grpc_gethostname() { - size_t host_name_max = (size_t)sysconf(_SC_HOST_NAME_MAX); - char *hostname = (char *)gpr_malloc(host_name_max); - if (gethostname(hostname, host_name_max) != 0) { - gpr_free(hostname); - return NULL; - } - return hostname; -} - -#endif // GRPC_POSIX_SYSCONF diff --git a/src/core/lib/iomgr/gethostname_sysconf.cc b/src/core/lib/iomgr/gethostname_sysconf.cc new file mode 100644 index 0000000000..51bac5d69d --- /dev/null +++ b/src/core/lib/iomgr/gethostname_sysconf.cc @@ -0,0 +1,37 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SYSCONF + +#include + +#include + +char *grpc_gethostname() { + size_t host_name_max = (size_t)sysconf(_SC_HOST_NAME_MAX); + char *hostname = (char *)gpr_malloc(host_name_max); + if (gethostname(hostname, host_name_max) != 0) { + gpr_free(hostname); + return NULL; + } + return hostname; +} + +#endif // GRPC_POSIX_SYSCONF diff --git a/src/core/lib/iomgr/iocp_windows.c b/src/core/lib/iomgr/iocp_windows.c deleted file mode 100644 index c082179c0b..0000000000 --- a/src/core/lib/iomgr/iocp_windows.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include - -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/socket_windows.h" -#include "src/core/lib/iomgr/timer.h" - -static ULONG g_iocp_kick_token; -static OVERLAPPED g_iocp_custom_overlap; - -static gpr_atm g_custom_events = 0; - -static HANDLE g_iocp; - -static DWORD deadline_to_millis_timeout(gpr_timespec deadline, - gpr_timespec now) { - gpr_timespec timeout; - static const int64_t max_spin_polling_us = 10; - if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { - return INFINITE; - } - if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( - max_spin_polling_us, - GPR_TIMESPAN))) <= 0) { - return 0; - } - timeout = gpr_time_sub(deadline, now); - return (DWORD)gpr_time_to_millis(gpr_time_add( - timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); -} - -grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx, - gpr_timespec deadline) { - BOOL success; - DWORD bytes = 0; - DWORD flags = 0; - ULONG_PTR completion_key; - LPOVERLAPPED overlapped; - grpc_winsocket *socket; - grpc_winsocket_callback_info *info; - GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); - success = GetQueuedCompletionStatus( - g_iocp, &bytes, &completion_key, &overlapped, - deadline_to_millis_timeout(deadline, gpr_now(deadline.clock_type))); - if (success == 0 && overlapped == NULL) { - return GRPC_IOCP_WORK_TIMEOUT; - } - GPR_ASSERT(completion_key && overlapped); - if (overlapped == &g_iocp_custom_overlap) { - gpr_atm_full_fetch_add(&g_custom_events, -1); - if (completion_key == (ULONG_PTR)&g_iocp_kick_token) { - /* We were awoken from a kick. */ - return GRPC_IOCP_WORK_KICK; - } - gpr_log(GPR_ERROR, "Unknown custom completion key."); - abort(); - } - - socket = (grpc_winsocket *)completion_key; - if (overlapped == &socket->write_info.overlapped) { - info = &socket->write_info; - } else if (overlapped == &socket->read_info.overlapped) { - info = &socket->read_info; - } else { - abort(); - } - success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, - FALSE, &flags); - info->bytes_transfered = bytes; - info->wsa_error = success ? 0 : WSAGetLastError(); - GPR_ASSERT(overlapped == &info->overlapped); - grpc_socket_become_ready(exec_ctx, socket, info); - return GRPC_IOCP_WORK_WORK; -} - -void grpc_iocp_init(void) { - g_iocp = - CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); - GPR_ASSERT(g_iocp); -} - -void grpc_iocp_kick(void) { - BOOL success; - - gpr_atm_full_fetch_add(&g_custom_events, 1); - success = PostQueuedCompletionStatus(g_iocp, 0, (ULONG_PTR)&g_iocp_kick_token, - &g_iocp_custom_overlap); - GPR_ASSERT(success); -} - -void grpc_iocp_flush(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_iocp_work_status work_status; - - do { - work_status = grpc_iocp_work(&exec_ctx, gpr_inf_past(GPR_CLOCK_MONOTONIC)); - } while (work_status == GRPC_IOCP_WORK_KICK || - grpc_exec_ctx_flush(&exec_ctx)); -} - -void grpc_iocp_shutdown(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - while (gpr_atm_acq_load(&g_custom_events)) { - grpc_iocp_work(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC)); - grpc_exec_ctx_flush(&exec_ctx); - } - grpc_exec_ctx_finish(&exec_ctx); - GPR_ASSERT(CloseHandle(g_iocp)); -} - -void grpc_iocp_add_socket(grpc_winsocket *socket) { - HANDLE ret; - if (socket->added_to_iocp) return; - ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp, - (uintptr_t)socket, 0); - if (!ret) { - char *utf8_message = gpr_format_message(WSAGetLastError()); - gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message); - gpr_free(utf8_message); - __debugbreak(); - abort(); - } - socket->added_to_iocp = 1; - GPR_ASSERT(ret == g_iocp); -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/iocp_windows.cc b/src/core/lib/iomgr/iocp_windows.cc new file mode 100644 index 0000000000..c082179c0b --- /dev/null +++ b/src/core/lib/iomgr/iocp_windows.cc @@ -0,0 +1,155 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include + +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/timer.h" + +static ULONG g_iocp_kick_token; +static OVERLAPPED g_iocp_custom_overlap; + +static gpr_atm g_custom_events = 0; + +static HANDLE g_iocp; + +static DWORD deadline_to_millis_timeout(gpr_timespec deadline, + gpr_timespec now) { + gpr_timespec timeout; + static const int64_t max_spin_polling_us = 10; + if (gpr_time_cmp(deadline, gpr_inf_future(deadline.clock_type)) == 0) { + return INFINITE; + } + if (gpr_time_cmp(deadline, gpr_time_add(now, gpr_time_from_micros( + max_spin_polling_us, + GPR_TIMESPAN))) <= 0) { + return 0; + } + timeout = gpr_time_sub(deadline, now); + return (DWORD)gpr_time_to_millis(gpr_time_add( + timeout, gpr_time_from_nanos(GPR_NS_PER_MS - 1, GPR_TIMESPAN))); +} + +grpc_iocp_work_status grpc_iocp_work(grpc_exec_ctx *exec_ctx, + gpr_timespec deadline) { + BOOL success; + DWORD bytes = 0; + DWORD flags = 0; + ULONG_PTR completion_key; + LPOVERLAPPED overlapped; + grpc_winsocket *socket; + grpc_winsocket_callback_info *info; + GRPC_STATS_INC_SYSCALL_POLL(exec_ctx); + success = GetQueuedCompletionStatus( + g_iocp, &bytes, &completion_key, &overlapped, + deadline_to_millis_timeout(deadline, gpr_now(deadline.clock_type))); + if (success == 0 && overlapped == NULL) { + return GRPC_IOCP_WORK_TIMEOUT; + } + GPR_ASSERT(completion_key && overlapped); + if (overlapped == &g_iocp_custom_overlap) { + gpr_atm_full_fetch_add(&g_custom_events, -1); + if (completion_key == (ULONG_PTR)&g_iocp_kick_token) { + /* We were awoken from a kick. */ + return GRPC_IOCP_WORK_KICK; + } + gpr_log(GPR_ERROR, "Unknown custom completion key."); + abort(); + } + + socket = (grpc_winsocket *)completion_key; + if (overlapped == &socket->write_info.overlapped) { + info = &socket->write_info; + } else if (overlapped == &socket->read_info.overlapped) { + info = &socket->read_info; + } else { + abort(); + } + success = WSAGetOverlappedResult(socket->socket, &info->overlapped, &bytes, + FALSE, &flags); + info->bytes_transfered = bytes; + info->wsa_error = success ? 0 : WSAGetLastError(); + GPR_ASSERT(overlapped == &info->overlapped); + grpc_socket_become_ready(exec_ctx, socket, info); + return GRPC_IOCP_WORK_WORK; +} + +void grpc_iocp_init(void) { + g_iocp = + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (ULONG_PTR)NULL, 0); + GPR_ASSERT(g_iocp); +} + +void grpc_iocp_kick(void) { + BOOL success; + + gpr_atm_full_fetch_add(&g_custom_events, 1); + success = PostQueuedCompletionStatus(g_iocp, 0, (ULONG_PTR)&g_iocp_kick_token, + &g_iocp_custom_overlap); + GPR_ASSERT(success); +} + +void grpc_iocp_flush(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_iocp_work_status work_status; + + do { + work_status = grpc_iocp_work(&exec_ctx, gpr_inf_past(GPR_CLOCK_MONOTONIC)); + } while (work_status == GRPC_IOCP_WORK_KICK || + grpc_exec_ctx_flush(&exec_ctx)); +} + +void grpc_iocp_shutdown(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (gpr_atm_acq_load(&g_custom_events)) { + grpc_iocp_work(&exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC)); + grpc_exec_ctx_flush(&exec_ctx); + } + grpc_exec_ctx_finish(&exec_ctx); + GPR_ASSERT(CloseHandle(g_iocp)); +} + +void grpc_iocp_add_socket(grpc_winsocket *socket) { + HANDLE ret; + if (socket->added_to_iocp) return; + ret = CreateIoCompletionPort((HANDLE)socket->socket, g_iocp, + (uintptr_t)socket, 0); + if (!ret) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "Unable to add socket to iocp: %s", utf8_message); + gpr_free(utf8_message); + __debugbreak(); + abort(); + } + socket->added_to_iocp = 1; + GPR_ASSERT(ret == g_iocp); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr.c b/src/core/lib/iomgr/iomgr.c deleted file mode 100644 index f63f190155..0000000000 --- a/src/core/lib/iomgr/iomgr.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/iomgr.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/network_status_tracker.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/timer_manager.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" - -static gpr_mu g_mu; -static gpr_cv g_rcv; -static int g_shutdown; -static grpc_iomgr_object g_root_object; - -void grpc_iomgr_init(grpc_exec_ctx *exec_ctx) { - g_shutdown = 0; - gpr_mu_init(&g_mu); - gpr_cv_init(&g_rcv); - grpc_exec_ctx_global_init(); - grpc_executor_init(exec_ctx); - grpc_timer_list_init(gpr_now(GPR_CLOCK_MONOTONIC)); - g_root_object.next = g_root_object.prev = &g_root_object; - g_root_object.name = (char *)"root"; - grpc_network_status_init(); - grpc_iomgr_platform_init(); -} - -void grpc_iomgr_start(grpc_exec_ctx *exec_ctx) { grpc_timer_manager_init(); } - -static size_t count_objects(void) { - grpc_iomgr_object *obj; - size_t n = 0; - for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { - n++; - } - return n; -} - -static void dump_objects(const char *kind) { - grpc_iomgr_object *obj; - for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { - gpr_log(GPR_DEBUG, "%s OBJECT: %s %p", kind, obj->name, obj); - } -} - -void grpc_iomgr_shutdown(grpc_exec_ctx *exec_ctx) { - gpr_timespec shutdown_deadline = gpr_time_add( - gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN)); - gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME); - - grpc_timer_manager_shutdown(); - grpc_iomgr_platform_flush(); - grpc_executor_shutdown(exec_ctx); - - gpr_mu_lock(&g_mu); - g_shutdown = 1; - while (g_root_object.next != &g_root_object) { - if (gpr_time_cmp( - gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time), - gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { - if (g_root_object.next != &g_root_object) { - gpr_log(GPR_DEBUG, - "Waiting for %" PRIuPTR " iomgr objects to be destroyed", - count_objects()); - } - last_warning_time = gpr_now(GPR_CLOCK_REALTIME); - } - if (grpc_timer_check(exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL) == - GRPC_TIMERS_FIRED) { - gpr_mu_unlock(&g_mu); - grpc_exec_ctx_flush(exec_ctx); - grpc_iomgr_platform_flush(); - gpr_mu_lock(&g_mu); - continue; - } - if (g_root_object.next != &g_root_object) { - if (grpc_iomgr_abort_on_leaks()) { - gpr_log(GPR_DEBUG, "Failed to free %" PRIuPTR - " iomgr objects before shutdown deadline: " - "memory leaks are likely", - count_objects()); - dump_objects("LEAKED"); - abort(); - } - gpr_timespec short_deadline = gpr_time_add( - gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN)); - if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) { - if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) { - if (g_root_object.next != &g_root_object) { - gpr_log(GPR_DEBUG, "Failed to free %" PRIuPTR - " iomgr objects before shutdown deadline: " - "memory leaks are likely", - count_objects()); - dump_objects("LEAKED"); - } - break; - } - } - } - } - gpr_mu_unlock(&g_mu); - - grpc_timer_list_shutdown(exec_ctx); - grpc_exec_ctx_flush(exec_ctx); - - /* ensure all threads have left g_mu */ - gpr_mu_lock(&g_mu); - gpr_mu_unlock(&g_mu); - - grpc_iomgr_platform_shutdown(); - grpc_exec_ctx_global_shutdown(); - grpc_network_status_shutdown(); - gpr_mu_destroy(&g_mu); - gpr_cv_destroy(&g_rcv); -} - -void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name) { - obj->name = gpr_strdup(name); - gpr_mu_lock(&g_mu); - obj->next = &g_root_object; - obj->prev = g_root_object.prev; - obj->next->prev = obj->prev->next = obj; - gpr_mu_unlock(&g_mu); -} - -void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) { - gpr_mu_lock(&g_mu); - obj->next->prev = obj->prev; - obj->prev->next = obj->next; - gpr_cv_signal(&g_rcv); - gpr_mu_unlock(&g_mu); - gpr_free(obj->name); -} - -bool grpc_iomgr_abort_on_leaks(void) { - char *env = gpr_getenv("GRPC_ABORT_ON_LEAKS"); - bool should_we = gpr_is_true(env); - gpr_free(env); - return should_we; -} diff --git a/src/core/lib/iomgr/iomgr.cc b/src/core/lib/iomgr/iomgr.cc new file mode 100644 index 0000000000..f63f190155 --- /dev/null +++ b/src/core/lib/iomgr/iomgr.cc @@ -0,0 +1,170 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/iomgr.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/timer_manager.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +static gpr_mu g_mu; +static gpr_cv g_rcv; +static int g_shutdown; +static grpc_iomgr_object g_root_object; + +void grpc_iomgr_init(grpc_exec_ctx *exec_ctx) { + g_shutdown = 0; + gpr_mu_init(&g_mu); + gpr_cv_init(&g_rcv); + grpc_exec_ctx_global_init(); + grpc_executor_init(exec_ctx); + grpc_timer_list_init(gpr_now(GPR_CLOCK_MONOTONIC)); + g_root_object.next = g_root_object.prev = &g_root_object; + g_root_object.name = (char *)"root"; + grpc_network_status_init(); + grpc_iomgr_platform_init(); +} + +void grpc_iomgr_start(grpc_exec_ctx *exec_ctx) { grpc_timer_manager_init(); } + +static size_t count_objects(void) { + grpc_iomgr_object *obj; + size_t n = 0; + for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { + n++; + } + return n; +} + +static void dump_objects(const char *kind) { + grpc_iomgr_object *obj; + for (obj = g_root_object.next; obj != &g_root_object; obj = obj->next) { + gpr_log(GPR_DEBUG, "%s OBJECT: %s %p", kind, obj->name, obj); + } +} + +void grpc_iomgr_shutdown(grpc_exec_ctx *exec_ctx) { + gpr_timespec shutdown_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_seconds(10, GPR_TIMESPAN)); + gpr_timespec last_warning_time = gpr_now(GPR_CLOCK_REALTIME); + + grpc_timer_manager_shutdown(); + grpc_iomgr_platform_flush(); + grpc_executor_shutdown(exec_ctx); + + gpr_mu_lock(&g_mu); + g_shutdown = 1; + while (g_root_object.next != &g_root_object) { + if (gpr_time_cmp( + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), last_warning_time), + gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { + if (g_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, + "Waiting for %" PRIuPTR " iomgr objects to be destroyed", + count_objects()); + } + last_warning_time = gpr_now(GPR_CLOCK_REALTIME); + } + if (grpc_timer_check(exec_ctx, gpr_inf_future(GPR_CLOCK_MONOTONIC), NULL) == + GRPC_TIMERS_FIRED) { + gpr_mu_unlock(&g_mu); + grpc_exec_ctx_flush(exec_ctx); + grpc_iomgr_platform_flush(); + gpr_mu_lock(&g_mu); + continue; + } + if (g_root_object.next != &g_root_object) { + if (grpc_iomgr_abort_on_leaks()) { + gpr_log(GPR_DEBUG, "Failed to free %" PRIuPTR + " iomgr objects before shutdown deadline: " + "memory leaks are likely", + count_objects()); + dump_objects("LEAKED"); + abort(); + } + gpr_timespec short_deadline = gpr_time_add( + gpr_now(GPR_CLOCK_REALTIME), gpr_time_from_millis(100, GPR_TIMESPAN)); + if (gpr_cv_wait(&g_rcv, &g_mu, short_deadline)) { + if (gpr_time_cmp(gpr_now(GPR_CLOCK_REALTIME), shutdown_deadline) > 0) { + if (g_root_object.next != &g_root_object) { + gpr_log(GPR_DEBUG, "Failed to free %" PRIuPTR + " iomgr objects before shutdown deadline: " + "memory leaks are likely", + count_objects()); + dump_objects("LEAKED"); + } + break; + } + } + } + } + gpr_mu_unlock(&g_mu); + + grpc_timer_list_shutdown(exec_ctx); + grpc_exec_ctx_flush(exec_ctx); + + /* ensure all threads have left g_mu */ + gpr_mu_lock(&g_mu); + gpr_mu_unlock(&g_mu); + + grpc_iomgr_platform_shutdown(); + grpc_exec_ctx_global_shutdown(); + grpc_network_status_shutdown(); + gpr_mu_destroy(&g_mu); + gpr_cv_destroy(&g_rcv); +} + +void grpc_iomgr_register_object(grpc_iomgr_object *obj, const char *name) { + obj->name = gpr_strdup(name); + gpr_mu_lock(&g_mu); + obj->next = &g_root_object; + obj->prev = g_root_object.prev; + obj->next->prev = obj->prev->next = obj; + gpr_mu_unlock(&g_mu); +} + +void grpc_iomgr_unregister_object(grpc_iomgr_object *obj) { + gpr_mu_lock(&g_mu); + obj->next->prev = obj->prev; + obj->prev->next = obj->next; + gpr_cv_signal(&g_rcv); + gpr_mu_unlock(&g_mu); + gpr_free(obj->name); +} + +bool grpc_iomgr_abort_on_leaks(void) { + char *env = gpr_getenv("GRPC_ABORT_ON_LEAKS"); + bool should_we = gpr_is_true(env); + gpr_free(env); + return should_we; +} diff --git a/src/core/lib/iomgr/iomgr_posix.c b/src/core/lib/iomgr/iomgr_posix.c deleted file mode 100644 index f5875a247e..0000000000 --- a/src/core/lib/iomgr/iomgr_posix.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_posix.h" -#include "src/core/lib/iomgr/tcp_posix.h" - -void grpc_iomgr_platform_init(void) { - grpc_wakeup_fd_global_init(); - grpc_event_engine_init(); - grpc_register_tracer(&grpc_tcp_trace); -} - -void grpc_iomgr_platform_flush(void) {} - -void grpc_iomgr_platform_shutdown(void) { - grpc_event_engine_shutdown(); - grpc_wakeup_fd_global_destroy(); -} - -#endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr_posix.cc b/src/core/lib/iomgr/iomgr_posix.cc new file mode 100644 index 0000000000..f5875a247e --- /dev/null +++ b/src/core/lib/iomgr/iomgr_posix.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" + +void grpc_iomgr_platform_init(void) { + grpc_wakeup_fd_global_init(); + grpc_event_engine_init(); + grpc_register_tracer(&grpc_tcp_trace); +} + +void grpc_iomgr_platform_flush(void) {} + +void grpc_iomgr_platform_shutdown(void) { + grpc_event_engine_shutdown(); + grpc_wakeup_fd_global_destroy(); +} + +#endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr_uv.c b/src/core/lib/iomgr/iomgr_uv.c deleted file mode 100644 index df5d23af3b..0000000000 --- a/src/core/lib/iomgr/iomgr_uv.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/pollset_uv.h" -#include "src/core/lib/iomgr/tcp_uv.h" - -gpr_thd_id g_init_thread; - -void grpc_iomgr_platform_init(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_pollset_global_init(); - grpc_register_tracer(&grpc_tcp_trace); - grpc_executor_set_threading(&exec_ctx, false); - g_init_thread = gpr_thd_currentid(); - grpc_exec_ctx_finish(&exec_ctx); -} -void grpc_iomgr_platform_flush(void) {} -void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/iomgr_uv.cc b/src/core/lib/iomgr/iomgr_uv.cc new file mode 100644 index 0000000000..df5d23af3b --- /dev/null +++ b/src/core/lib/iomgr/iomgr_uv.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/pollset_uv.h" +#include "src/core/lib/iomgr/tcp_uv.h" + +gpr_thd_id g_init_thread; + +void grpc_iomgr_platform_init(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_pollset_global_init(); + grpc_register_tracer(&grpc_tcp_trace); + grpc_executor_set_threading(&exec_ctx, false); + g_init_thread = gpr_thd_currentid(); + grpc_exec_ctx_finish(&exec_ctx); +} +void grpc_iomgr_platform_flush(void) {} +void grpc_iomgr_platform_shutdown(void) { grpc_pollset_global_shutdown(); } + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/iomgr_windows.c b/src/core/lib/iomgr/iomgr_windows.c deleted file mode 100644 index 630370166d..0000000000 --- a/src/core/lib/iomgr/iomgr_windows.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include "src/core/lib/iomgr/sockaddr_windows.h" - -#include - -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "src/core/lib/iomgr/pollset_windows.h" -#include "src/core/lib/iomgr/socket_windows.h" - -/* Windows' io manager is going to be fully designed using IO completion - ports. All of what we're doing here is basically make sure that - Windows sockets are initialized in and out. */ - -static void winsock_init(void) { - WSADATA wsaData; - int status = WSAStartup(MAKEWORD(2, 0), &wsaData); - GPR_ASSERT(status == 0); -} - -static void winsock_shutdown(void) { - int status = WSACleanup(); - GPR_ASSERT(status == 0); -} - -void grpc_iomgr_platform_init(void) { - winsock_init(); - grpc_iocp_init(); - grpc_pollset_global_init(); -} - -void grpc_iomgr_platform_flush(void) { grpc_iocp_flush(); } - -void grpc_iomgr_platform_shutdown(void) { - grpc_pollset_global_shutdown(); - grpc_iocp_shutdown(); - winsock_shutdown(); -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/iomgr_windows.cc b/src/core/lib/iomgr/iomgr_windows.cc new file mode 100644 index 0000000000..630370166d --- /dev/null +++ b/src/core/lib/iomgr/iomgr_windows.cc @@ -0,0 +1,61 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#include + +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/socket_windows.h" + +/* Windows' io manager is going to be fully designed using IO completion + ports. All of what we're doing here is basically make sure that + Windows sockets are initialized in and out. */ + +static void winsock_init(void) { + WSADATA wsaData; + int status = WSAStartup(MAKEWORD(2, 0), &wsaData); + GPR_ASSERT(status == 0); +} + +static void winsock_shutdown(void) { + int status = WSACleanup(); + GPR_ASSERT(status == 0); +} + +void grpc_iomgr_platform_init(void) { + winsock_init(); + grpc_iocp_init(); + grpc_pollset_global_init(); +} + +void grpc_iomgr_platform_flush(void) { grpc_iocp_flush(); } + +void grpc_iomgr_platform_shutdown(void) { + grpc_pollset_global_shutdown(); + grpc_iocp_shutdown(); + winsock_shutdown(); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/is_epollexclusive_available.c b/src/core/lib/iomgr/is_epollexclusive_available.c deleted file mode 100644 index d08844c0df..0000000000 --- a/src/core/lib/iomgr/is_epollexclusive_available.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#include "src/core/lib/iomgr/is_epollexclusive_available.h" - -#ifdef GRPC_LINUX_EPOLL - -#include - -#include -#include -#include - -#include "src/core/lib/iomgr/sys_epoll_wrapper.h" - -/* This polling engine is only relevant on linux kernels supporting epoll() */ -bool grpc_is_epollexclusive_available(void) { - static bool logged_why_not = false; - - int fd = epoll_create1(EPOLL_CLOEXEC); - if (fd < 0) { - if (!logged_why_not) { - gpr_log(GPR_ERROR, - "epoll_create1 failed with error: %d. Not using epollex polling " - "engine.", - fd); - logged_why_not = true; - } - return false; - } - int evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); - if (evfd < 0) { - if (!logged_why_not) { - gpr_log(GPR_ERROR, - "eventfd failed with error: %d. Not using epollex polling " - "engine.", - fd); - logged_why_not = true; - } - close(fd); - return false; - } - struct epoll_event ev; - /* choose events that should cause an error on - EPOLLEXCLUSIVE enabled kernels - specifically the combination of - EPOLLONESHOT and EPOLLEXCLUSIVE */ - ev.events = (uint32_t)(EPOLLET | EPOLLIN | EPOLLEXCLUSIVE | EPOLLONESHOT); - ev.data.ptr = NULL; - if (epoll_ctl(fd, EPOLL_CTL_ADD, evfd, &ev) != 0) { - if (errno != EINVAL) { - if (!logged_why_not) { - gpr_log( - GPR_ERROR, - "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT failed with error: " - "%d. Not using epollex polling engine.", - errno); - logged_why_not = true; - } - close(fd); - close(evfd); - return false; - } - } else { - if (!logged_why_not) { - gpr_log(GPR_ERROR, - "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT succeeded. This is " - "evidence of no EPOLLEXCLUSIVE support. Not using " - "epollex polling engine."); - logged_why_not = true; - } - close(fd); - close(evfd); - return false; - } - close(evfd); - close(fd); - return true; -} - -#else - -bool grpc_is_epollexclusive_available(void) { return false; } - -#endif diff --git a/src/core/lib/iomgr/is_epollexclusive_available.cc b/src/core/lib/iomgr/is_epollexclusive_available.cc new file mode 100644 index 0000000000..d08844c0df --- /dev/null +++ b/src/core/lib/iomgr/is_epollexclusive_available.cc @@ -0,0 +1,101 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#include "src/core/lib/iomgr/is_epollexclusive_available.h" + +#ifdef GRPC_LINUX_EPOLL + +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/sys_epoll_wrapper.h" + +/* This polling engine is only relevant on linux kernels supporting epoll() */ +bool grpc_is_epollexclusive_available(void) { + static bool logged_why_not = false; + + int fd = epoll_create1(EPOLL_CLOEXEC); + if (fd < 0) { + if (!logged_why_not) { + gpr_log(GPR_ERROR, + "epoll_create1 failed with error: %d. Not using epollex polling " + "engine.", + fd); + logged_why_not = true; + } + return false; + } + int evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (evfd < 0) { + if (!logged_why_not) { + gpr_log(GPR_ERROR, + "eventfd failed with error: %d. Not using epollex polling " + "engine.", + fd); + logged_why_not = true; + } + close(fd); + return false; + } + struct epoll_event ev; + /* choose events that should cause an error on + EPOLLEXCLUSIVE enabled kernels - specifically the combination of + EPOLLONESHOT and EPOLLEXCLUSIVE */ + ev.events = (uint32_t)(EPOLLET | EPOLLIN | EPOLLEXCLUSIVE | EPOLLONESHOT); + ev.data.ptr = NULL; + if (epoll_ctl(fd, EPOLL_CTL_ADD, evfd, &ev) != 0) { + if (errno != EINVAL) { + if (!logged_why_not) { + gpr_log( + GPR_ERROR, + "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT failed with error: " + "%d. Not using epollex polling engine.", + errno); + logged_why_not = true; + } + close(fd); + close(evfd); + return false; + } + } else { + if (!logged_why_not) { + gpr_log(GPR_ERROR, + "epoll_ctl with EPOLLEXCLUSIVE | EPOLLONESHOT succeeded. This is " + "evidence of no EPOLLEXCLUSIVE support. Not using " + "epollex polling engine."); + logged_why_not = true; + } + close(fd); + close(evfd); + return false; + } + close(evfd); + close(fd); + return true; +} + +#else + +bool grpc_is_epollexclusive_available(void) { return false; } + +#endif diff --git a/src/core/lib/iomgr/load_file.c b/src/core/lib/iomgr/load_file.c deleted file mode 100644 index 0b4d41ea4b..0000000000 --- a/src/core/lib/iomgr/load_file.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/load_file.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/string.h" - -grpc_error *grpc_load_file(const char *filename, int add_null_terminator, - grpc_slice *output) { - unsigned char *contents = NULL; - size_t contents_size = 0; - grpc_slice result = grpc_empty_slice(); - FILE *file; - size_t bytes_read = 0; - grpc_error *error = GRPC_ERROR_NONE; - - GRPC_SCHEDULING_START_BLOCKING_REGION; - file = fopen(filename, "rb"); - if (file == NULL) { - error = GRPC_OS_ERROR(errno, "fopen"); - goto end; - } - fseek(file, 0, SEEK_END); - /* Converting to size_t on the assumption that it will not fail */ - contents_size = (size_t)ftell(file); - fseek(file, 0, SEEK_SET); - contents = (unsigned char *)gpr_malloc(contents_size + - (add_null_terminator ? 1 : 0)); - bytes_read = fread(contents, 1, contents_size, file); - if (bytes_read < contents_size) { - error = GRPC_OS_ERROR(errno, "fread"); - GPR_ASSERT(ferror(file)); - goto end; - } - if (add_null_terminator) { - contents[contents_size++] = 0; - } - result = grpc_slice_new(contents, contents_size, gpr_free); - -end: - *output = result; - if (file != NULL) fclose(file); - if (error != GRPC_ERROR_NONE) { - grpc_error *error_out = - grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to load file", &error, 1), - GRPC_ERROR_STR_FILENAME, - grpc_slice_from_copied_string( - filename)); // TODO(ncteisen), always static? - GRPC_ERROR_UNREF(error); - error = error_out; - } - GRPC_SCHEDULING_END_BLOCKING_REGION; - return error; -} diff --git a/src/core/lib/iomgr/load_file.cc b/src/core/lib/iomgr/load_file.cc new file mode 100644 index 0000000000..0b4d41ea4b --- /dev/null +++ b/src/core/lib/iomgr/load_file.cc @@ -0,0 +1,78 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/load_file.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/string.h" + +grpc_error *grpc_load_file(const char *filename, int add_null_terminator, + grpc_slice *output) { + unsigned char *contents = NULL; + size_t contents_size = 0; + grpc_slice result = grpc_empty_slice(); + FILE *file; + size_t bytes_read = 0; + grpc_error *error = GRPC_ERROR_NONE; + + GRPC_SCHEDULING_START_BLOCKING_REGION; + file = fopen(filename, "rb"); + if (file == NULL) { + error = GRPC_OS_ERROR(errno, "fopen"); + goto end; + } + fseek(file, 0, SEEK_END); + /* Converting to size_t on the assumption that it will not fail */ + contents_size = (size_t)ftell(file); + fseek(file, 0, SEEK_SET); + contents = (unsigned char *)gpr_malloc(contents_size + + (add_null_terminator ? 1 : 0)); + bytes_read = fread(contents, 1, contents_size, file); + if (bytes_read < contents_size) { + error = GRPC_OS_ERROR(errno, "fread"); + GPR_ASSERT(ferror(file)); + goto end; + } + if (add_null_terminator) { + contents[contents_size++] = 0; + } + result = grpc_slice_new(contents, contents_size, gpr_free); + +end: + *output = result; + if (file != NULL) fclose(file); + if (error != GRPC_ERROR_NONE) { + grpc_error *error_out = + grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to load file", &error, 1), + GRPC_ERROR_STR_FILENAME, + grpc_slice_from_copied_string( + filename)); // TODO(ncteisen), always static? + GRPC_ERROR_UNREF(error); + error = error_out; + } + GRPC_SCHEDULING_END_BLOCKING_REGION; + return error; +} diff --git a/src/core/lib/iomgr/lockfree_event.c b/src/core/lib/iomgr/lockfree_event.c deleted file mode 100644 index f967b22ba9..0000000000 --- a/src/core/lib/iomgr/lockfree_event.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/lockfree_event.h" - -#include - -#include "src/core/lib/debug/trace.h" - -extern grpc_tracer_flag grpc_polling_trace; - -/* 'state' holds the to call when the fd is readable or writable respectively. - It can contain one of the following values: - CLOSURE_READY : The fd has an I/O event of interest but there is no - closure yet to execute - - CLOSURE_NOT_READY : The fd has no I/O event of interest - - closure ptr : The closure to be executed when the fd has an I/O - event of interest - - shutdown_error | FD_SHUTDOWN_BIT : - 'shutdown_error' field ORed with FD_SHUTDOWN_BIT. - This indicates that the fd is shutdown. Since all - memory allocations are word-aligned, the lower two - bits of the shutdown_error pointer are always 0. So - it is safe to OR these with FD_SHUTDOWN_BIT - - Valid state transitions: - - <-----3------ CLOSURE_NOT_READY ----1----> CLOSURE_READY - | | ^ | ^ | | - | | | | | | | - | +--------------4----------+ 6 +---------2---------------+ | - | | | - | v | - +-----5-------> [shutdown_error | FD_SHUTDOWN_BIT] <----7---------+ - - For 1, 4 : See grpc_lfev_set_ready() function - For 2, 3 : See grpc_lfev_notify_on() function - For 5,6,7: See grpc_lfev_set_shutdown() function */ - -#define CLOSURE_NOT_READY ((gpr_atm)0) -#define CLOSURE_READY ((gpr_atm)2) - -#define FD_SHUTDOWN_BIT ((gpr_atm)1) - -void grpc_lfev_init(gpr_atm *state) { - gpr_atm_no_barrier_store(state, CLOSURE_NOT_READY); -} - -void grpc_lfev_destroy(gpr_atm *state) { - gpr_atm curr = gpr_atm_no_barrier_load(state); - if (curr & FD_SHUTDOWN_BIT) { - GRPC_ERROR_UNREF((grpc_error *)(curr & ~FD_SHUTDOWN_BIT)); - } else { - GPR_ASSERT(curr == CLOSURE_NOT_READY || curr == CLOSURE_READY); - } -} - -bool grpc_lfev_is_shutdown(gpr_atm *state) { - gpr_atm curr = gpr_atm_no_barrier_load(state); - return (curr & FD_SHUTDOWN_BIT) != 0; -} - -void grpc_lfev_notify_on(grpc_exec_ctx *exec_ctx, gpr_atm *state, - grpc_closure *closure, const char *variable) { - while (true) { - gpr_atm curr = gpr_atm_no_barrier_load(state); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, "lfev_notify_on[%s]: %p curr=%p closure=%p", variable, - state, (void *)curr, closure); - } - switch (curr) { - case CLOSURE_NOT_READY: { - /* CLOSURE_NOT_READY -> . - - We're guaranteed by API that there's an acquire barrier before here, - so there's no need to double-dip and this can be a release-only. - - The release itself pairs with the acquire half of a set_ready full - barrier. */ - if (gpr_atm_rel_cas(state, CLOSURE_NOT_READY, (gpr_atm)closure)) { - return; /* Successful. Return */ - } - - break; /* retry */ - } - - case CLOSURE_READY: { - /* Change the state to CLOSURE_NOT_READY. Schedule the closure if - successful. If not, the state most likely transitioned to shutdown. - We should retry. - - This can be a no-barrier cas since the state is being transitioned to - CLOSURE_NOT_READY; set_ready and set_shutdown do not schedule any - closure when transitioning out of CLOSURE_NO_READY state (i.e there - is no other code that needs to 'happen-after' this) */ - if (gpr_atm_no_barrier_cas(state, CLOSURE_READY, CLOSURE_NOT_READY)) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); - return; /* Successful. Return */ - } - - break; /* retry */ - } - - default: { - /* 'curr' is either a closure or the fd is shutdown(in which case 'curr' - contains a pointer to the shutdown-error). If the fd is shutdown, - schedule the closure with the shutdown error */ - if ((curr & FD_SHUTDOWN_BIT) > 0) { - grpc_error *shutdown_err = (grpc_error *)(curr & ~FD_SHUTDOWN_BIT); - GRPC_CLOSURE_SCHED(exec_ctx, closure, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "FD Shutdown", &shutdown_err, 1)); - return; - } - - /* There is already a closure!. This indicates a bug in the code */ - gpr_log(GPR_ERROR, - "notify_on called with a previous callback still pending"); - abort(); - } - } - } - - GPR_UNREACHABLE_CODE(return ); -} - -bool grpc_lfev_set_shutdown(grpc_exec_ctx *exec_ctx, gpr_atm *state, - grpc_error *shutdown_err) { - gpr_atm new_state = (gpr_atm)shutdown_err | FD_SHUTDOWN_BIT; - - while (true) { - gpr_atm curr = gpr_atm_no_barrier_load(state); - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, "lfev_set_shutdown: %p curr=%p err=%s", state, - (void *)curr, grpc_error_string(shutdown_err)); - } - switch (curr) { - case CLOSURE_READY: - case CLOSURE_NOT_READY: - /* Need a full barrier here so that the initial load in notify_on - doesn't need a barrier */ - if (gpr_atm_full_cas(state, curr, new_state)) { - return true; /* early out */ - } - break; /* retry */ - - default: { - /* 'curr' is either a closure or the fd is already shutdown */ - - /* If fd is already shutdown, we are done */ - if ((curr & FD_SHUTDOWN_BIT) > 0) { - GRPC_ERROR_UNREF(shutdown_err); - return false; - } - - /* Fd is not shutdown. Schedule the closure and move the state to - shutdown state. - Needs an acquire to pair with setting the closure (and get a - happens-after on that edge), and a release to pair with anything - loading the shutdown state. */ - if (gpr_atm_full_cas(state, curr, new_state)) { - GRPC_CLOSURE_SCHED(exec_ctx, (grpc_closure *)curr, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "FD Shutdown", &shutdown_err, 1)); - return true; - } - - /* 'curr' was a closure but now changed to a different state. We will - have to retry */ - break; - } - } - } - - GPR_UNREACHABLE_CODE(return false); -} - -void grpc_lfev_set_ready(grpc_exec_ctx *exec_ctx, gpr_atm *state, - const char *variable) { - while (true) { - gpr_atm curr = gpr_atm_no_barrier_load(state); - - if (GRPC_TRACER_ON(grpc_polling_trace)) { - gpr_log(GPR_ERROR, "lfev_set_ready[%s]: %p curr=%p", variable, state, - (void *)curr); - } - - switch (curr) { - case CLOSURE_READY: { - /* Already ready. We are done here */ - return; - } - - case CLOSURE_NOT_READY: { - /* No barrier required as we're transitioning to a state that does not - involve a closure */ - if (gpr_atm_no_barrier_cas(state, CLOSURE_NOT_READY, CLOSURE_READY)) { - return; /* early out */ - } - break; /* retry */ - } - - default: { - /* 'curr' is either a closure or the fd is shutdown */ - if ((curr & FD_SHUTDOWN_BIT) > 0) { - /* The fd is shutdown. Do nothing */ - return; - } - /* Full cas: acquire pairs with this cas' release in the event of a - spurious set_ready; release pairs with this or the acquire in - notify_on (or set_shutdown) */ - else if (gpr_atm_full_cas(state, curr, CLOSURE_NOT_READY)) { - GRPC_CLOSURE_SCHED(exec_ctx, (grpc_closure *)curr, GRPC_ERROR_NONE); - return; - } - /* else the state changed again (only possible by either a racing - set_ready or set_shutdown functions. In both these cases, the closure - would have been scheduled for execution. So we are done here */ - return; - } - } - } -} diff --git a/src/core/lib/iomgr/lockfree_event.cc b/src/core/lib/iomgr/lockfree_event.cc new file mode 100644 index 0000000000..f967b22ba9 --- /dev/null +++ b/src/core/lib/iomgr/lockfree_event.cc @@ -0,0 +1,241 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/lockfree_event.h" + +#include + +#include "src/core/lib/debug/trace.h" + +extern grpc_tracer_flag grpc_polling_trace; + +/* 'state' holds the to call when the fd is readable or writable respectively. + It can contain one of the following values: + CLOSURE_READY : The fd has an I/O event of interest but there is no + closure yet to execute + + CLOSURE_NOT_READY : The fd has no I/O event of interest + + closure ptr : The closure to be executed when the fd has an I/O + event of interest + + shutdown_error | FD_SHUTDOWN_BIT : + 'shutdown_error' field ORed with FD_SHUTDOWN_BIT. + This indicates that the fd is shutdown. Since all + memory allocations are word-aligned, the lower two + bits of the shutdown_error pointer are always 0. So + it is safe to OR these with FD_SHUTDOWN_BIT + + Valid state transitions: + + <-----3------ CLOSURE_NOT_READY ----1----> CLOSURE_READY + | | ^ | ^ | | + | | | | | | | + | +--------------4----------+ 6 +---------2---------------+ | + | | | + | v | + +-----5-------> [shutdown_error | FD_SHUTDOWN_BIT] <----7---------+ + + For 1, 4 : See grpc_lfev_set_ready() function + For 2, 3 : See grpc_lfev_notify_on() function + For 5,6,7: See grpc_lfev_set_shutdown() function */ + +#define CLOSURE_NOT_READY ((gpr_atm)0) +#define CLOSURE_READY ((gpr_atm)2) + +#define FD_SHUTDOWN_BIT ((gpr_atm)1) + +void grpc_lfev_init(gpr_atm *state) { + gpr_atm_no_barrier_store(state, CLOSURE_NOT_READY); +} + +void grpc_lfev_destroy(gpr_atm *state) { + gpr_atm curr = gpr_atm_no_barrier_load(state); + if (curr & FD_SHUTDOWN_BIT) { + GRPC_ERROR_UNREF((grpc_error *)(curr & ~FD_SHUTDOWN_BIT)); + } else { + GPR_ASSERT(curr == CLOSURE_NOT_READY || curr == CLOSURE_READY); + } +} + +bool grpc_lfev_is_shutdown(gpr_atm *state) { + gpr_atm curr = gpr_atm_no_barrier_load(state); + return (curr & FD_SHUTDOWN_BIT) != 0; +} + +void grpc_lfev_notify_on(grpc_exec_ctx *exec_ctx, gpr_atm *state, + grpc_closure *closure, const char *variable) { + while (true) { + gpr_atm curr = gpr_atm_no_barrier_load(state); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, "lfev_notify_on[%s]: %p curr=%p closure=%p", variable, + state, (void *)curr, closure); + } + switch (curr) { + case CLOSURE_NOT_READY: { + /* CLOSURE_NOT_READY -> . + + We're guaranteed by API that there's an acquire barrier before here, + so there's no need to double-dip and this can be a release-only. + + The release itself pairs with the acquire half of a set_ready full + barrier. */ + if (gpr_atm_rel_cas(state, CLOSURE_NOT_READY, (gpr_atm)closure)) { + return; /* Successful. Return */ + } + + break; /* retry */ + } + + case CLOSURE_READY: { + /* Change the state to CLOSURE_NOT_READY. Schedule the closure if + successful. If not, the state most likely transitioned to shutdown. + We should retry. + + This can be a no-barrier cas since the state is being transitioned to + CLOSURE_NOT_READY; set_ready and set_shutdown do not schedule any + closure when transitioning out of CLOSURE_NO_READY state (i.e there + is no other code that needs to 'happen-after' this) */ + if (gpr_atm_no_barrier_cas(state, CLOSURE_READY, CLOSURE_NOT_READY)) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); + return; /* Successful. Return */ + } + + break; /* retry */ + } + + default: { + /* 'curr' is either a closure or the fd is shutdown(in which case 'curr' + contains a pointer to the shutdown-error). If the fd is shutdown, + schedule the closure with the shutdown error */ + if ((curr & FD_SHUTDOWN_BIT) > 0) { + grpc_error *shutdown_err = (grpc_error *)(curr & ~FD_SHUTDOWN_BIT); + GRPC_CLOSURE_SCHED(exec_ctx, closure, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "FD Shutdown", &shutdown_err, 1)); + return; + } + + /* There is already a closure!. This indicates a bug in the code */ + gpr_log(GPR_ERROR, + "notify_on called with a previous callback still pending"); + abort(); + } + } + } + + GPR_UNREACHABLE_CODE(return ); +} + +bool grpc_lfev_set_shutdown(grpc_exec_ctx *exec_ctx, gpr_atm *state, + grpc_error *shutdown_err) { + gpr_atm new_state = (gpr_atm)shutdown_err | FD_SHUTDOWN_BIT; + + while (true) { + gpr_atm curr = gpr_atm_no_barrier_load(state); + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, "lfev_set_shutdown: %p curr=%p err=%s", state, + (void *)curr, grpc_error_string(shutdown_err)); + } + switch (curr) { + case CLOSURE_READY: + case CLOSURE_NOT_READY: + /* Need a full barrier here so that the initial load in notify_on + doesn't need a barrier */ + if (gpr_atm_full_cas(state, curr, new_state)) { + return true; /* early out */ + } + break; /* retry */ + + default: { + /* 'curr' is either a closure or the fd is already shutdown */ + + /* If fd is already shutdown, we are done */ + if ((curr & FD_SHUTDOWN_BIT) > 0) { + GRPC_ERROR_UNREF(shutdown_err); + return false; + } + + /* Fd is not shutdown. Schedule the closure and move the state to + shutdown state. + Needs an acquire to pair with setting the closure (and get a + happens-after on that edge), and a release to pair with anything + loading the shutdown state. */ + if (gpr_atm_full_cas(state, curr, new_state)) { + GRPC_CLOSURE_SCHED(exec_ctx, (grpc_closure *)curr, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "FD Shutdown", &shutdown_err, 1)); + return true; + } + + /* 'curr' was a closure but now changed to a different state. We will + have to retry */ + break; + } + } + } + + GPR_UNREACHABLE_CODE(return false); +} + +void grpc_lfev_set_ready(grpc_exec_ctx *exec_ctx, gpr_atm *state, + const char *variable) { + while (true) { + gpr_atm curr = gpr_atm_no_barrier_load(state); + + if (GRPC_TRACER_ON(grpc_polling_trace)) { + gpr_log(GPR_ERROR, "lfev_set_ready[%s]: %p curr=%p", variable, state, + (void *)curr); + } + + switch (curr) { + case CLOSURE_READY: { + /* Already ready. We are done here */ + return; + } + + case CLOSURE_NOT_READY: { + /* No barrier required as we're transitioning to a state that does not + involve a closure */ + if (gpr_atm_no_barrier_cas(state, CLOSURE_NOT_READY, CLOSURE_READY)) { + return; /* early out */ + } + break; /* retry */ + } + + default: { + /* 'curr' is either a closure or the fd is shutdown */ + if ((curr & FD_SHUTDOWN_BIT) > 0) { + /* The fd is shutdown. Do nothing */ + return; + } + /* Full cas: acquire pairs with this cas' release in the event of a + spurious set_ready; release pairs with this or the acquire in + notify_on (or set_shutdown) */ + else if (gpr_atm_full_cas(state, curr, CLOSURE_NOT_READY)) { + GRPC_CLOSURE_SCHED(exec_ctx, (grpc_closure *)curr, GRPC_ERROR_NONE); + return; + } + /* else the state changed again (only possible by either a racing + set_ready or set_shutdown functions. In both these cases, the closure + would have been scheduled for execution. So we are done here */ + return; + } + } + } +} diff --git a/src/core/lib/iomgr/network_status_tracker.c b/src/core/lib/iomgr/network_status_tracker.c deleted file mode 100644 index 57a7faa9f1..0000000000 --- a/src/core/lib/iomgr/network_status_tracker.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/network_status_tracker.h" -#include "src/core/lib/iomgr/endpoint.h" - -void grpc_network_status_shutdown(void) {} - -void grpc_network_status_init(void) { - // TODO(makarandd): Install callback with OS to monitor network status. -} - -void grpc_destroy_network_status_monitor() {} - -void grpc_network_status_register_endpoint(grpc_endpoint *ep) { (void)ep; } - -void grpc_network_status_unregister_endpoint(grpc_endpoint *ep) { (void)ep; } - -void grpc_network_status_shutdown_all_endpoints() {} diff --git a/src/core/lib/iomgr/network_status_tracker.cc b/src/core/lib/iomgr/network_status_tracker.cc new file mode 100644 index 0000000000..57a7faa9f1 --- /dev/null +++ b/src/core/lib/iomgr/network_status_tracker.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/endpoint.h" + +void grpc_network_status_shutdown(void) {} + +void grpc_network_status_init(void) { + // TODO(makarandd): Install callback with OS to monitor network status. +} + +void grpc_destroy_network_status_monitor() {} + +void grpc_network_status_register_endpoint(grpc_endpoint *ep) { (void)ep; } + +void grpc_network_status_unregister_endpoint(grpc_endpoint *ep) { (void)ep; } + +void grpc_network_status_shutdown_all_endpoints() {} diff --git a/src/core/lib/iomgr/polling_entity.c b/src/core/lib/iomgr/polling_entity.c deleted file mode 100644 index 8591a5518e..0000000000 --- a/src/core/lib/iomgr/polling_entity.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include "src/core/lib/iomgr/polling_entity.h" - -grpc_polling_entity grpc_polling_entity_create_from_pollset_set( - grpc_pollset_set *pollset_set) { - grpc_polling_entity pollent; - pollent.pollent.pollset_set = pollset_set; - pollent.tag = GRPC_POLLS_POLLSET_SET; - return pollent; -} - -grpc_polling_entity grpc_polling_entity_create_from_pollset( - grpc_pollset *pollset) { - grpc_polling_entity pollent; - pollent.pollent.pollset = pollset; - pollent.tag = GRPC_POLLS_POLLSET; - return pollent; -} - -grpc_pollset *grpc_polling_entity_pollset(grpc_polling_entity *pollent) { - if (pollent->tag == GRPC_POLLS_POLLSET) { - return pollent->pollent.pollset; - } - return NULL; -} - -grpc_pollset_set *grpc_polling_entity_pollset_set( - grpc_polling_entity *pollent) { - if (pollent->tag == GRPC_POLLS_POLLSET_SET) { - return pollent->pollent.pollset_set; - } - return NULL; -} - -bool grpc_polling_entity_is_empty(const grpc_polling_entity *pollent) { - return pollent->tag == GRPC_POLLS_NONE; -} - -void grpc_polling_entity_add_to_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_polling_entity *pollent, - grpc_pollset_set *pss_dst) { - if (pollent->tag == GRPC_POLLS_POLLSET) { - GPR_ASSERT(pollent->pollent.pollset != NULL); - grpc_pollset_set_add_pollset(exec_ctx, pss_dst, pollent->pollent.pollset); - } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) { - GPR_ASSERT(pollent->pollent.pollset_set != NULL); - grpc_pollset_set_add_pollset_set(exec_ctx, pss_dst, - pollent->pollent.pollset_set); - } else { - gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); - abort(); - } -} - -void grpc_polling_entity_del_from_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_polling_entity *pollent, - grpc_pollset_set *pss_dst) { - if (pollent->tag == GRPC_POLLS_POLLSET) { - GPR_ASSERT(pollent->pollent.pollset != NULL); - grpc_pollset_set_del_pollset(exec_ctx, pss_dst, pollent->pollent.pollset); - } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) { - GPR_ASSERT(pollent->pollent.pollset_set != NULL); - grpc_pollset_set_del_pollset_set(exec_ctx, pss_dst, - pollent->pollent.pollset_set); - } else { - gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); - abort(); - } -} diff --git a/src/core/lib/iomgr/polling_entity.cc b/src/core/lib/iomgr/polling_entity.cc new file mode 100644 index 0000000000..8591a5518e --- /dev/null +++ b/src/core/lib/iomgr/polling_entity.cc @@ -0,0 +1,89 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include "src/core/lib/iomgr/polling_entity.h" + +grpc_polling_entity grpc_polling_entity_create_from_pollset_set( + grpc_pollset_set *pollset_set) { + grpc_polling_entity pollent; + pollent.pollent.pollset_set = pollset_set; + pollent.tag = GRPC_POLLS_POLLSET_SET; + return pollent; +} + +grpc_polling_entity grpc_polling_entity_create_from_pollset( + grpc_pollset *pollset) { + grpc_polling_entity pollent; + pollent.pollent.pollset = pollset; + pollent.tag = GRPC_POLLS_POLLSET; + return pollent; +} + +grpc_pollset *grpc_polling_entity_pollset(grpc_polling_entity *pollent) { + if (pollent->tag == GRPC_POLLS_POLLSET) { + return pollent->pollent.pollset; + } + return NULL; +} + +grpc_pollset_set *grpc_polling_entity_pollset_set( + grpc_polling_entity *pollent) { + if (pollent->tag == GRPC_POLLS_POLLSET_SET) { + return pollent->pollent.pollset_set; + } + return NULL; +} + +bool grpc_polling_entity_is_empty(const grpc_polling_entity *pollent) { + return pollent->tag == GRPC_POLLS_NONE; +} + +void grpc_polling_entity_add_to_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_polling_entity *pollent, + grpc_pollset_set *pss_dst) { + if (pollent->tag == GRPC_POLLS_POLLSET) { + GPR_ASSERT(pollent->pollent.pollset != NULL); + grpc_pollset_set_add_pollset(exec_ctx, pss_dst, pollent->pollent.pollset); + } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) { + GPR_ASSERT(pollent->pollent.pollset_set != NULL); + grpc_pollset_set_add_pollset_set(exec_ctx, pss_dst, + pollent->pollent.pollset_set); + } else { + gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); + abort(); + } +} + +void grpc_polling_entity_del_from_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_polling_entity *pollent, + grpc_pollset_set *pss_dst) { + if (pollent->tag == GRPC_POLLS_POLLSET) { + GPR_ASSERT(pollent->pollent.pollset != NULL); + grpc_pollset_set_del_pollset(exec_ctx, pss_dst, pollent->pollent.pollset); + } else if (pollent->tag == GRPC_POLLS_POLLSET_SET) { + GPR_ASSERT(pollent->pollent.pollset_set != NULL); + grpc_pollset_set_del_pollset_set(exec_ctx, pss_dst, + pollent->pollent.pollset_set); + } else { + gpr_log(GPR_ERROR, "Invalid grpc_polling_entity tag '%d'", pollent->tag); + abort(); + } +} diff --git a/src/core/lib/iomgr/pollset_set_uv.c b/src/core/lib/iomgr/pollset_set_uv.c deleted file mode 100644 index 90186edbb7..0000000000 --- a/src/core/lib/iomgr/pollset_set_uv.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include "src/core/lib/iomgr/pollset_set.h" - -grpc_pollset_set* grpc_pollset_set_create(void) { - return (grpc_pollset_set*)((intptr_t)0xdeafbeef); -} - -void grpc_pollset_set_destroy(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set) {} - -void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_set_uv.cc b/src/core/lib/iomgr/pollset_set_uv.cc new file mode 100644 index 0000000000..90186edbb7 --- /dev/null +++ b/src/core/lib/iomgr/pollset_set_uv.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include "src/core/lib/iomgr/pollset_set.h" + +grpc_pollset_set* grpc_pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +void grpc_pollset_set_destroy(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set) {} + +void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void grpc_pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_set_windows.c b/src/core/lib/iomgr/pollset_set_windows.c deleted file mode 100644 index 2105a47ad4..0000000000 --- a/src/core/lib/iomgr/pollset_set_windows.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include "src/core/lib/iomgr/pollset_set_windows.h" - -grpc_pollset_set* grpc_pollset_set_create(void) { - return (grpc_pollset_set*)((intptr_t)0xdeafbeef); -} - -void grpc_pollset_set_destroy(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set) {} - -void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* pollset_set, - grpc_pollset* pollset) {} - -void grpc_pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, - grpc_pollset_set* bag, - grpc_pollset_set* item) {} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_set_windows.cc b/src/core/lib/iomgr/pollset_set_windows.cc new file mode 100644 index 0000000000..2105a47ad4 --- /dev/null +++ b/src/core/lib/iomgr/pollset_set_windows.cc @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/pollset_set_windows.h" + +grpc_pollset_set* grpc_pollset_set_create(void) { + return (grpc_pollset_set*)((intptr_t)0xdeafbeef); +} + +void grpc_pollset_set_destroy(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set) {} + +void grpc_pollset_set_add_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void grpc_pollset_set_del_pollset(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* pollset_set, + grpc_pollset* pollset) {} + +void grpc_pollset_set_add_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +void grpc_pollset_set_del_pollset_set(grpc_exec_ctx* exec_ctx, + grpc_pollset_set* bag, + grpc_pollset_set* item) {} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_uv.c b/src/core/lib/iomgr/pollset_uv.c deleted file mode 100644 index 2651325e25..0000000000 --- a/src/core/lib/iomgr/pollset_uv.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include - -#include - -#include -#include -#include - -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/pollset.h" -#include "src/core/lib/iomgr/pollset_uv.h" - -#include "src/core/lib/debug/trace.h" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_fd_refcount = - GRPC_TRACER_INITIALIZER(false, "fd_refcount"); -#endif - -struct grpc_pollset { - uv_timer_t timer; - int shutting_down; -}; - -/* Indicates that grpc_pollset_work should run an iteration of the UV loop - before running callbacks. This defaults to 1, and should be disabled if - grpc_pollset_work will be called within the callstack of uv_run */ -int grpc_pollset_work_run_loop; - -gpr_mu grpc_polling_mu; - -/* This is used solely to kick the uv loop, by setting a callback to be run - immediately in the next loop iteration. - Note: In the future, if there is a bug that involves missing wakeups in the - future, try adding a uv_async_t to kick the loop differently */ -uv_timer_t *dummy_uv_handle; - -size_t grpc_pollset_size() { return sizeof(grpc_pollset); } - -void dummy_timer_cb(uv_timer_t *handle) {} - -void dummy_handle_close_cb(uv_handle_t *handle) { gpr_free(handle); } - -void grpc_pollset_global_init(void) { - gpr_mu_init(&grpc_polling_mu); - dummy_uv_handle = gpr_malloc(sizeof(uv_timer_t)); - uv_timer_init(uv_default_loop(), dummy_uv_handle); - grpc_pollset_work_run_loop = 1; -} - -void grpc_pollset_global_shutdown(void) { - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_mu_destroy(&grpc_polling_mu); - uv_close((uv_handle_t *)dummy_uv_handle, dummy_handle_close_cb); -} - -static void timer_run_cb(uv_timer_t *timer) {} - -static void timer_close_cb(uv_handle_t *handle) { handle->data = (void *)1; } - -void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - GRPC_UV_ASSERT_SAME_THREAD(); - *mu = &grpc_polling_mu; - uv_timer_init(uv_default_loop(), &pollset->timer); - pollset->shutting_down = 0; -} - -void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - GPR_ASSERT(!pollset->shutting_down); - GRPC_UV_ASSERT_SAME_THREAD(); - pollset->shutting_down = 1; - if (grpc_pollset_work_run_loop) { - // Drain any pending UV callbacks without blocking - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } else { - // kick the loop once - uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); - } - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); -} - -void grpc_pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { - GRPC_UV_ASSERT_SAME_THREAD(); - uv_close((uv_handle_t *)&pollset->timer, timer_close_cb); - // timer.data is a boolean indicating that the timer has finished closing - pollset->timer.data = (void *)0; - if (grpc_pollset_work_run_loop) { - while (!pollset->timer.data) { - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } - } -} - -grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - uint64_t timeout; - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_mu_unlock(&grpc_polling_mu); - if (grpc_pollset_work_run_loop) { - if (gpr_time_cmp(deadline, now) >= 0) { - timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); - } else { - timeout = 0; - } - /* We special-case timeout=0 so that we don't bother with the timer when - the loop won't block anyway */ - if (timeout > 0) { - uv_timer_start(&pollset->timer, timer_run_cb, timeout, 0); - /* Run until there is some I/O activity or the timer triggers. It doesn't - matter which happens */ - uv_run(uv_default_loop(), UV_RUN_ONCE); - uv_timer_stop(&pollset->timer); - } else { - uv_run(uv_default_loop(), UV_RUN_NOWAIT); - } - } - if (!grpc_closure_list_empty(exec_ctx->closure_list)) { - grpc_exec_ctx_flush(exec_ctx); - } - gpr_mu_lock(&grpc_polling_mu); - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *specific_worker) { - GRPC_UV_ASSERT_SAME_THREAD(); - uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); - return GRPC_ERROR_NONE; -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_uv.cc b/src/core/lib/iomgr/pollset_uv.cc new file mode 100644 index 0000000000..2651325e25 --- /dev/null +++ b/src/core/lib/iomgr/pollset_uv.cc @@ -0,0 +1,155 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include + +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_uv.h" + +#include "src/core/lib/debug/trace.h" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_fd_refcount = + GRPC_TRACER_INITIALIZER(false, "fd_refcount"); +#endif + +struct grpc_pollset { + uv_timer_t timer; + int shutting_down; +}; + +/* Indicates that grpc_pollset_work should run an iteration of the UV loop + before running callbacks. This defaults to 1, and should be disabled if + grpc_pollset_work will be called within the callstack of uv_run */ +int grpc_pollset_work_run_loop; + +gpr_mu grpc_polling_mu; + +/* This is used solely to kick the uv loop, by setting a callback to be run + immediately in the next loop iteration. + Note: In the future, if there is a bug that involves missing wakeups in the + future, try adding a uv_async_t to kick the loop differently */ +uv_timer_t *dummy_uv_handle; + +size_t grpc_pollset_size() { return sizeof(grpc_pollset); } + +void dummy_timer_cb(uv_timer_t *handle) {} + +void dummy_handle_close_cb(uv_handle_t *handle) { gpr_free(handle); } + +void grpc_pollset_global_init(void) { + gpr_mu_init(&grpc_polling_mu); + dummy_uv_handle = gpr_malloc(sizeof(uv_timer_t)); + uv_timer_init(uv_default_loop(), dummy_uv_handle); + grpc_pollset_work_run_loop = 1; +} + +void grpc_pollset_global_shutdown(void) { + GRPC_UV_ASSERT_SAME_THREAD(); + gpr_mu_destroy(&grpc_polling_mu); + uv_close((uv_handle_t *)dummy_uv_handle, dummy_handle_close_cb); +} + +static void timer_run_cb(uv_timer_t *timer) {} + +static void timer_close_cb(uv_handle_t *handle) { handle->data = (void *)1; } + +void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + GRPC_UV_ASSERT_SAME_THREAD(); + *mu = &grpc_polling_mu; + uv_timer_init(uv_default_loop(), &pollset->timer); + pollset->shutting_down = 0; +} + +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + GPR_ASSERT(!pollset->shutting_down); + GRPC_UV_ASSERT_SAME_THREAD(); + pollset->shutting_down = 1; + if (grpc_pollset_work_run_loop) { + // Drain any pending UV callbacks without blocking + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } else { + // kick the loop once + uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); + } + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); +} + +void grpc_pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) { + GRPC_UV_ASSERT_SAME_THREAD(); + uv_close((uv_handle_t *)&pollset->timer, timer_close_cb); + // timer.data is a boolean indicating that the timer has finished closing + pollset->timer.data = (void *)0; + if (grpc_pollset_work_run_loop) { + while (!pollset->timer.data) { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } + } +} + +grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + uint64_t timeout; + GRPC_UV_ASSERT_SAME_THREAD(); + gpr_mu_unlock(&grpc_polling_mu); + if (grpc_pollset_work_run_loop) { + if (gpr_time_cmp(deadline, now) >= 0) { + timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); + } else { + timeout = 0; + } + /* We special-case timeout=0 so that we don't bother with the timer when + the loop won't block anyway */ + if (timeout > 0) { + uv_timer_start(&pollset->timer, timer_run_cb, timeout, 0); + /* Run until there is some I/O activity or the timer triggers. It doesn't + matter which happens */ + uv_run(uv_default_loop(), UV_RUN_ONCE); + uv_timer_stop(&pollset->timer); + } else { + uv_run(uv_default_loop(), UV_RUN_NOWAIT); + } + } + if (!grpc_closure_list_empty(exec_ctx->closure_list)) { + grpc_exec_ctx_flush(exec_ctx); + } + gpr_mu_lock(&grpc_polling_mu); + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *specific_worker) { + GRPC_UV_ASSERT_SAME_THREAD(); + uv_timer_start(dummy_uv_handle, dummy_timer_cb, 0, 0); + return GRPC_ERROR_NONE; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/pollset_windows.c b/src/core/lib/iomgr/pollset_windows.c deleted file mode 100644 index eb295d3eeb..0000000000 --- a/src/core/lib/iomgr/pollset_windows.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include -#include - -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/pollset.h" -#include "src/core/lib/iomgr/pollset_windows.h" - -#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_fd_refcount = - GRPC_TRACER_INITIALIZER(false, "fd_refcount"); -#endif - -gpr_mu grpc_polling_mu; -static grpc_pollset_worker *g_active_poller; -static grpc_pollset_worker g_global_root_worker; - -void grpc_pollset_global_init(void) { - gpr_mu_init(&grpc_polling_mu); - g_active_poller = NULL; - g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = - g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = - &g_global_root_worker; -} - -void grpc_pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } - -static void remove_worker(grpc_pollset_worker *worker, - grpc_pollset_worker_link_type type) { - worker->links[type].prev->links[type].next = worker->links[type].next; - worker->links[type].next->links[type].prev = worker->links[type].prev; - worker->links[type].next = worker->links[type].prev = worker; -} - -static int has_workers(grpc_pollset_worker *root, - grpc_pollset_worker_link_type type) { - return root->links[type].next != root; -} - -static grpc_pollset_worker *pop_front_worker( - grpc_pollset_worker *root, grpc_pollset_worker_link_type type) { - if (has_workers(root, type)) { - grpc_pollset_worker *w = root->links[type].next; - remove_worker(w, type); - return w; - } else { - return NULL; - } -} - -static void push_front_worker(grpc_pollset_worker *root, - grpc_pollset_worker_link_type type, - grpc_pollset_worker *worker) { - worker->links[type].prev = root; - worker->links[type].next = worker->links[type].prev->links[type].next; - worker->links[type].prev->links[type].next = - worker->links[type].next->links[type].prev = worker; -} - -size_t grpc_pollset_size(void) { return sizeof(grpc_pollset); } - -/* There isn't really any such thing as a pollset under Windows, due to the - nature of the IO completion ports. We're still going to provide a minimal - set of features for the sake of the rest of grpc. But grpc_pollset_work - won't actually do any polling, and return as quickly as possible. */ - -void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { - *mu = &grpc_polling_mu; - pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = - pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = - &pollset->root_worker; -} - -void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure) { - pollset->shutting_down = 1; - grpc_pollset_kick(exec_ctx, pollset, GRPC_POLLSET_KICK_BROADCAST); - if (!pollset->is_iocp_worker) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); - } else { - pollset->on_shutdown = closure; - } -} - -void grpc_pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {} - -grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker_hdl, - gpr_timespec now, gpr_timespec deadline) { - grpc_pollset_worker worker; - if (worker_hdl) *worker_hdl = &worker; - - int added_worker = 0; - worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = - worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = - worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = - worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL; - worker.kicked = 0; - worker.pollset = pollset; - gpr_cv_init(&worker.cv); - if (!pollset->kicked_without_pollers && !pollset->shutting_down) { - if (g_active_poller == NULL) { - grpc_pollset_worker *next_worker; - /* become poller */ - pollset->is_iocp_worker = 1; - g_active_poller = &worker; - gpr_mu_unlock(&grpc_polling_mu); - grpc_iocp_work(exec_ctx, deadline); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&grpc_polling_mu); - pollset->is_iocp_worker = 0; - g_active_poller = NULL; - /* try to get a worker from this pollsets worker list */ - next_worker = pop_front_worker(&pollset->root_worker, - GRPC_POLLSET_WORKER_LINK_POLLSET); - if (next_worker == NULL) { - /* try to get a worker from the global list */ - next_worker = pop_front_worker(&g_global_root_worker, - GRPC_POLLSET_WORKER_LINK_GLOBAL); - } - if (next_worker != NULL) { - next_worker->kicked = 1; - gpr_cv_signal(&next_worker->cv); - } - - if (pollset->shutting_down && pollset->on_shutdown != NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, pollset->on_shutdown, GRPC_ERROR_NONE); - pollset->on_shutdown = NULL; - } - goto done; - } - push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL, - &worker); - push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET, - &worker); - added_worker = 1; - while (!worker.kicked) { - if (gpr_cv_wait(&worker.cv, &grpc_polling_mu, deadline)) { - break; - } - } - } else { - pollset->kicked_without_pollers = 0; - } -done: - if (!grpc_closure_list_empty(exec_ctx->closure_list)) { - gpr_mu_unlock(&grpc_polling_mu); - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&grpc_polling_mu); - } - if (added_worker) { - remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_GLOBAL); - remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_POLLSET); - } - gpr_cv_destroy(&worker.cv); - if (worker_hdl) *worker_hdl = NULL; - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *p, - grpc_pollset_worker *specific_worker) { - if (specific_worker != NULL) { - if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { - for (specific_worker = - p->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next; - specific_worker != &p->root_worker; - specific_worker = - specific_worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next) { - specific_worker->kicked = 1; - gpr_cv_signal(&specific_worker->cv); - } - p->kicked_without_pollers = 1; - if (p->is_iocp_worker) { - grpc_iocp_kick(); - } - } else { - if (p->is_iocp_worker && g_active_poller == specific_worker) { - grpc_iocp_kick(); - } else { - specific_worker->kicked = 1; - gpr_cv_signal(&specific_worker->cv); - } - } - } else { - specific_worker = - pop_front_worker(&p->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET); - if (specific_worker != NULL) { - grpc_pollset_kick(exec_ctx, p, specific_worker); - } else if (p->is_iocp_worker) { - grpc_iocp_kick(); - } else { - p->kicked_without_pollers = 1; - } - } - return GRPC_ERROR_NONE; -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/pollset_windows.cc b/src/core/lib/iomgr/pollset_windows.cc new file mode 100644 index 0000000000..eb295d3eeb --- /dev/null +++ b/src/core/lib/iomgr/pollset_windows.cc @@ -0,0 +1,222 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include +#include + +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_windows.h" + +#define GRPC_POLLSET_KICK_BROADCAST ((grpc_pollset_worker *)1) + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_fd_refcount = + GRPC_TRACER_INITIALIZER(false, "fd_refcount"); +#endif + +gpr_mu grpc_polling_mu; +static grpc_pollset_worker *g_active_poller; +static grpc_pollset_worker g_global_root_worker; + +void grpc_pollset_global_init(void) { + gpr_mu_init(&grpc_polling_mu); + g_active_poller = NULL; + g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = + g_global_root_worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = + &g_global_root_worker; +} + +void grpc_pollset_global_shutdown(void) { gpr_mu_destroy(&grpc_polling_mu); } + +static void remove_worker(grpc_pollset_worker *worker, + grpc_pollset_worker_link_type type) { + worker->links[type].prev->links[type].next = worker->links[type].next; + worker->links[type].next->links[type].prev = worker->links[type].prev; + worker->links[type].next = worker->links[type].prev = worker; +} + +static int has_workers(grpc_pollset_worker *root, + grpc_pollset_worker_link_type type) { + return root->links[type].next != root; +} + +static grpc_pollset_worker *pop_front_worker( + grpc_pollset_worker *root, grpc_pollset_worker_link_type type) { + if (has_workers(root, type)) { + grpc_pollset_worker *w = root->links[type].next; + remove_worker(w, type); + return w; + } else { + return NULL; + } +} + +static void push_front_worker(grpc_pollset_worker *root, + grpc_pollset_worker_link_type type, + grpc_pollset_worker *worker) { + worker->links[type].prev = root; + worker->links[type].next = worker->links[type].prev->links[type].next; + worker->links[type].prev->links[type].next = + worker->links[type].next->links[type].prev = worker; +} + +size_t grpc_pollset_size(void) { return sizeof(grpc_pollset); } + +/* There isn't really any such thing as a pollset under Windows, due to the + nature of the IO completion ports. We're still going to provide a minimal + set of features for the sake of the rest of grpc. But grpc_pollset_work + won't actually do any polling, and return as quickly as possible. */ + +void grpc_pollset_init(grpc_pollset *pollset, gpr_mu **mu) { + *mu = &grpc_polling_mu; + pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = + pollset->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = + &pollset->root_worker; +} + +void grpc_pollset_shutdown(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure) { + pollset->shutting_down = 1; + grpc_pollset_kick(exec_ctx, pollset, GRPC_POLLSET_KICK_BROADCAST); + if (!pollset->is_iocp_worker) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); + } else { + pollset->on_shutdown = closure; + } +} + +void grpc_pollset_destroy(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset) {} + +grpc_error *grpc_pollset_work(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker_hdl, + gpr_timespec now, gpr_timespec deadline) { + grpc_pollset_worker worker; + if (worker_hdl) *worker_hdl = &worker; + + int added_worker = 0; + worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next = + worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].prev = + worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].next = + worker.links[GRPC_POLLSET_WORKER_LINK_GLOBAL].prev = NULL; + worker.kicked = 0; + worker.pollset = pollset; + gpr_cv_init(&worker.cv); + if (!pollset->kicked_without_pollers && !pollset->shutting_down) { + if (g_active_poller == NULL) { + grpc_pollset_worker *next_worker; + /* become poller */ + pollset->is_iocp_worker = 1; + g_active_poller = &worker; + gpr_mu_unlock(&grpc_polling_mu); + grpc_iocp_work(exec_ctx, deadline); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&grpc_polling_mu); + pollset->is_iocp_worker = 0; + g_active_poller = NULL; + /* try to get a worker from this pollsets worker list */ + next_worker = pop_front_worker(&pollset->root_worker, + GRPC_POLLSET_WORKER_LINK_POLLSET); + if (next_worker == NULL) { + /* try to get a worker from the global list */ + next_worker = pop_front_worker(&g_global_root_worker, + GRPC_POLLSET_WORKER_LINK_GLOBAL); + } + if (next_worker != NULL) { + next_worker->kicked = 1; + gpr_cv_signal(&next_worker->cv); + } + + if (pollset->shutting_down && pollset->on_shutdown != NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, pollset->on_shutdown, GRPC_ERROR_NONE); + pollset->on_shutdown = NULL; + } + goto done; + } + push_front_worker(&g_global_root_worker, GRPC_POLLSET_WORKER_LINK_GLOBAL, + &worker); + push_front_worker(&pollset->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET, + &worker); + added_worker = 1; + while (!worker.kicked) { + if (gpr_cv_wait(&worker.cv, &grpc_polling_mu, deadline)) { + break; + } + } + } else { + pollset->kicked_without_pollers = 0; + } +done: + if (!grpc_closure_list_empty(exec_ctx->closure_list)) { + gpr_mu_unlock(&grpc_polling_mu); + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&grpc_polling_mu); + } + if (added_worker) { + remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_GLOBAL); + remove_worker(&worker, GRPC_POLLSET_WORKER_LINK_POLLSET); + } + gpr_cv_destroy(&worker.cv); + if (worker_hdl) *worker_hdl = NULL; + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_pollset_kick(grpc_exec_ctx *exec_ctx, grpc_pollset *p, + grpc_pollset_worker *specific_worker) { + if (specific_worker != NULL) { + if (specific_worker == GRPC_POLLSET_KICK_BROADCAST) { + for (specific_worker = + p->root_worker.links[GRPC_POLLSET_WORKER_LINK_POLLSET].next; + specific_worker != &p->root_worker; + specific_worker = + specific_worker->links[GRPC_POLLSET_WORKER_LINK_POLLSET].next) { + specific_worker->kicked = 1; + gpr_cv_signal(&specific_worker->cv); + } + p->kicked_without_pollers = 1; + if (p->is_iocp_worker) { + grpc_iocp_kick(); + } + } else { + if (p->is_iocp_worker && g_active_poller == specific_worker) { + grpc_iocp_kick(); + } else { + specific_worker->kicked = 1; + gpr_cv_signal(&specific_worker->cv); + } + } + } else { + specific_worker = + pop_front_worker(&p->root_worker, GRPC_POLLSET_WORKER_LINK_POLLSET); + if (specific_worker != NULL) { + grpc_pollset_kick(exec_ctx, p, specific_worker); + } else if (p->is_iocp_worker) { + grpc_iocp_kick(); + } else { + p->kicked_without_pollers = 1; + } + } + return GRPC_ERROR_NONE; +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/resolve_address_posix.c b/src/core/lib/iomgr/resolve_address_posix.c deleted file mode 100644 index 60cfeebd47..0000000000 --- a/src/core/lib/iomgr/resolve_address_posix.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/sockaddr.h" - -#include "src/core/lib/iomgr/resolve_address.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/string.h" - -static grpc_error *blocking_resolve_address_impl( - const char *name, const char *default_port, - grpc_resolved_addresses **addresses) { - struct addrinfo hints; - struct addrinfo *result = NULL, *resp; - char *host; - char *port; - int s; - size_t i; - grpc_error *err; - - if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' && - name[4] == ':' && name[5] != 0) { - return grpc_resolve_unix_domain_address(name + 5, addresses); - } - - /* parse name, splitting it into host and port parts */ - gpr_split_host_port(name, &host, &port); - if (host == NULL) { - err = grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); - goto done; - } - if (port == NULL) { - if (default_port == NULL) { - err = grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); - goto done; - } - port = gpr_strdup(default_port); - } - - /* Call getaddrinfo */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints.ai_socktype = SOCK_STREAM; /* stream socket */ - hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ - - GRPC_SCHEDULING_START_BLOCKING_REGION; - s = getaddrinfo(host, port, &hints, &result); - GRPC_SCHEDULING_END_BLOCKING_REGION; - - if (s != 0) { - /* Retry if well-known service name is recognized */ - const char *svc[][2] = {{"http", "80"}, {"https", "443"}}; - for (i = 0; i < GPR_ARRAY_SIZE(svc); i++) { - if (strcmp(port, svc[i][0]) == 0) { - GRPC_SCHEDULING_START_BLOCKING_REGION; - s = getaddrinfo(host, svc[i][1], &hints, &result); - GRPC_SCHEDULING_END_BLOCKING_REGION; - break; - } - } - } - - if (s != 0) { - err = grpc_error_set_str( - grpc_error_set_str( - grpc_error_set_str( - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("OS Error"), - GRPC_ERROR_INT_ERRNO, s), - GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(gai_strerror(s))), - GRPC_ERROR_STR_SYSCALL, - grpc_slice_from_static_string("getaddrinfo")), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); - goto done; - } - - /* Success path: set addrs non-NULL, fill it in */ - *addresses = - (grpc_resolved_addresses *)gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addresses)->naddrs = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - (*addresses)->naddrs++; - } - (*addresses)->addrs = (grpc_resolved_address *)gpr_malloc( - sizeof(grpc_resolved_address) * (*addresses)->naddrs); - i = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); - (*addresses)->addrs[i].len = resp->ai_addrlen; - i++; - } - err = GRPC_ERROR_NONE; - -done: - gpr_free(host); - gpr_free(port); - if (result) { - freeaddrinfo(result); - } - return err; -} - -grpc_error *(*grpc_blocking_resolve_address)( - const char *name, const char *default_port, - grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; - -typedef struct { - char *name; - char *default_port; - grpc_closure *on_done; - grpc_resolved_addresses **addrs_out; - grpc_closure request_closure; - void *arg; -} request; - -/* Callback to be passed to grpc_executor to asynch-ify - * grpc_blocking_resolve_address */ -static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, - grpc_error *error) { - request *r = (request *)rp; - GRPC_CLOSURE_SCHED( - exec_ctx, r->on_done, - grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out)); - gpr_free(r->name); - gpr_free(r->default_port); - gpr_free(r); -} - -void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, - const char *default_port, - grpc_pollset_set *interested_parties, - grpc_closure *on_done, - grpc_resolved_addresses **addrs) { - request *r = (request *)gpr_malloc(sizeof(request)); - GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, - grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); - r->name = gpr_strdup(name); - r->default_port = gpr_strdup(default_port); - r->on_done = on_done; - r->addrs_out = addrs; - GRPC_CLOSURE_SCHED(exec_ctx, &r->request_closure, GRPC_ERROR_NONE); -} - -void (*grpc_resolve_address)( - grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, - grpc_pollset_set *interested_parties, grpc_closure *on_done, - grpc_resolved_addresses **addrs) = resolve_address_impl; - -#endif diff --git a/src/core/lib/iomgr/resolve_address_posix.cc b/src/core/lib/iomgr/resolve_address_posix.cc new file mode 100644 index 0000000000..60cfeebd47 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_posix.cc @@ -0,0 +1,193 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include "src/core/lib/iomgr/resolve_address.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/string.h" + +static grpc_error *blocking_resolve_address_impl( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) { + struct addrinfo hints; + struct addrinfo *result = NULL, *resp; + char *host; + char *port; + int s; + size_t i; + grpc_error *err; + + if (name[0] == 'u' && name[1] == 'n' && name[2] == 'i' && name[3] == 'x' && + name[4] == ':' && name[5] != 0) { + return grpc_resolve_unix_domain_address(name + 5, addresses); + } + + /* parse name, splitting it into host and port parts */ + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + err = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("unparseable host:port"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto done; + } + if (port == NULL) { + if (default_port == NULL) { + err = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("no port in name"), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto done; + } + port = gpr_strdup(default_port); + } + + /* Call getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + + GRPC_SCHEDULING_START_BLOCKING_REGION; + s = getaddrinfo(host, port, &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + + if (s != 0) { + /* Retry if well-known service name is recognized */ + const char *svc[][2] = {{"http", "80"}, {"https", "443"}}; + for (i = 0; i < GPR_ARRAY_SIZE(svc); i++) { + if (strcmp(port, svc[i][0]) == 0) { + GRPC_SCHEDULING_START_BLOCKING_REGION; + s = getaddrinfo(host, svc[i][1], &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + break; + } + } + } + + if (s != 0) { + err = grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_str( + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("OS Error"), + GRPC_ERROR_INT_ERRNO, s), + GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(gai_strerror(s))), + GRPC_ERROR_STR_SYSCALL, + grpc_slice_from_static_string("getaddrinfo")), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(name)); + goto done; + } + + /* Success path: set addrs non-NULL, fill it in */ + *addresses = + (grpc_resolved_addresses *)gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + (*addresses)->naddrs++; + } + (*addresses)->addrs = (grpc_resolved_address *)gpr_malloc( + sizeof(grpc_resolved_address) * (*addresses)->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + (*addresses)->addrs[i].len = resp->ai_addrlen; + i++; + } + err = GRPC_ERROR_NONE; + +done: + gpr_free(host); + gpr_free(port); + if (result) { + freeaddrinfo(result); + } + return err; +} + +grpc_error *(*grpc_blocking_resolve_address)( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; + +typedef struct { + char *name; + char *default_port; + grpc_closure *on_done; + grpc_resolved_addresses **addrs_out; + grpc_closure request_closure; + void *arg; +} request; + +/* Callback to be passed to grpc_executor to asynch-ify + * grpc_blocking_resolve_address */ +static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, + grpc_error *error) { + request *r = (request *)rp; + GRPC_CLOSURE_SCHED( + exec_ctx, r->on_done, + grpc_blocking_resolve_address(r->name, r->default_port, r->addrs_out)); + gpr_free(r->name); + gpr_free(r->default_port); + gpr_free(r); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { + if (addrs != NULL) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) { + request *r = (request *)gpr_malloc(sizeof(request)); + GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); + r->name = gpr_strdup(name); + r->default_port = gpr_strdup(default_port); + r->on_done = on_done; + r->addrs_out = addrs; + GRPC_CLOSURE_SCHED(exec_ctx, &r->request_closure, GRPC_ERROR_NONE); +} + +void (*grpc_resolve_address)( + grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, + grpc_pollset_set *interested_parties, grpc_closure *on_done, + grpc_resolved_addresses **addrs) = resolve_address_impl; + +#endif diff --git a/src/core/lib/iomgr/resolve_address_uv.c b/src/core/lib/iomgr/resolve_address_uv.c deleted file mode 100644 index 2d438e8b48..0000000000 --- a/src/core/lib/iomgr/resolve_address_uv.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" -#ifdef GRPC_UV - -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/iomgr/closure.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" - -#include - -typedef struct request { - grpc_closure *on_done; - grpc_resolved_addresses **addresses; - struct addrinfo *hints; - char *host; - char *port; -} request; - -static int retry_named_port_failure(int status, request *r, - uv_getaddrinfo_cb getaddrinfo_cb) { - if (status != 0) { - // This loop is copied from resolve_address_posix.c - char *svc[][2] = {{"http", "80"}, {"https", "443"}}; - for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { - if (strcmp(r->port, svc[i][0]) == 0) { - int retry_status; - uv_getaddrinfo_t *req = gpr_malloc(sizeof(uv_getaddrinfo_t)); - req->data = r; - r->port = gpr_strdup(svc[i][1]); - retry_status = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_cb, - r->host, r->port, r->hints); - if (retry_status < 0 || getaddrinfo_cb == NULL) { - // The callback will not be called - gpr_free(req); - } - return retry_status; - } - } - } - /* If this function calls uv_getaddrinfo, it will return that function's - return value. That function only returns numbers <=0, so we can safely - return 1 to indicate that we never retried */ - return 1; -} - -static grpc_error *handle_addrinfo_result(int status, struct addrinfo *result, - grpc_resolved_addresses **addresses) { - struct addrinfo *resp; - size_t i; - if (status != 0) { - grpc_error *error; - *addresses = NULL; - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addresses)->naddrs = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - (*addresses)->naddrs++; - } - (*addresses)->addrs = - gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs); - i = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); - (*addresses)->addrs[i].len = resp->ai_addrlen; - i++; - } - - { - for (i = 0; i < (*addresses)->naddrs; i++) { - char *buf; - grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); - gpr_free(buf); - } - } - return GRPC_ERROR_NONE; -} - -static void getaddrinfo_callback(uv_getaddrinfo_t *req, int status, - struct addrinfo *res) { - request *r = (request *)req->data; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_error *error; - int retry_status; - char *port = r->port; - - gpr_free(req); - retry_status = retry_named_port_failure(status, r, getaddrinfo_callback); - if (retry_status == 0) { - /* The request is being retried. It is using its own port string, so we free - * the original one */ - gpr_free(port); - return; - } - /* Either no retry was attempted, or the retry failed. Either way, the - original error probably has more interesting information */ - error = handle_addrinfo_result(status, res, r->addresses); - GRPC_CLOSURE_SCHED(&exec_ctx, r->on_done, error); - grpc_exec_ctx_finish(&exec_ctx); - gpr_free(r->hints); - gpr_free(r->host); - gpr_free(r->port); - gpr_free(r); - uv_freeaddrinfo(res); -} - -static grpc_error *try_split_host_port(const char *name, - const char *default_port, char **host, - char **port) { - /* parse name, splitting it into host and port parts */ - grpc_error *error; - gpr_split_host_port(name, host, port); - if (*host == NULL) { - char *msg; - gpr_asprintf(&msg, "unparseable host:port: '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - if (*port == NULL) { - // TODO(murgatroid99): add tests for this case - if (default_port == NULL) { - char *msg; - gpr_asprintf(&msg, "no port in name '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - *port = gpr_strdup(default_port); - } - return GRPC_ERROR_NONE; -} - -static grpc_error *blocking_resolve_address_impl( - const char *name, const char *default_port, - grpc_resolved_addresses **addresses) { - char *host; - char *port; - struct addrinfo hints; - uv_getaddrinfo_t req; - int s; - grpc_error *err; - int retry_status; - - GRPC_UV_ASSERT_SAME_THREAD(); - - req.addrinfo = NULL; - - err = try_split_host_port(name, default_port, &host, &port); - if (err != GRPC_ERROR_NONE) { - goto done; - } - - /* Call getaddrinfo */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints.ai_socktype = SOCK_STREAM; /* stream socket */ - hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ - - s = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); - request r = { - .addresses = addresses, .hints = &hints, .host = host, .port = port}; - retry_status = retry_named_port_failure(s, &r, NULL); - if (retry_status <= 0) { - s = retry_status; - } - err = handle_addrinfo_result(s, req.addrinfo, addresses); - -done: - gpr_free(host); - gpr_free(port); - if (req.addrinfo) { - uv_freeaddrinfo(req.addrinfo); - } - return err; -} - -grpc_error *(*grpc_blocking_resolve_address)( - const char *name, const char *default_port, - grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; - -void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, - const char *default_port, - grpc_pollset_set *interested_parties, - grpc_closure *on_done, - grpc_resolved_addresses **addrs) { - uv_getaddrinfo_t *req = NULL; - request *r = NULL; - struct addrinfo *hints = NULL; - char *host = NULL; - char *port = NULL; - grpc_error *err; - int s; - GRPC_UV_ASSERT_SAME_THREAD(); - err = try_split_host_port(name, default_port, &host, &port); - if (err != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(exec_ctx, on_done, err); - gpr_free(host); - gpr_free(port); - return; - } - r = gpr_malloc(sizeof(request)); - r->on_done = on_done; - r->addresses = addrs; - r->host = host; - r->port = port; - req = gpr_malloc(sizeof(uv_getaddrinfo_t)); - req->data = r; - - /* Call getaddrinfo */ - hints = gpr_malloc(sizeof(struct addrinfo)); - memset(hints, 0, sizeof(struct addrinfo)); - hints->ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints->ai_socktype = SOCK_STREAM; /* stream socket */ - hints->ai_flags = AI_PASSIVE; /* for wildcard IP address */ - r->hints = hints; - - s = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_callback, host, port, - hints); - - if (s != 0) { - *addrs = NULL; - err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); - err = grpc_error_set_str(err, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(s))); - GRPC_CLOSURE_SCHED(exec_ctx, on_done, err); - gpr_free(r); - gpr_free(req); - gpr_free(hints); - gpr_free(host); - gpr_free(port); - } -} - -void (*grpc_resolve_address)( - grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, - grpc_pollset_set *interested_parties, grpc_closure *on_done, - grpc_resolved_addresses **addrs) = resolve_address_impl; - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/resolve_address_uv.cc b/src/core/lib/iomgr/resolve_address_uv.cc new file mode 100644 index 0000000000..2d438e8b48 --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_uv.cc @@ -0,0 +1,280 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_UV + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/closure.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include + +typedef struct request { + grpc_closure *on_done; + grpc_resolved_addresses **addresses; + struct addrinfo *hints; + char *host; + char *port; +} request; + +static int retry_named_port_failure(int status, request *r, + uv_getaddrinfo_cb getaddrinfo_cb) { + if (status != 0) { + // This loop is copied from resolve_address_posix.c + char *svc[][2] = {{"http", "80"}, {"https", "443"}}; + for (size_t i = 0; i < GPR_ARRAY_SIZE(svc); i++) { + if (strcmp(r->port, svc[i][0]) == 0) { + int retry_status; + uv_getaddrinfo_t *req = gpr_malloc(sizeof(uv_getaddrinfo_t)); + req->data = r; + r->port = gpr_strdup(svc[i][1]); + retry_status = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_cb, + r->host, r->port, r->hints); + if (retry_status < 0 || getaddrinfo_cb == NULL) { + // The callback will not be called + gpr_free(req); + } + return retry_status; + } + } + } + /* If this function calls uv_getaddrinfo, it will return that function's + return value. That function only returns numbers <=0, so we can safely + return 1 to indicate that we never retried */ + return 1; +} + +static grpc_error *handle_addrinfo_result(int status, struct addrinfo *result, + grpc_resolved_addresses **addresses) { + struct addrinfo *resp; + size_t i; + if (status != 0) { + grpc_error *error; + *addresses = NULL; + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + return error; + } + (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + (*addresses)->naddrs++; + } + (*addresses)->addrs = + gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + (*addresses)->addrs[i].len = resp->ai_addrlen; + i++; + } + + { + for (i = 0; i < (*addresses)->naddrs; i++) { + char *buf; + grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); + gpr_free(buf); + } + } + return GRPC_ERROR_NONE; +} + +static void getaddrinfo_callback(uv_getaddrinfo_t *req, int status, + struct addrinfo *res) { + request *r = (request *)req->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_error *error; + int retry_status; + char *port = r->port; + + gpr_free(req); + retry_status = retry_named_port_failure(status, r, getaddrinfo_callback); + if (retry_status == 0) { + /* The request is being retried. It is using its own port string, so we free + * the original one */ + gpr_free(port); + return; + } + /* Either no retry was attempted, or the retry failed. Either way, the + original error probably has more interesting information */ + error = handle_addrinfo_result(status, res, r->addresses); + GRPC_CLOSURE_SCHED(&exec_ctx, r->on_done, error); + grpc_exec_ctx_finish(&exec_ctx); + gpr_free(r->hints); + gpr_free(r->host); + gpr_free(r->port); + gpr_free(r); + uv_freeaddrinfo(res); +} + +static grpc_error *try_split_host_port(const char *name, + const char *default_port, char **host, + char **port) { + /* parse name, splitting it into host and port parts */ + grpc_error *error; + gpr_split_host_port(name, host, port); + if (*host == NULL) { + char *msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + if (*port == NULL) { + // TODO(murgatroid99): add tests for this case + if (default_port == NULL) { + char *msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *port = gpr_strdup(default_port); + } + return GRPC_ERROR_NONE; +} + +static grpc_error *blocking_resolve_address_impl( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) { + char *host; + char *port; + struct addrinfo hints; + uv_getaddrinfo_t req; + int s; + grpc_error *err; + int retry_status; + + GRPC_UV_ASSERT_SAME_THREAD(); + + req.addrinfo = NULL; + + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + goto done; + } + + /* Call getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + + s = uv_getaddrinfo(uv_default_loop(), &req, NULL, host, port, &hints); + request r = { + .addresses = addresses, .hints = &hints, .host = host, .port = port}; + retry_status = retry_named_port_failure(s, &r, NULL); + if (retry_status <= 0) { + s = retry_status; + } + err = handle_addrinfo_result(s, req.addrinfo, addresses); + +done: + gpr_free(host); + gpr_free(port); + if (req.addrinfo) { + uv_freeaddrinfo(req.addrinfo); + } + return err; +} + +grpc_error *(*grpc_blocking_resolve_address)( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { + if (addrs != NULL) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addrs) { + uv_getaddrinfo_t *req = NULL; + request *r = NULL; + struct addrinfo *hints = NULL; + char *host = NULL; + char *port = NULL; + grpc_error *err; + int s; + GRPC_UV_ASSERT_SAME_THREAD(); + err = try_split_host_port(name, default_port, &host, &port); + if (err != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(exec_ctx, on_done, err); + gpr_free(host); + gpr_free(port); + return; + } + r = gpr_malloc(sizeof(request)); + r->on_done = on_done; + r->addresses = addrs; + r->host = host; + r->port = port; + req = gpr_malloc(sizeof(uv_getaddrinfo_t)); + req->data = r; + + /* Call getaddrinfo */ + hints = gpr_malloc(sizeof(struct addrinfo)); + memset(hints, 0, sizeof(struct addrinfo)); + hints->ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints->ai_socktype = SOCK_STREAM; /* stream socket */ + hints->ai_flags = AI_PASSIVE; /* for wildcard IP address */ + r->hints = hints; + + s = uv_getaddrinfo(uv_default_loop(), req, getaddrinfo_callback, host, port, + hints); + + if (s != 0) { + *addrs = NULL; + err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getaddrinfo failed"); + err = grpc_error_set_str(err, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(s))); + GRPC_CLOSURE_SCHED(exec_ctx, on_done, err); + gpr_free(r); + gpr_free(req); + gpr_free(hints); + gpr_free(host); + gpr_free(port); + } +} + +void (*grpc_resolve_address)( + grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, + grpc_pollset_set *interested_parties, grpc_closure *on_done, + grpc_resolved_addresses **addrs) = resolve_address_impl; + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/resolve_address_windows.c b/src/core/lib/iomgr/resolve_address_windows.c deleted file mode 100644 index 0cb0029f4e..0000000000 --- a/src/core/lib/iomgr/resolve_address_windows.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" -#ifdef GRPC_WINSOCK_SOCKET - -#include "src/core/lib/iomgr/sockaddr.h" - -#include "src/core/lib/iomgr/resolve_address.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/string.h" - -typedef struct { - char *name; - char *default_port; - grpc_closure request_closure; - grpc_closure *on_done; - grpc_resolved_addresses **addresses; -} request; - -static grpc_error *blocking_resolve_address_impl( - const char *name, const char *default_port, - grpc_resolved_addresses **addresses) { - struct addrinfo hints; - struct addrinfo *result = NULL, *resp; - char *host; - char *port; - int s; - size_t i; - grpc_error *error = GRPC_ERROR_NONE; - - /* parse name, splitting it into host and port parts */ - gpr_split_host_port(name, &host, &port); - if (host == NULL) { - char *msg; - gpr_asprintf(&msg, "unparseable host:port: '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - goto done; - } - if (port == NULL) { - if (default_port == NULL) { - char *msg; - gpr_asprintf(&msg, "no port in name '%s'", name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - goto done; - } - port = gpr_strdup(default_port); - } - - /* Call getaddrinfo */ - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ - hints.ai_socktype = SOCK_STREAM; /* stream socket */ - hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ - - GRPC_SCHEDULING_START_BLOCKING_REGION; - s = getaddrinfo(host, port, &hints, &result); - GRPC_SCHEDULING_END_BLOCKING_REGION; - if (s != 0) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo"); - goto done; - } - - /* Success path: set addrs non-NULL, fill it in */ - (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addresses)->naddrs = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - (*addresses)->naddrs++; - } - (*addresses)->addrs = - gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs); - i = 0; - for (resp = result; resp != NULL; resp = resp->ai_next) { - memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); - (*addresses)->addrs[i].len = resp->ai_addrlen; - i++; - } - - { - for (i = 0; i < (*addresses)->naddrs; i++) { - char *buf; - grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); - gpr_free(buf); - } - } - -done: - gpr_free(host); - gpr_free(port); - if (result) { - freeaddrinfo(result); - } - return error; -} - -grpc_error *(*grpc_blocking_resolve_address)( - const char *name, const char *default_port, - grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; - -/* Callback to be passed to grpc_executor to asynch-ify - * grpc_blocking_resolve_address */ -static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, - grpc_error *error) { - request *r = rp; - if (error == GRPC_ERROR_NONE) { - error = - grpc_blocking_resolve_address(r->name, r->default_port, r->addresses); - } else { - GRPC_ERROR_REF(error); - } - GRPC_CLOSURE_SCHED(exec_ctx, r->on_done, error); - gpr_free(r->name); - gpr_free(r->default_port); - gpr_free(r); -} - -void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { - if (addrs != NULL) { - gpr_free(addrs->addrs); - } - gpr_free(addrs); -} - -static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, - const char *default_port, - grpc_pollset_set *interested_parties, - grpc_closure *on_done, - grpc_resolved_addresses **addresses) { - request *r = gpr_malloc(sizeof(request)); - GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, - grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); - r->name = gpr_strdup(name); - r->default_port = gpr_strdup(default_port); - r->on_done = on_done; - r->addresses = addresses; - GRPC_CLOSURE_SCHED(exec_ctx, &r->request_closure, GRPC_ERROR_NONE); -} - -void (*grpc_resolve_address)( - grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, - grpc_pollset_set *interested_parties, grpc_closure *on_done, - grpc_resolved_addresses **addresses) = resolve_address_impl; - -#endif diff --git a/src/core/lib/iomgr/resolve_address_windows.cc b/src/core/lib/iomgr/resolve_address_windows.cc new file mode 100644 index 0000000000..0cb0029f4e --- /dev/null +++ b/src/core/lib/iomgr/resolve_address_windows.cc @@ -0,0 +1,175 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include "src/core/lib/iomgr/resolve_address.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/string.h" + +typedef struct { + char *name; + char *default_port; + grpc_closure request_closure; + grpc_closure *on_done; + grpc_resolved_addresses **addresses; +} request; + +static grpc_error *blocking_resolve_address_impl( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) { + struct addrinfo hints; + struct addrinfo *result = NULL, *resp; + char *host; + char *port; + int s; + size_t i; + grpc_error *error = GRPC_ERROR_NONE; + + /* parse name, splitting it into host and port parts */ + gpr_split_host_port(name, &host, &port); + if (host == NULL) { + char *msg; + gpr_asprintf(&msg, "unparseable host:port: '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto done; + } + if (port == NULL) { + if (default_port == NULL) { + char *msg; + gpr_asprintf(&msg, "no port in name '%s'", name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto done; + } + port = gpr_strdup(default_port); + } + + /* Call getaddrinfo */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* ipv4 or ipv6 */ + hints.ai_socktype = SOCK_STREAM; /* stream socket */ + hints.ai_flags = AI_PASSIVE; /* for wildcard IP address */ + + GRPC_SCHEDULING_START_BLOCKING_REGION; + s = getaddrinfo(host, port, &hints, &result); + GRPC_SCHEDULING_END_BLOCKING_REGION; + if (s != 0) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "getaddrinfo"); + goto done; + } + + /* Success path: set addrs non-NULL, fill it in */ + (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses)->naddrs = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + (*addresses)->naddrs++; + } + (*addresses)->addrs = + gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs); + i = 0; + for (resp = result; resp != NULL; resp = resp->ai_next) { + memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); + (*addresses)->addrs[i].len = resp->ai_addrlen; + i++; + } + + { + for (i = 0; i < (*addresses)->naddrs; i++) { + char *buf; + grpc_sockaddr_to_string(&buf, &(*addresses)->addrs[i], 0); + gpr_free(buf); + } + } + +done: + gpr_free(host); + gpr_free(port); + if (result) { + freeaddrinfo(result); + } + return error; +} + +grpc_error *(*grpc_blocking_resolve_address)( + const char *name, const char *default_port, + grpc_resolved_addresses **addresses) = blocking_resolve_address_impl; + +/* Callback to be passed to grpc_executor to asynch-ify + * grpc_blocking_resolve_address */ +static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, + grpc_error *error) { + request *r = rp; + if (error == GRPC_ERROR_NONE) { + error = + grpc_blocking_resolve_address(r->name, r->default_port, r->addresses); + } else { + GRPC_ERROR_REF(error); + } + GRPC_CLOSURE_SCHED(exec_ctx, r->on_done, error); + gpr_free(r->name); + gpr_free(r->default_port); + gpr_free(r); +} + +void grpc_resolved_addresses_destroy(grpc_resolved_addresses *addrs) { + if (addrs != NULL) { + gpr_free(addrs->addrs); + } + gpr_free(addrs); +} + +static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, + const char *default_port, + grpc_pollset_set *interested_parties, + grpc_closure *on_done, + grpc_resolved_addresses **addresses) { + request *r = gpr_malloc(sizeof(request)); + GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); + r->name = gpr_strdup(name); + r->default_port = gpr_strdup(default_port); + r->on_done = on_done; + r->addresses = addresses; + GRPC_CLOSURE_SCHED(exec_ctx, &r->request_closure, GRPC_ERROR_NONE); +} + +void (*grpc_resolve_address)( + grpc_exec_ctx *exec_ctx, const char *name, const char *default_port, + grpc_pollset_set *interested_parties, grpc_closure *on_done, + grpc_resolved_addresses **addresses) = resolve_address_impl; + +#endif diff --git a/src/core/lib/iomgr/resource_quota.c b/src/core/lib/iomgr/resource_quota.c deleted file mode 100644 index 4d69986fbc..0000000000 --- a/src/core/lib/iomgr/resource_quota.c +++ /dev/null @@ -1,868 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/resource_quota.h" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/iomgr/combiner.h" - -grpc_tracer_flag grpc_resource_quota_trace = - GRPC_TRACER_INITIALIZER(false, "resource_quota"); - -#define MEMORY_USAGE_ESTIMATION_MAX 65536 - -/* Internal linked list pointers for a resource user */ -typedef struct { - grpc_resource_user *next; - grpc_resource_user *prev; -} grpc_resource_user_link; - -/* Resource users are kept in (potentially) several intrusive linked lists - at once. These are the list names. */ -typedef enum { - /* Resource users that are waiting for an allocation */ - GRPC_RULIST_AWAITING_ALLOCATION, - /* Resource users that have free memory available for internal reclamation */ - GRPC_RULIST_NON_EMPTY_FREE_POOL, - /* Resource users that have published a benign reclamation is available */ - GRPC_RULIST_RECLAIMER_BENIGN, - /* Resource users that have published a destructive reclamation is - available */ - GRPC_RULIST_RECLAIMER_DESTRUCTIVE, - /* Number of lists: must be last */ - GRPC_RULIST_COUNT -} grpc_rulist; - -struct grpc_resource_user { - /* The quota this resource user consumes from */ - grpc_resource_quota *resource_quota; - - /* Closure to schedule an allocation under the resource quota combiner lock */ - grpc_closure allocate_closure; - /* Closure to publish a non empty free pool under the resource quota combiner - lock */ - grpc_closure add_to_free_pool_closure; - - /* one ref for each ref call (released by grpc_resource_user_unref), and one - ref for each byte allocated (released by grpc_resource_user_free) */ - gpr_atm refs; - /* is this resource user unlocked? starts at 0, increases for each shutdown - call */ - gpr_atm shutdown; - - gpr_mu mu; - /* The amount of memory (in bytes) this user has cached for its own use: to - avoid quota contention, each resource user can keep some memory in - addition to what it is immediately using (e.g., for caching), and the quota - can pull it back under memory pressure. - This value can become negative if more memory has been requested than - existed in the free pool, at which point the quota is consulted to bring - this value non-negative (asynchronously). */ - int64_t free_pool; - /* A list of closures to call once free_pool becomes non-negative - ie when - all outstanding allocations have been granted. */ - grpc_closure_list on_allocated; - /* True if we are currently trying to allocate from the quota, false if not */ - bool allocating; - /* True if we are currently trying to add ourselves to the non-free quota - list, false otherwise */ - bool added_to_free_pool; - - /* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer - */ - grpc_closure *reclaimers[2]; - /* Reclaimers just posted: once we're in the combiner lock, we'll move them - to the array above */ - grpc_closure *new_reclaimers[2]; - /* Trampoline closures to finish reclamation and re-enter the quota combiner - lock */ - grpc_closure post_reclaimer_closure[2]; - - /* Closure to execute under the quota combiner to de-register and shutdown the - resource user */ - grpc_closure destroy_closure; - - /* Links in the various grpc_rulist lists */ - grpc_resource_user_link links[GRPC_RULIST_COUNT]; - - /* The name of this resource user, for debugging/tracing */ - char *name; -}; - -struct grpc_resource_quota { - /* refcount */ - gpr_refcount refs; - - /* estimate of current memory usage - scaled to the range [0..RESOURCE_USAGE_ESTIMATION_MAX] */ - gpr_atm memory_usage_estimation; - - /* Master combiner lock: all activity on a quota executes under this combiner - * (so no mutex is needed for this data structure) */ - grpc_combiner *combiner; - /* Size of the resource quota */ - int64_t size; - /* Amount of free memory in the resource quota */ - int64_t free_pool; - - gpr_atm last_size; - - /* Has rq_step been scheduled to occur? */ - bool step_scheduled; - /* Are we currently reclaiming memory */ - bool reclaiming; - /* Closure around rq_step */ - grpc_closure rq_step_closure; - /* Closure around rq_reclamation_done */ - grpc_closure rq_reclamation_done_closure; - - /* This is only really usable for debugging: it's always a stale pointer, but - a stale pointer that might just be fresh enough to guide us to where the - reclamation system is stuck */ - grpc_closure *debug_only_last_initiated_reclaimer; - grpc_resource_user *debug_only_last_reclaimer_resource_user; - - /* Roots of all resource user lists */ - grpc_resource_user *roots[GRPC_RULIST_COUNT]; - - char *name; -}; - -/******************************************************************************* - * list management - */ - -static void rulist_add_head(grpc_resource_user *resource_user, - grpc_rulist list) { - grpc_resource_quota *resource_quota = resource_user->resource_quota; - grpc_resource_user **root = &resource_quota->roots[list]; - if (*root == NULL) { - *root = resource_user; - resource_user->links[list].next = resource_user->links[list].prev = - resource_user; - } else { - resource_user->links[list].next = *root; - resource_user->links[list].prev = (*root)->links[list].prev; - resource_user->links[list].next->links[list].prev = - resource_user->links[list].prev->links[list].next = resource_user; - *root = resource_user; - } -} - -static void rulist_add_tail(grpc_resource_user *resource_user, - grpc_rulist list) { - grpc_resource_quota *resource_quota = resource_user->resource_quota; - grpc_resource_user **root = &resource_quota->roots[list]; - if (*root == NULL) { - *root = resource_user; - resource_user->links[list].next = resource_user->links[list].prev = - resource_user; - } else { - resource_user->links[list].next = (*root)->links[list].next; - resource_user->links[list].prev = *root; - resource_user->links[list].next->links[list].prev = - resource_user->links[list].prev->links[list].next = resource_user; - } -} - -static bool rulist_empty(grpc_resource_quota *resource_quota, - grpc_rulist list) { - return resource_quota->roots[list] == NULL; -} - -static grpc_resource_user *rulist_pop_head(grpc_resource_quota *resource_quota, - grpc_rulist list) { - grpc_resource_user **root = &resource_quota->roots[list]; - grpc_resource_user *resource_user = *root; - if (resource_user == NULL) { - return NULL; - } - if (resource_user->links[list].next == resource_user) { - *root = NULL; - } else { - resource_user->links[list].next->links[list].prev = - resource_user->links[list].prev; - resource_user->links[list].prev->links[list].next = - resource_user->links[list].next; - *root = resource_user->links[list].next; - } - resource_user->links[list].next = resource_user->links[list].prev = NULL; - return resource_user; -} - -static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) { - if (resource_user->links[list].next == NULL) return; - grpc_resource_quota *resource_quota = resource_user->resource_quota; - if (resource_quota->roots[list] == resource_user) { - resource_quota->roots[list] = resource_user->links[list].next; - if (resource_quota->roots[list] == resource_user) { - resource_quota->roots[list] = NULL; - } - } - resource_user->links[list].next->links[list].prev = - resource_user->links[list].prev; - resource_user->links[list].prev->links[list].next = - resource_user->links[list].next; - resource_user->links[list].next = resource_user->links[list].prev = NULL; -} - -/******************************************************************************* - * resource quota state machine - */ - -static bool rq_alloc(grpc_exec_ctx *exec_ctx, - grpc_resource_quota *resource_quota); -static bool rq_reclaim_from_per_user_free_pool( - grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota); -static bool rq_reclaim(grpc_exec_ctx *exec_ctx, - grpc_resource_quota *resource_quota, bool destructive); - -static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) { - grpc_resource_quota *resource_quota = (grpc_resource_quota *)rq; - resource_quota->step_scheduled = false; - do { - if (rq_alloc(exec_ctx, resource_quota)) goto done; - } while (rq_reclaim_from_per_user_free_pool(exec_ctx, resource_quota)); - - if (!rq_reclaim(exec_ctx, resource_quota, false)) { - rq_reclaim(exec_ctx, resource_quota, true); - } - -done: - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); -} - -static void rq_step_sched(grpc_exec_ctx *exec_ctx, - grpc_resource_quota *resource_quota) { - if (resource_quota->step_scheduled) return; - resource_quota->step_scheduled = true; - grpc_resource_quota_ref_internal(resource_quota); - GRPC_CLOSURE_SCHED(exec_ctx, &resource_quota->rq_step_closure, - GRPC_ERROR_NONE); -} - -/* update the atomically available resource estimate - use no barriers since - timeliness of delivery really doesn't matter much */ -static void rq_update_estimate(grpc_resource_quota *resource_quota) { - gpr_atm memory_usage_estimation = MEMORY_USAGE_ESTIMATION_MAX; - if (resource_quota->size != 0) { - memory_usage_estimation = - GPR_CLAMP((gpr_atm)((1.0 - - ((double)resource_quota->free_pool) / - ((double)resource_quota->size)) * - MEMORY_USAGE_ESTIMATION_MAX), - 0, MEMORY_USAGE_ESTIMATION_MAX); - } - gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, - memory_usage_estimation); -} - -/* returns true if all allocations are completed */ -static bool rq_alloc(grpc_exec_ctx *exec_ctx, - grpc_resource_quota *resource_quota) { - grpc_resource_user *resource_user; - while ((resource_user = rulist_pop_head(resource_quota, - GRPC_RULIST_AWAITING_ALLOCATION))) { - gpr_mu_lock(&resource_user->mu); - if (resource_user->free_pool < 0 && - -resource_user->free_pool <= resource_quota->free_pool) { - int64_t amt = -resource_user->free_pool; - resource_user->free_pool = 0; - resource_quota->free_pool -= amt; - rq_update_estimate(resource_quota); - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64 - " bytes; rq_free_pool -> %" PRId64, - resource_quota->name, resource_user->name, amt, - resource_quota->free_pool); - } - } else if (GRPC_TRACER_ON(grpc_resource_quota_trace) && - resource_user->free_pool >= 0) { - gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request", - resource_quota->name, resource_user->name); - } - if (resource_user->free_pool >= 0) { - resource_user->allocating = false; - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &resource_user->on_allocated); - gpr_mu_unlock(&resource_user->mu); - } else { - rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION); - gpr_mu_unlock(&resource_user->mu); - return false; - } - } - return true; -} - -/* returns true if any memory could be reclaimed from buffers */ -static bool rq_reclaim_from_per_user_free_pool( - grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) { - grpc_resource_user *resource_user; - while ((resource_user = rulist_pop_head(resource_quota, - GRPC_RULIST_NON_EMPTY_FREE_POOL))) { - gpr_mu_lock(&resource_user->mu); - if (resource_user->free_pool > 0) { - int64_t amt = resource_user->free_pool; - resource_user->free_pool = 0; - resource_quota->free_pool += amt; - rq_update_estimate(resource_quota); - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64 - " bytes; rq_free_pool -> %" PRId64, - resource_quota->name, resource_user->name, amt, - resource_quota->free_pool); - } - gpr_mu_unlock(&resource_user->mu); - return true; - } else { - gpr_mu_unlock(&resource_user->mu); - } - } - return false; -} - -/* returns true if reclamation is proceeding */ -static bool rq_reclaim(grpc_exec_ctx *exec_ctx, - grpc_resource_quota *resource_quota, bool destructive) { - if (resource_quota->reclaiming) return true; - grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE - : GRPC_RULIST_RECLAIMER_BENIGN; - grpc_resource_user *resource_user = rulist_pop_head(resource_quota, list); - if (resource_user == NULL) return false; - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation", - resource_quota->name, resource_user->name, - destructive ? "destructive" : "benign"); - } - resource_quota->reclaiming = true; - grpc_resource_quota_ref_internal(resource_quota); - grpc_closure *c = resource_user->reclaimers[destructive]; - GPR_ASSERT(c); - resource_quota->debug_only_last_reclaimer_resource_user = resource_user; - resource_quota->debug_only_last_initiated_reclaimer = c; - resource_user->reclaimers[destructive] = NULL; - GRPC_CLOSURE_RUN(exec_ctx, c, GRPC_ERROR_NONE); - return true; -} - -/******************************************************************************* - * ru_slice: a slice implementation that is backed by a grpc_resource_user - */ - -typedef struct { - grpc_slice_refcount base; - gpr_refcount refs; - grpc_resource_user *resource_user; - size_t size; -} ru_slice_refcount; - -static void ru_slice_ref(void *p) { - ru_slice_refcount *rc = (ru_slice_refcount *)p; - gpr_ref(&rc->refs); -} - -static void ru_slice_unref(grpc_exec_ctx *exec_ctx, void *p) { - ru_slice_refcount *rc = (ru_slice_refcount *)p; - if (gpr_unref(&rc->refs)) { - grpc_resource_user_free(exec_ctx, rc->resource_user, rc->size); - gpr_free(rc); - } -} - -static const grpc_slice_refcount_vtable ru_slice_vtable = { - ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl, - grpc_slice_default_hash_impl}; - -static grpc_slice ru_slice_create(grpc_resource_user *resource_user, - size_t size) { - ru_slice_refcount *rc = - (ru_slice_refcount *)gpr_malloc(sizeof(ru_slice_refcount) + size); - rc->base.vtable = &ru_slice_vtable; - rc->base.sub_refcount = &rc->base; - gpr_ref_init(&rc->refs, 1); - rc->resource_user = resource_user; - rc->size = size; - grpc_slice slice; - slice.refcount = &rc->base; - slice.data.refcounted.bytes = (uint8_t *)(rc + 1); - slice.data.refcounted.length = size; - return slice; -} - -/******************************************************************************* - * grpc_resource_quota internal implementation: resource user manipulation under - * the combiner - */ - -static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) { - grpc_resource_user *resource_user = (grpc_resource_user *)ru; - if (rulist_empty(resource_user->resource_quota, - GRPC_RULIST_AWAITING_ALLOCATION)) { - rq_step_sched(exec_ctx, resource_user->resource_quota); - } - rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION); -} - -static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru, - grpc_error *error) { - grpc_resource_user *resource_user = (grpc_resource_user *)ru; - if (!rulist_empty(resource_user->resource_quota, - GRPC_RULIST_AWAITING_ALLOCATION) && - rulist_empty(resource_user->resource_quota, - GRPC_RULIST_NON_EMPTY_FREE_POOL)) { - rq_step_sched(exec_ctx, resource_user->resource_quota); - } - rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL); -} - -static bool ru_post_reclaimer(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user, - bool destructive) { - grpc_closure *closure = resource_user->new_reclaimers[destructive]; - GPR_ASSERT(closure != NULL); - resource_user->new_reclaimers[destructive] = NULL; - GPR_ASSERT(resource_user->reclaimers[destructive] == NULL); - if (gpr_atm_acq_load(&resource_user->shutdown) > 0) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_CANCELLED); - return false; - } - resource_user->reclaimers[destructive] = closure; - return true; -} - -static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru, - grpc_error *error) { - grpc_resource_user *resource_user = (grpc_resource_user *)ru; - if (!ru_post_reclaimer(exec_ctx, resource_user, false)) return; - if (!rulist_empty(resource_user->resource_quota, - GRPC_RULIST_AWAITING_ALLOCATION) && - rulist_empty(resource_user->resource_quota, - GRPC_RULIST_NON_EMPTY_FREE_POOL) && - rulist_empty(resource_user->resource_quota, - GRPC_RULIST_RECLAIMER_BENIGN)) { - rq_step_sched(exec_ctx, resource_user->resource_quota); - } - rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN); -} - -static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru, - grpc_error *error) { - grpc_resource_user *resource_user = (grpc_resource_user *)ru; - if (!ru_post_reclaimer(exec_ctx, resource_user, true)) return; - if (!rulist_empty(resource_user->resource_quota, - GRPC_RULIST_AWAITING_ALLOCATION) && - rulist_empty(resource_user->resource_quota, - GRPC_RULIST_NON_EMPTY_FREE_POOL) && - rulist_empty(resource_user->resource_quota, - GRPC_RULIST_RECLAIMER_BENIGN) && - rulist_empty(resource_user->resource_quota, - GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) { - rq_step_sched(exec_ctx, resource_user->resource_quota); - } - rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE); -} - -static void ru_shutdown(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) { - grpc_resource_user *resource_user = (grpc_resource_user *)ru; - GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[0], - GRPC_ERROR_CANCELLED); - GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[1], - GRPC_ERROR_CANCELLED); - resource_user->reclaimers[0] = NULL; - resource_user->reclaimers[1] = NULL; - rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN); - rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE); -} - -static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) { - grpc_resource_user *resource_user = (grpc_resource_user *)ru; - GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->refs) == 0); - for (int i = 0; i < GRPC_RULIST_COUNT; i++) { - rulist_remove(resource_user, (grpc_rulist)i); - } - GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[0], - GRPC_ERROR_CANCELLED); - GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[1], - GRPC_ERROR_CANCELLED); - if (resource_user->free_pool != 0) { - resource_user->resource_quota->free_pool += resource_user->free_pool; - rq_step_sched(exec_ctx, resource_user->resource_quota); - } - grpc_resource_quota_unref_internal(exec_ctx, resource_user->resource_quota); - gpr_mu_destroy(&resource_user->mu); - gpr_free(resource_user->name); - gpr_free(resource_user); -} - -static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_resource_user_slice_allocator *slice_allocator = - (grpc_resource_user_slice_allocator *)arg; - if (error == GRPC_ERROR_NONE) { - for (size_t i = 0; i < slice_allocator->count; i++) { - grpc_slice_buffer_add_indexed( - slice_allocator->dest, ru_slice_create(slice_allocator->resource_user, - slice_allocator->length)); - } - } - GRPC_CLOSURE_RUN(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error)); -} - -/******************************************************************************* - * grpc_resource_quota internal implementation: quota manipulation under the - * combiner - */ - -typedef struct { - int64_t size; - grpc_resource_quota *resource_quota; - grpc_closure closure; -} rq_resize_args; - -static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) { - rq_resize_args *a = (rq_resize_args *)args; - int64_t delta = a->size - a->resource_quota->size; - a->resource_quota->size += delta; - a->resource_quota->free_pool += delta; - rq_update_estimate(a->resource_quota); - rq_step_sched(exec_ctx, a->resource_quota); - grpc_resource_quota_unref_internal(exec_ctx, a->resource_quota); - gpr_free(a); -} - -static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq, - grpc_error *error) { - grpc_resource_quota *resource_quota = (grpc_resource_quota *)rq; - resource_quota->reclaiming = false; - rq_step_sched(exec_ctx, resource_quota); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); -} - -/******************************************************************************* - * grpc_resource_quota api - */ - -/* Public API */ -grpc_resource_quota *grpc_resource_quota_create(const char *name) { - grpc_resource_quota *resource_quota = - (grpc_resource_quota *)gpr_malloc(sizeof(*resource_quota)); - gpr_ref_init(&resource_quota->refs, 1); - resource_quota->combiner = grpc_combiner_create(); - resource_quota->free_pool = INT64_MAX; - resource_quota->size = INT64_MAX; - gpr_atm_no_barrier_store(&resource_quota->last_size, GPR_ATM_MAX); - resource_quota->step_scheduled = false; - resource_quota->reclaiming = false; - gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, 0); - if (name != NULL) { - resource_quota->name = gpr_strdup(name); - } else { - gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR, - (intptr_t)resource_quota); - } - GRPC_CLOSURE_INIT(&resource_quota->rq_step_closure, rq_step, resource_quota, - grpc_combiner_finally_scheduler(resource_quota->combiner)); - GRPC_CLOSURE_INIT(&resource_quota->rq_reclamation_done_closure, - rq_reclamation_done, resource_quota, - grpc_combiner_scheduler(resource_quota->combiner)); - for (int i = 0; i < GRPC_RULIST_COUNT; i++) { - resource_quota->roots[i] = NULL; - } - return resource_quota; -} - -void grpc_resource_quota_unref_internal(grpc_exec_ctx *exec_ctx, - grpc_resource_quota *resource_quota) { - if (gpr_unref(&resource_quota->refs)) { - GRPC_COMBINER_UNREF(exec_ctx, resource_quota->combiner, "resource_quota"); - gpr_free(resource_quota->name); - gpr_free(resource_quota); - } -} - -/* Public API */ -void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_resource_quota_unref_internal(&exec_ctx, resource_quota); - grpc_exec_ctx_finish(&exec_ctx); -} - -grpc_resource_quota *grpc_resource_quota_ref_internal( - grpc_resource_quota *resource_quota) { - gpr_ref(&resource_quota->refs); - return resource_quota; -} - -/* Public API */ -void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) { - grpc_resource_quota_ref_internal(resource_quota); -} - -double grpc_resource_quota_get_memory_pressure( - grpc_resource_quota *resource_quota) { - return ((double)(gpr_atm_no_barrier_load( - &resource_quota->memory_usage_estimation))) / - ((double)MEMORY_USAGE_ESTIMATION_MAX); -} - -/* Public API */ -void grpc_resource_quota_resize(grpc_resource_quota *resource_quota, - size_t size) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - rq_resize_args *a = (rq_resize_args *)gpr_malloc(sizeof(*a)); - a->resource_quota = grpc_resource_quota_ref_internal(resource_quota); - a->size = (int64_t)size; - gpr_atm_no_barrier_store(&resource_quota->last_size, - (gpr_atm)GPR_MIN((size_t)GPR_ATM_MAX, size)); - GRPC_CLOSURE_INIT(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(&exec_ctx, &a->closure, GRPC_ERROR_NONE); - grpc_exec_ctx_finish(&exec_ctx); -} - -size_t grpc_resource_quota_peek_size(grpc_resource_quota *resource_quota) { - return (size_t)gpr_atm_no_barrier_load(&resource_quota->last_size); -} - -/******************************************************************************* - * grpc_resource_user channel args api - */ - -grpc_resource_quota *grpc_resource_quota_from_channel_args( - const grpc_channel_args *channel_args) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { - if (channel_args->args[i].type == GRPC_ARG_POINTER) { - return grpc_resource_quota_ref_internal( - (grpc_resource_quota *)channel_args->args[i].value.pointer.p); - } else { - gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer"); - } - } - } - return grpc_resource_quota_create(NULL); -} - -static void *rq_copy(void *rq) { - grpc_resource_quota_ref((grpc_resource_quota *)rq); - return rq; -} - -static void rq_destroy(grpc_exec_ctx *exec_ctx, void *rq) { - grpc_resource_quota_unref_internal(exec_ctx, (grpc_resource_quota *)rq); -} - -static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); } - -const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) { - static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp}; - return &vtable; -} - -/******************************************************************************* - * grpc_resource_user api - */ - -grpc_resource_user *grpc_resource_user_create( - grpc_resource_quota *resource_quota, const char *name) { - grpc_resource_user *resource_user = - (grpc_resource_user *)gpr_malloc(sizeof(*resource_user)); - resource_user->resource_quota = - grpc_resource_quota_ref_internal(resource_quota); - GRPC_CLOSURE_INIT(&resource_user->allocate_closure, &ru_allocate, - resource_user, - grpc_combiner_scheduler(resource_quota->combiner)); - GRPC_CLOSURE_INIT(&resource_user->add_to_free_pool_closure, - &ru_add_to_free_pool, resource_user, - grpc_combiner_scheduler(resource_quota->combiner)); - GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[0], - &ru_post_benign_reclaimer, resource_user, - grpc_combiner_scheduler(resource_quota->combiner)); - GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[1], - &ru_post_destructive_reclaimer, resource_user, - grpc_combiner_scheduler(resource_quota->combiner)); - GRPC_CLOSURE_INIT(&resource_user->destroy_closure, &ru_destroy, resource_user, - grpc_combiner_scheduler(resource_quota->combiner)); - gpr_mu_init(&resource_user->mu); - gpr_atm_rel_store(&resource_user->refs, 1); - gpr_atm_rel_store(&resource_user->shutdown, 0); - resource_user->free_pool = 0; - grpc_closure_list_init(&resource_user->on_allocated); - resource_user->allocating = false; - resource_user->added_to_free_pool = false; - resource_user->reclaimers[0] = NULL; - resource_user->reclaimers[1] = NULL; - resource_user->new_reclaimers[0] = NULL; - resource_user->new_reclaimers[1] = NULL; - for (int i = 0; i < GRPC_RULIST_COUNT; i++) { - resource_user->links[i].next = resource_user->links[i].prev = NULL; - } - if (name != NULL) { - resource_user->name = gpr_strdup(name); - } else { - gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR, - (intptr_t)resource_user); - } - return resource_user; -} - -grpc_resource_quota *grpc_resource_user_quota( - grpc_resource_user *resource_user) { - return resource_user->resource_quota; -} - -static void ru_ref_by(grpc_resource_user *resource_user, gpr_atm amount) { - GPR_ASSERT(amount > 0); - GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&resource_user->refs, amount) != 0); -} - -static void ru_unref_by(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user, gpr_atm amount) { - GPR_ASSERT(amount > 0); - gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount); - GPR_ASSERT(old >= amount); - if (old == amount) { - GRPC_CLOSURE_SCHED(exec_ctx, &resource_user->destroy_closure, - GRPC_ERROR_NONE); - } -} - -void grpc_resource_user_ref(grpc_resource_user *resource_user) { - ru_ref_by(resource_user, 1); -} - -void grpc_resource_user_unref(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user) { - ru_unref_by(exec_ctx, resource_user, 1); -} - -void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user) { - if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) { - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_CREATE( - ru_shutdown, resource_user, - grpc_combiner_scheduler(resource_user->resource_quota->combiner)), - GRPC_ERROR_NONE); - } -} - -void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user, size_t size, - grpc_closure *optional_on_done) { - gpr_mu_lock(&resource_user->mu); - ru_ref_by(resource_user, (gpr_atm)size); - resource_user->free_pool -= (int64_t)size; - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64, - resource_user->resource_quota->name, resource_user->name, size, - resource_user->free_pool); - } - if (resource_user->free_pool < 0) { - grpc_closure_list_append(&resource_user->on_allocated, optional_on_done, - GRPC_ERROR_NONE); - if (!resource_user->allocating) { - resource_user->allocating = true; - GRPC_CLOSURE_SCHED(exec_ctx, &resource_user->allocate_closure, - GRPC_ERROR_NONE); - } - } else { - GRPC_CLOSURE_SCHED(exec_ctx, optional_on_done, GRPC_ERROR_NONE); - } - gpr_mu_unlock(&resource_user->mu); -} - -void grpc_resource_user_free(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user, size_t size) { - gpr_mu_lock(&resource_user->mu); - bool was_zero_or_negative = resource_user->free_pool <= 0; - resource_user->free_pool += (int64_t)size; - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64, - resource_user->resource_quota->name, resource_user->name, size, - resource_user->free_pool); - } - bool is_bigger_than_zero = resource_user->free_pool > 0; - if (is_bigger_than_zero && was_zero_or_negative && - !resource_user->added_to_free_pool) { - resource_user->added_to_free_pool = true; - GRPC_CLOSURE_SCHED(exec_ctx, &resource_user->add_to_free_pool_closure, - GRPC_ERROR_NONE); - } - gpr_mu_unlock(&resource_user->mu); - ru_unref_by(exec_ctx, resource_user, (gpr_atm)size); -} - -void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user, - bool destructive, - grpc_closure *closure) { - GPR_ASSERT(resource_user->new_reclaimers[destructive] == NULL); - resource_user->new_reclaimers[destructive] = closure; - GRPC_CLOSURE_SCHED(exec_ctx, - &resource_user->post_reclaimer_closure[destructive], - GRPC_ERROR_NONE); -} - -void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user) { - if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { - gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete", - resource_user->resource_quota->name, resource_user->name); - } - GRPC_CLOSURE_SCHED( - exec_ctx, &resource_user->resource_quota->rq_reclamation_done_closure, - GRPC_ERROR_NONE); -} - -void grpc_resource_user_slice_allocator_init( - grpc_resource_user_slice_allocator *slice_allocator, - grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) { - GRPC_CLOSURE_INIT(&slice_allocator->on_allocated, ru_allocated_slices, - slice_allocator, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&slice_allocator->on_done, cb, p, - grpc_schedule_on_exec_ctx); - slice_allocator->resource_user = resource_user; -} - -void grpc_resource_user_alloc_slices( - grpc_exec_ctx *exec_ctx, - grpc_resource_user_slice_allocator *slice_allocator, size_t length, - size_t count, grpc_slice_buffer *dest) { - slice_allocator->length = length; - slice_allocator->count = count; - slice_allocator->dest = dest; - grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user, - count * length, &slice_allocator->on_allocated); -} - -grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user, - size_t size) { - grpc_resource_user_alloc(exec_ctx, resource_user, size, NULL); - return ru_slice_create(resource_user, size); -} diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc new file mode 100644 index 0000000000..4d69986fbc --- /dev/null +++ b/src/core/lib/iomgr/resource_quota.cc @@ -0,0 +1,868 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/resource_quota.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/combiner.h" + +grpc_tracer_flag grpc_resource_quota_trace = + GRPC_TRACER_INITIALIZER(false, "resource_quota"); + +#define MEMORY_USAGE_ESTIMATION_MAX 65536 + +/* Internal linked list pointers for a resource user */ +typedef struct { + grpc_resource_user *next; + grpc_resource_user *prev; +} grpc_resource_user_link; + +/* Resource users are kept in (potentially) several intrusive linked lists + at once. These are the list names. */ +typedef enum { + /* Resource users that are waiting for an allocation */ + GRPC_RULIST_AWAITING_ALLOCATION, + /* Resource users that have free memory available for internal reclamation */ + GRPC_RULIST_NON_EMPTY_FREE_POOL, + /* Resource users that have published a benign reclamation is available */ + GRPC_RULIST_RECLAIMER_BENIGN, + /* Resource users that have published a destructive reclamation is + available */ + GRPC_RULIST_RECLAIMER_DESTRUCTIVE, + /* Number of lists: must be last */ + GRPC_RULIST_COUNT +} grpc_rulist; + +struct grpc_resource_user { + /* The quota this resource user consumes from */ + grpc_resource_quota *resource_quota; + + /* Closure to schedule an allocation under the resource quota combiner lock */ + grpc_closure allocate_closure; + /* Closure to publish a non empty free pool under the resource quota combiner + lock */ + grpc_closure add_to_free_pool_closure; + + /* one ref for each ref call (released by grpc_resource_user_unref), and one + ref for each byte allocated (released by grpc_resource_user_free) */ + gpr_atm refs; + /* is this resource user unlocked? starts at 0, increases for each shutdown + call */ + gpr_atm shutdown; + + gpr_mu mu; + /* The amount of memory (in bytes) this user has cached for its own use: to + avoid quota contention, each resource user can keep some memory in + addition to what it is immediately using (e.g., for caching), and the quota + can pull it back under memory pressure. + This value can become negative if more memory has been requested than + existed in the free pool, at which point the quota is consulted to bring + this value non-negative (asynchronously). */ + int64_t free_pool; + /* A list of closures to call once free_pool becomes non-negative - ie when + all outstanding allocations have been granted. */ + grpc_closure_list on_allocated; + /* True if we are currently trying to allocate from the quota, false if not */ + bool allocating; + /* True if we are currently trying to add ourselves to the non-free quota + list, false otherwise */ + bool added_to_free_pool; + + /* Reclaimers: index 0 is the benign reclaimer, 1 is the destructive reclaimer + */ + grpc_closure *reclaimers[2]; + /* Reclaimers just posted: once we're in the combiner lock, we'll move them + to the array above */ + grpc_closure *new_reclaimers[2]; + /* Trampoline closures to finish reclamation and re-enter the quota combiner + lock */ + grpc_closure post_reclaimer_closure[2]; + + /* Closure to execute under the quota combiner to de-register and shutdown the + resource user */ + grpc_closure destroy_closure; + + /* Links in the various grpc_rulist lists */ + grpc_resource_user_link links[GRPC_RULIST_COUNT]; + + /* The name of this resource user, for debugging/tracing */ + char *name; +}; + +struct grpc_resource_quota { + /* refcount */ + gpr_refcount refs; + + /* estimate of current memory usage + scaled to the range [0..RESOURCE_USAGE_ESTIMATION_MAX] */ + gpr_atm memory_usage_estimation; + + /* Master combiner lock: all activity on a quota executes under this combiner + * (so no mutex is needed for this data structure) */ + grpc_combiner *combiner; + /* Size of the resource quota */ + int64_t size; + /* Amount of free memory in the resource quota */ + int64_t free_pool; + + gpr_atm last_size; + + /* Has rq_step been scheduled to occur? */ + bool step_scheduled; + /* Are we currently reclaiming memory */ + bool reclaiming; + /* Closure around rq_step */ + grpc_closure rq_step_closure; + /* Closure around rq_reclamation_done */ + grpc_closure rq_reclamation_done_closure; + + /* This is only really usable for debugging: it's always a stale pointer, but + a stale pointer that might just be fresh enough to guide us to where the + reclamation system is stuck */ + grpc_closure *debug_only_last_initiated_reclaimer; + grpc_resource_user *debug_only_last_reclaimer_resource_user; + + /* Roots of all resource user lists */ + grpc_resource_user *roots[GRPC_RULIST_COUNT]; + + char *name; +}; + +/******************************************************************************* + * list management + */ + +static void rulist_add_head(grpc_resource_user *resource_user, + grpc_rulist list) { + grpc_resource_quota *resource_quota = resource_user->resource_quota; + grpc_resource_user **root = &resource_quota->roots[list]; + if (*root == NULL) { + *root = resource_user; + resource_user->links[list].next = resource_user->links[list].prev = + resource_user; + } else { + resource_user->links[list].next = *root; + resource_user->links[list].prev = (*root)->links[list].prev; + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev->links[list].next = resource_user; + *root = resource_user; + } +} + +static void rulist_add_tail(grpc_resource_user *resource_user, + grpc_rulist list) { + grpc_resource_quota *resource_quota = resource_user->resource_quota; + grpc_resource_user **root = &resource_quota->roots[list]; + if (*root == NULL) { + *root = resource_user; + resource_user->links[list].next = resource_user->links[list].prev = + resource_user; + } else { + resource_user->links[list].next = (*root)->links[list].next; + resource_user->links[list].prev = *root; + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev->links[list].next = resource_user; + } +} + +static bool rulist_empty(grpc_resource_quota *resource_quota, + grpc_rulist list) { + return resource_quota->roots[list] == NULL; +} + +static grpc_resource_user *rulist_pop_head(grpc_resource_quota *resource_quota, + grpc_rulist list) { + grpc_resource_user **root = &resource_quota->roots[list]; + grpc_resource_user *resource_user = *root; + if (resource_user == NULL) { + return NULL; + } + if (resource_user->links[list].next == resource_user) { + *root = NULL; + } else { + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev; + resource_user->links[list].prev->links[list].next = + resource_user->links[list].next; + *root = resource_user->links[list].next; + } + resource_user->links[list].next = resource_user->links[list].prev = NULL; + return resource_user; +} + +static void rulist_remove(grpc_resource_user *resource_user, grpc_rulist list) { + if (resource_user->links[list].next == NULL) return; + grpc_resource_quota *resource_quota = resource_user->resource_quota; + if (resource_quota->roots[list] == resource_user) { + resource_quota->roots[list] = resource_user->links[list].next; + if (resource_quota->roots[list] == resource_user) { + resource_quota->roots[list] = NULL; + } + } + resource_user->links[list].next->links[list].prev = + resource_user->links[list].prev; + resource_user->links[list].prev->links[list].next = + resource_user->links[list].next; + resource_user->links[list].next = resource_user->links[list].prev = NULL; +} + +/******************************************************************************* + * resource quota state machine + */ + +static bool rq_alloc(grpc_exec_ctx *exec_ctx, + grpc_resource_quota *resource_quota); +static bool rq_reclaim_from_per_user_free_pool( + grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota); +static bool rq_reclaim(grpc_exec_ctx *exec_ctx, + grpc_resource_quota *resource_quota, bool destructive); + +static void rq_step(grpc_exec_ctx *exec_ctx, void *rq, grpc_error *error) { + grpc_resource_quota *resource_quota = (grpc_resource_quota *)rq; + resource_quota->step_scheduled = false; + do { + if (rq_alloc(exec_ctx, resource_quota)) goto done; + } while (rq_reclaim_from_per_user_free_pool(exec_ctx, resource_quota)); + + if (!rq_reclaim(exec_ctx, resource_quota, false)) { + rq_reclaim(exec_ctx, resource_quota, true); + } + +done: + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); +} + +static void rq_step_sched(grpc_exec_ctx *exec_ctx, + grpc_resource_quota *resource_quota) { + if (resource_quota->step_scheduled) return; + resource_quota->step_scheduled = true; + grpc_resource_quota_ref_internal(resource_quota); + GRPC_CLOSURE_SCHED(exec_ctx, &resource_quota->rq_step_closure, + GRPC_ERROR_NONE); +} + +/* update the atomically available resource estimate - use no barriers since + timeliness of delivery really doesn't matter much */ +static void rq_update_estimate(grpc_resource_quota *resource_quota) { + gpr_atm memory_usage_estimation = MEMORY_USAGE_ESTIMATION_MAX; + if (resource_quota->size != 0) { + memory_usage_estimation = + GPR_CLAMP((gpr_atm)((1.0 - + ((double)resource_quota->free_pool) / + ((double)resource_quota->size)) * + MEMORY_USAGE_ESTIMATION_MAX), + 0, MEMORY_USAGE_ESTIMATION_MAX); + } + gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, + memory_usage_estimation); +} + +/* returns true if all allocations are completed */ +static bool rq_alloc(grpc_exec_ctx *exec_ctx, + grpc_resource_quota *resource_quota) { + grpc_resource_user *resource_user; + while ((resource_user = rulist_pop_head(resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION))) { + gpr_mu_lock(&resource_user->mu); + if (resource_user->free_pool < 0 && + -resource_user->free_pool <= resource_quota->free_pool) { + int64_t amt = -resource_user->free_pool; + resource_user->free_pool = 0; + resource_quota->free_pool -= amt; + rq_update_estimate(resource_quota); + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "RQ %s %s: grant alloc %" PRId64 + " bytes; rq_free_pool -> %" PRId64, + resource_quota->name, resource_user->name, amt, + resource_quota->free_pool); + } + } else if (GRPC_TRACER_ON(grpc_resource_quota_trace) && + resource_user->free_pool >= 0) { + gpr_log(GPR_DEBUG, "RQ %s %s: discard already satisfied alloc request", + resource_quota->name, resource_user->name); + } + if (resource_user->free_pool >= 0) { + resource_user->allocating = false; + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &resource_user->on_allocated); + gpr_mu_unlock(&resource_user->mu); + } else { + rulist_add_head(resource_user, GRPC_RULIST_AWAITING_ALLOCATION); + gpr_mu_unlock(&resource_user->mu); + return false; + } + } + return true; +} + +/* returns true if any memory could be reclaimed from buffers */ +static bool rq_reclaim_from_per_user_free_pool( + grpc_exec_ctx *exec_ctx, grpc_resource_quota *resource_quota) { + grpc_resource_user *resource_user; + while ((resource_user = rulist_pop_head(resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL))) { + gpr_mu_lock(&resource_user->mu); + if (resource_user->free_pool > 0) { + int64_t amt = resource_user->free_pool; + resource_user->free_pool = 0; + resource_quota->free_pool += amt; + rq_update_estimate(resource_quota); + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "RQ %s %s: reclaim_from_per_user_free_pool %" PRId64 + " bytes; rq_free_pool -> %" PRId64, + resource_quota->name, resource_user->name, amt, + resource_quota->free_pool); + } + gpr_mu_unlock(&resource_user->mu); + return true; + } else { + gpr_mu_unlock(&resource_user->mu); + } + } + return false; +} + +/* returns true if reclamation is proceeding */ +static bool rq_reclaim(grpc_exec_ctx *exec_ctx, + grpc_resource_quota *resource_quota, bool destructive) { + if (resource_quota->reclaiming) return true; + grpc_rulist list = destructive ? GRPC_RULIST_RECLAIMER_DESTRUCTIVE + : GRPC_RULIST_RECLAIMER_BENIGN; + grpc_resource_user *resource_user = rulist_pop_head(resource_quota, list); + if (resource_user == NULL) return false; + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "RQ %s %s: initiate %s reclamation", + resource_quota->name, resource_user->name, + destructive ? "destructive" : "benign"); + } + resource_quota->reclaiming = true; + grpc_resource_quota_ref_internal(resource_quota); + grpc_closure *c = resource_user->reclaimers[destructive]; + GPR_ASSERT(c); + resource_quota->debug_only_last_reclaimer_resource_user = resource_user; + resource_quota->debug_only_last_initiated_reclaimer = c; + resource_user->reclaimers[destructive] = NULL; + GRPC_CLOSURE_RUN(exec_ctx, c, GRPC_ERROR_NONE); + return true; +} + +/******************************************************************************* + * ru_slice: a slice implementation that is backed by a grpc_resource_user + */ + +typedef struct { + grpc_slice_refcount base; + gpr_refcount refs; + grpc_resource_user *resource_user; + size_t size; +} ru_slice_refcount; + +static void ru_slice_ref(void *p) { + ru_slice_refcount *rc = (ru_slice_refcount *)p; + gpr_ref(&rc->refs); +} + +static void ru_slice_unref(grpc_exec_ctx *exec_ctx, void *p) { + ru_slice_refcount *rc = (ru_slice_refcount *)p; + if (gpr_unref(&rc->refs)) { + grpc_resource_user_free(exec_ctx, rc->resource_user, rc->size); + gpr_free(rc); + } +} + +static const grpc_slice_refcount_vtable ru_slice_vtable = { + ru_slice_ref, ru_slice_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +static grpc_slice ru_slice_create(grpc_resource_user *resource_user, + size_t size) { + ru_slice_refcount *rc = + (ru_slice_refcount *)gpr_malloc(sizeof(ru_slice_refcount) + size); + rc->base.vtable = &ru_slice_vtable; + rc->base.sub_refcount = &rc->base; + gpr_ref_init(&rc->refs, 1); + rc->resource_user = resource_user; + rc->size = size; + grpc_slice slice; + slice.refcount = &rc->base; + slice.data.refcounted.bytes = (uint8_t *)(rc + 1); + slice.data.refcounted.length = size; + return slice; +} + +/******************************************************************************* + * grpc_resource_quota internal implementation: resource user manipulation under + * the combiner + */ + +static void ru_allocate(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) { + grpc_resource_user *resource_user = (grpc_resource_user *)ru; + if (rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION)) { + rq_step_sched(exec_ctx, resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_AWAITING_ALLOCATION); +} + +static void ru_add_to_free_pool(grpc_exec_ctx *exec_ctx, void *ru, + grpc_error *error) { + grpc_resource_user *resource_user = (grpc_resource_user *)ru; + if (!rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL)) { + rq_step_sched(exec_ctx, resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_NON_EMPTY_FREE_POOL); +} + +static bool ru_post_reclaimer(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user, + bool destructive) { + grpc_closure *closure = resource_user->new_reclaimers[destructive]; + GPR_ASSERT(closure != NULL); + resource_user->new_reclaimers[destructive] = NULL; + GPR_ASSERT(resource_user->reclaimers[destructive] == NULL); + if (gpr_atm_acq_load(&resource_user->shutdown) > 0) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_CANCELLED); + return false; + } + resource_user->reclaimers[destructive] = closure; + return true; +} + +static void ru_post_benign_reclaimer(grpc_exec_ctx *exec_ctx, void *ru, + grpc_error *error) { + grpc_resource_user *resource_user = (grpc_resource_user *)ru; + if (!ru_post_reclaimer(exec_ctx, resource_user, false)) return; + if (!rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_RECLAIMER_BENIGN)) { + rq_step_sched(exec_ctx, resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_BENIGN); +} + +static void ru_post_destructive_reclaimer(grpc_exec_ctx *exec_ctx, void *ru, + grpc_error *error) { + grpc_resource_user *resource_user = (grpc_resource_user *)ru; + if (!ru_post_reclaimer(exec_ctx, resource_user, true)) return; + if (!rulist_empty(resource_user->resource_quota, + GRPC_RULIST_AWAITING_ALLOCATION) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_NON_EMPTY_FREE_POOL) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_RECLAIMER_BENIGN) && + rulist_empty(resource_user->resource_quota, + GRPC_RULIST_RECLAIMER_DESTRUCTIVE)) { + rq_step_sched(exec_ctx, resource_user->resource_quota); + } + rulist_add_tail(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE); +} + +static void ru_shutdown(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) { + grpc_resource_user *resource_user = (grpc_resource_user *)ru; + GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[0], + GRPC_ERROR_CANCELLED); + GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[1], + GRPC_ERROR_CANCELLED); + resource_user->reclaimers[0] = NULL; + resource_user->reclaimers[1] = NULL; + rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_BENIGN); + rulist_remove(resource_user, GRPC_RULIST_RECLAIMER_DESTRUCTIVE); +} + +static void ru_destroy(grpc_exec_ctx *exec_ctx, void *ru, grpc_error *error) { + grpc_resource_user *resource_user = (grpc_resource_user *)ru; + GPR_ASSERT(gpr_atm_no_barrier_load(&resource_user->refs) == 0); + for (int i = 0; i < GRPC_RULIST_COUNT; i++) { + rulist_remove(resource_user, (grpc_rulist)i); + } + GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[0], + GRPC_ERROR_CANCELLED); + GRPC_CLOSURE_SCHED(exec_ctx, resource_user->reclaimers[1], + GRPC_ERROR_CANCELLED); + if (resource_user->free_pool != 0) { + resource_user->resource_quota->free_pool += resource_user->free_pool; + rq_step_sched(exec_ctx, resource_user->resource_quota); + } + grpc_resource_quota_unref_internal(exec_ctx, resource_user->resource_quota); + gpr_mu_destroy(&resource_user->mu); + gpr_free(resource_user->name); + gpr_free(resource_user); +} + +static void ru_allocated_slices(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_resource_user_slice_allocator *slice_allocator = + (grpc_resource_user_slice_allocator *)arg; + if (error == GRPC_ERROR_NONE) { + for (size_t i = 0; i < slice_allocator->count; i++) { + grpc_slice_buffer_add_indexed( + slice_allocator->dest, ru_slice_create(slice_allocator->resource_user, + slice_allocator->length)); + } + } + GRPC_CLOSURE_RUN(exec_ctx, &slice_allocator->on_done, GRPC_ERROR_REF(error)); +} + +/******************************************************************************* + * grpc_resource_quota internal implementation: quota manipulation under the + * combiner + */ + +typedef struct { + int64_t size; + grpc_resource_quota *resource_quota; + grpc_closure closure; +} rq_resize_args; + +static void rq_resize(grpc_exec_ctx *exec_ctx, void *args, grpc_error *error) { + rq_resize_args *a = (rq_resize_args *)args; + int64_t delta = a->size - a->resource_quota->size; + a->resource_quota->size += delta; + a->resource_quota->free_pool += delta; + rq_update_estimate(a->resource_quota); + rq_step_sched(exec_ctx, a->resource_quota); + grpc_resource_quota_unref_internal(exec_ctx, a->resource_quota); + gpr_free(a); +} + +static void rq_reclamation_done(grpc_exec_ctx *exec_ctx, void *rq, + grpc_error *error) { + grpc_resource_quota *resource_quota = (grpc_resource_quota *)rq; + resource_quota->reclaiming = false; + rq_step_sched(exec_ctx, resource_quota); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); +} + +/******************************************************************************* + * grpc_resource_quota api + */ + +/* Public API */ +grpc_resource_quota *grpc_resource_quota_create(const char *name) { + grpc_resource_quota *resource_quota = + (grpc_resource_quota *)gpr_malloc(sizeof(*resource_quota)); + gpr_ref_init(&resource_quota->refs, 1); + resource_quota->combiner = grpc_combiner_create(); + resource_quota->free_pool = INT64_MAX; + resource_quota->size = INT64_MAX; + gpr_atm_no_barrier_store(&resource_quota->last_size, GPR_ATM_MAX); + resource_quota->step_scheduled = false; + resource_quota->reclaiming = false; + gpr_atm_no_barrier_store(&resource_quota->memory_usage_estimation, 0); + if (name != NULL) { + resource_quota->name = gpr_strdup(name); + } else { + gpr_asprintf(&resource_quota->name, "anonymous_pool_%" PRIxPTR, + (intptr_t)resource_quota); + } + GRPC_CLOSURE_INIT(&resource_quota->rq_step_closure, rq_step, resource_quota, + grpc_combiner_finally_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_quota->rq_reclamation_done_closure, + rq_reclamation_done, resource_quota, + grpc_combiner_scheduler(resource_quota->combiner)); + for (int i = 0; i < GRPC_RULIST_COUNT; i++) { + resource_quota->roots[i] = NULL; + } + return resource_quota; +} + +void grpc_resource_quota_unref_internal(grpc_exec_ctx *exec_ctx, + grpc_resource_quota *resource_quota) { + if (gpr_unref(&resource_quota->refs)) { + GRPC_COMBINER_UNREF(exec_ctx, resource_quota->combiner, "resource_quota"); + gpr_free(resource_quota->name); + gpr_free(resource_quota); + } +} + +/* Public API */ +void grpc_resource_quota_unref(grpc_resource_quota *resource_quota) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_resource_quota_unref_internal(&exec_ctx, resource_quota); + grpc_exec_ctx_finish(&exec_ctx); +} + +grpc_resource_quota *grpc_resource_quota_ref_internal( + grpc_resource_quota *resource_quota) { + gpr_ref(&resource_quota->refs); + return resource_quota; +} + +/* Public API */ +void grpc_resource_quota_ref(grpc_resource_quota *resource_quota) { + grpc_resource_quota_ref_internal(resource_quota); +} + +double grpc_resource_quota_get_memory_pressure( + grpc_resource_quota *resource_quota) { + return ((double)(gpr_atm_no_barrier_load( + &resource_quota->memory_usage_estimation))) / + ((double)MEMORY_USAGE_ESTIMATION_MAX); +} + +/* Public API */ +void grpc_resource_quota_resize(grpc_resource_quota *resource_quota, + size_t size) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + rq_resize_args *a = (rq_resize_args *)gpr_malloc(sizeof(*a)); + a->resource_quota = grpc_resource_quota_ref_internal(resource_quota); + a->size = (int64_t)size; + gpr_atm_no_barrier_store(&resource_quota->last_size, + (gpr_atm)GPR_MIN((size_t)GPR_ATM_MAX, size)); + GRPC_CLOSURE_INIT(&a->closure, rq_resize, a, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(&exec_ctx, &a->closure, GRPC_ERROR_NONE); + grpc_exec_ctx_finish(&exec_ctx); +} + +size_t grpc_resource_quota_peek_size(grpc_resource_quota *resource_quota) { + return (size_t)gpr_atm_no_barrier_load(&resource_quota->last_size); +} + +/******************************************************************************* + * grpc_resource_user channel args api + */ + +grpc_resource_quota *grpc_resource_quota_from_channel_args( + const grpc_channel_args *channel_args) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + if (channel_args->args[i].type == GRPC_ARG_POINTER) { + return grpc_resource_quota_ref_internal( + (grpc_resource_quota *)channel_args->args[i].value.pointer.p); + } else { + gpr_log(GPR_DEBUG, GRPC_ARG_RESOURCE_QUOTA " should be a pointer"); + } + } + } + return grpc_resource_quota_create(NULL); +} + +static void *rq_copy(void *rq) { + grpc_resource_quota_ref((grpc_resource_quota *)rq); + return rq; +} + +static void rq_destroy(grpc_exec_ctx *exec_ctx, void *rq) { + grpc_resource_quota_unref_internal(exec_ctx, (grpc_resource_quota *)rq); +} + +static int rq_cmp(void *a, void *b) { return GPR_ICMP(a, b); } + +const grpc_arg_pointer_vtable *grpc_resource_quota_arg_vtable(void) { + static const grpc_arg_pointer_vtable vtable = {rq_copy, rq_destroy, rq_cmp}; + return &vtable; +} + +/******************************************************************************* + * grpc_resource_user api + */ + +grpc_resource_user *grpc_resource_user_create( + grpc_resource_quota *resource_quota, const char *name) { + grpc_resource_user *resource_user = + (grpc_resource_user *)gpr_malloc(sizeof(*resource_user)); + resource_user->resource_quota = + grpc_resource_quota_ref_internal(resource_quota); + GRPC_CLOSURE_INIT(&resource_user->allocate_closure, &ru_allocate, + resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->add_to_free_pool_closure, + &ru_add_to_free_pool, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[0], + &ru_post_benign_reclaimer, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->post_reclaimer_closure[1], + &ru_post_destructive_reclaimer, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + GRPC_CLOSURE_INIT(&resource_user->destroy_closure, &ru_destroy, resource_user, + grpc_combiner_scheduler(resource_quota->combiner)); + gpr_mu_init(&resource_user->mu); + gpr_atm_rel_store(&resource_user->refs, 1); + gpr_atm_rel_store(&resource_user->shutdown, 0); + resource_user->free_pool = 0; + grpc_closure_list_init(&resource_user->on_allocated); + resource_user->allocating = false; + resource_user->added_to_free_pool = false; + resource_user->reclaimers[0] = NULL; + resource_user->reclaimers[1] = NULL; + resource_user->new_reclaimers[0] = NULL; + resource_user->new_reclaimers[1] = NULL; + for (int i = 0; i < GRPC_RULIST_COUNT; i++) { + resource_user->links[i].next = resource_user->links[i].prev = NULL; + } + if (name != NULL) { + resource_user->name = gpr_strdup(name); + } else { + gpr_asprintf(&resource_user->name, "anonymous_resource_user_%" PRIxPTR, + (intptr_t)resource_user); + } + return resource_user; +} + +grpc_resource_quota *grpc_resource_user_quota( + grpc_resource_user *resource_user) { + return resource_user->resource_quota; +} + +static void ru_ref_by(grpc_resource_user *resource_user, gpr_atm amount) { + GPR_ASSERT(amount > 0); + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&resource_user->refs, amount) != 0); +} + +static void ru_unref_by(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user, gpr_atm amount) { + GPR_ASSERT(amount > 0); + gpr_atm old = gpr_atm_full_fetch_add(&resource_user->refs, -amount); + GPR_ASSERT(old >= amount); + if (old == amount) { + GRPC_CLOSURE_SCHED(exec_ctx, &resource_user->destroy_closure, + GRPC_ERROR_NONE); + } +} + +void grpc_resource_user_ref(grpc_resource_user *resource_user) { + ru_ref_by(resource_user, 1); +} + +void grpc_resource_user_unref(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user) { + ru_unref_by(exec_ctx, resource_user, 1); +} + +void grpc_resource_user_shutdown(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user) { + if (gpr_atm_full_fetch_add(&resource_user->shutdown, 1) == 0) { + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_CREATE( + ru_shutdown, resource_user, + grpc_combiner_scheduler(resource_user->resource_quota->combiner)), + GRPC_ERROR_NONE); + } +} + +void grpc_resource_user_alloc(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user, size_t size, + grpc_closure *optional_on_done) { + gpr_mu_lock(&resource_user->mu); + ru_ref_by(resource_user, (gpr_atm)size); + resource_user->free_pool -= (int64_t)size; + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "RQ %s %s: alloc %" PRIdPTR "; free_pool -> %" PRId64, + resource_user->resource_quota->name, resource_user->name, size, + resource_user->free_pool); + } + if (resource_user->free_pool < 0) { + grpc_closure_list_append(&resource_user->on_allocated, optional_on_done, + GRPC_ERROR_NONE); + if (!resource_user->allocating) { + resource_user->allocating = true; + GRPC_CLOSURE_SCHED(exec_ctx, &resource_user->allocate_closure, + GRPC_ERROR_NONE); + } + } else { + GRPC_CLOSURE_SCHED(exec_ctx, optional_on_done, GRPC_ERROR_NONE); + } + gpr_mu_unlock(&resource_user->mu); +} + +void grpc_resource_user_free(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user, size_t size) { + gpr_mu_lock(&resource_user->mu); + bool was_zero_or_negative = resource_user->free_pool <= 0; + resource_user->free_pool += (int64_t)size; + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "RQ %s %s: free %" PRIdPTR "; free_pool -> %" PRId64, + resource_user->resource_quota->name, resource_user->name, size, + resource_user->free_pool); + } + bool is_bigger_than_zero = resource_user->free_pool > 0; + if (is_bigger_than_zero && was_zero_or_negative && + !resource_user->added_to_free_pool) { + resource_user->added_to_free_pool = true; + GRPC_CLOSURE_SCHED(exec_ctx, &resource_user->add_to_free_pool_closure, + GRPC_ERROR_NONE); + } + gpr_mu_unlock(&resource_user->mu); + ru_unref_by(exec_ctx, resource_user, (gpr_atm)size); +} + +void grpc_resource_user_post_reclaimer(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user, + bool destructive, + grpc_closure *closure) { + GPR_ASSERT(resource_user->new_reclaimers[destructive] == NULL); + resource_user->new_reclaimers[destructive] = closure; + GRPC_CLOSURE_SCHED(exec_ctx, + &resource_user->post_reclaimer_closure[destructive], + GRPC_ERROR_NONE); +} + +void grpc_resource_user_finish_reclamation(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user) { + if (GRPC_TRACER_ON(grpc_resource_quota_trace)) { + gpr_log(GPR_DEBUG, "RQ %s %s: reclamation complete", + resource_user->resource_quota->name, resource_user->name); + } + GRPC_CLOSURE_SCHED( + exec_ctx, &resource_user->resource_quota->rq_reclamation_done_closure, + GRPC_ERROR_NONE); +} + +void grpc_resource_user_slice_allocator_init( + grpc_resource_user_slice_allocator *slice_allocator, + grpc_resource_user *resource_user, grpc_iomgr_cb_func cb, void *p) { + GRPC_CLOSURE_INIT(&slice_allocator->on_allocated, ru_allocated_slices, + slice_allocator, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&slice_allocator->on_done, cb, p, + grpc_schedule_on_exec_ctx); + slice_allocator->resource_user = resource_user; +} + +void grpc_resource_user_alloc_slices( + grpc_exec_ctx *exec_ctx, + grpc_resource_user_slice_allocator *slice_allocator, size_t length, + size_t count, grpc_slice_buffer *dest) { + slice_allocator->length = length; + slice_allocator->count = count; + slice_allocator->dest = dest; + grpc_resource_user_alloc(exec_ctx, slice_allocator->resource_user, + count * length, &slice_allocator->on_allocated); +} + +grpc_slice grpc_resource_user_slice_malloc(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user, + size_t size) { + grpc_resource_user_alloc(exec_ctx, resource_user, size, NULL); + return ru_slice_create(resource_user, size); +} diff --git a/src/core/lib/iomgr/sockaddr_utils.c b/src/core/lib/iomgr/sockaddr_utils.c deleted file mode 100644 index 3f4145d104..0000000000 --- a/src/core/lib/iomgr/sockaddr_utils.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/sockaddr_utils.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/socket_utils.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/support/string.h" - -static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0xff, 0xff}; - -int grpc_sockaddr_is_v4mapped(const grpc_resolved_address *resolved_addr, - grpc_resolved_address *resolved_addr4_out) { - GPR_ASSERT(resolved_addr != resolved_addr4_out); - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - struct sockaddr_in *addr4_out = - resolved_addr4_out == NULL - ? NULL - : (struct sockaddr_in *)resolved_addr4_out->addr; - if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; - if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, - sizeof(kV4MappedPrefix)) == 0) { - if (resolved_addr4_out != NULL) { - /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ - memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out)); - addr4_out->sin_family = AF_INET; - /* s6_addr32 would be nice, but it's non-standard. */ - memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4); - addr4_out->sin_port = addr6->sin6_port; - resolved_addr4_out->len = sizeof(struct sockaddr_in); - } - return 1; - } - } - return 0; -} - -int grpc_sockaddr_to_v4mapped(const grpc_resolved_address *resolved_addr, - grpc_resolved_address *resolved_addr6_out) { - GPR_ASSERT(resolved_addr != resolved_addr6_out); - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - struct sockaddr_in6 *addr6_out = - (struct sockaddr_in6 *)resolved_addr6_out->addr; - if (addr->sa_family == AF_INET) { - const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; - memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out)); - addr6_out->sin6_family = AF_INET6; - memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12); - memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4); - addr6_out->sin6_port = addr4->sin_port; - resolved_addr6_out->len = sizeof(struct sockaddr_in6); - return 1; - } - return 0; -} - -int grpc_sockaddr_is_wildcard(const grpc_resolved_address *resolved_addr, - int *port_out) { - const struct sockaddr *addr; - grpc_resolved_address addr4_normalized; - if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) { - resolved_addr = &addr4_normalized; - } - addr = (const struct sockaddr *)resolved_addr->addr; - if (addr->sa_family == AF_INET) { - /* Check for 0.0.0.0 */ - const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; - if (addr4->sin_addr.s_addr != 0) { - return 0; - } - *port_out = ntohs(addr4->sin_port); - return 1; - } else if (addr->sa_family == AF_INET6) { - /* Check for :: */ - const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; - int i; - for (i = 0; i < 16; i++) { - if (addr6->sin6_addr.s6_addr[i] != 0) { - return 0; - } - } - *port_out = ntohs(addr6->sin6_port); - return 1; - } else { - return 0; - } -} - -void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address *wild4_out, - grpc_resolved_address *wild6_out) { - grpc_sockaddr_make_wildcard4(port, wild4_out); - grpc_sockaddr_make_wildcard6(port, wild6_out); -} - -void grpc_sockaddr_make_wildcard4(int port, - grpc_resolved_address *resolved_wild_out) { - struct sockaddr_in *wild_out = (struct sockaddr_in *)resolved_wild_out->addr; - GPR_ASSERT(port >= 0 && port < 65536); - memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); - wild_out->sin_family = AF_INET; - wild_out->sin_port = htons((uint16_t)port); - resolved_wild_out->len = sizeof(struct sockaddr_in); -} - -void grpc_sockaddr_make_wildcard6(int port, - grpc_resolved_address *resolved_wild_out) { - struct sockaddr_in6 *wild_out = - (struct sockaddr_in6 *)resolved_wild_out->addr; - GPR_ASSERT(port >= 0 && port < 65536); - memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); - wild_out->sin6_family = AF_INET6; - wild_out->sin6_port = htons((uint16_t)port); - resolved_wild_out->len = sizeof(struct sockaddr_in6); -} - -int grpc_sockaddr_to_string(char **out, - const grpc_resolved_address *resolved_addr, - int normalize) { - const struct sockaddr *addr; - const int save_errno = errno; - grpc_resolved_address addr_normalized; - char ntop_buf[INET6_ADDRSTRLEN]; - const void *ip = NULL; - int port; - uint32_t sin6_scope_id = 0; - int ret; - - *out = NULL; - if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { - resolved_addr = &addr_normalized; - } - addr = (const struct sockaddr *)resolved_addr->addr; - if (addr->sa_family == AF_INET) { - const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; - ip = &addr4->sin_addr; - port = ntohs(addr4->sin_port); - } else if (addr->sa_family == AF_INET6) { - const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; - ip = &addr6->sin6_addr; - port = ntohs(addr6->sin6_port); - sin6_scope_id = addr6->sin6_scope_id; - } - if (ip != NULL && - grpc_inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) { - if (sin6_scope_id != 0) { - char *host_with_scope; - /* Enclose sin6_scope_id with the format defined in RFC 6784 section 2. */ - gpr_asprintf(&host_with_scope, "%s%%25%" PRIu32, ntop_buf, sin6_scope_id); - ret = gpr_join_host_port(out, host_with_scope, port); - gpr_free(host_with_scope); - } else { - ret = gpr_join_host_port(out, ntop_buf, port); - } - } else { - ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family); - } - /* This is probably redundant, but we wouldn't want to log the wrong error. */ - errno = save_errno; - return ret; -} - -char *grpc_sockaddr_to_uri(const grpc_resolved_address *resolved_addr) { - grpc_resolved_address addr_normalized; - if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { - resolved_addr = &addr_normalized; - } - const char *scheme = grpc_sockaddr_get_uri_scheme(resolved_addr); - if (scheme == NULL || strcmp("unix", scheme) == 0) { - return grpc_sockaddr_to_uri_unix_if_possible(resolved_addr); - } - char *path = NULL; - char *uri_str = NULL; - if (grpc_sockaddr_to_string(&path, resolved_addr, - false /* suppress errors */) && - scheme != NULL) { - gpr_asprintf(&uri_str, "%s:%s", scheme, path); - } - gpr_free(path); - return uri_str != NULL ? uri_str : NULL; -} - -const char *grpc_sockaddr_get_uri_scheme( - const grpc_resolved_address *resolved_addr) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - switch (addr->sa_family) { - case AF_INET: - return "ipv4"; - case AF_INET6: - return "ipv6"; - case AF_UNIX: - return "unix"; - } - return NULL; -} - -int grpc_sockaddr_get_family(const grpc_resolved_address *resolved_addr) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - return addr->sa_family; -} - -int grpc_sockaddr_get_port(const grpc_resolved_address *resolved_addr) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - switch (addr->sa_family) { - case AF_INET: - return ntohs(((struct sockaddr_in *)addr)->sin_port); - case AF_INET6: - return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); - default: - if (grpc_is_unix_socket(resolved_addr)) { - return 1; - } - gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port", - addr->sa_family); - return 0; - } -} - -int grpc_sockaddr_set_port(const grpc_resolved_address *resolved_addr, - int port) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - switch (addr->sa_family) { - case AF_INET: - GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in *)addr)->sin_port = htons((uint16_t)port); - return 1; - case AF_INET6: - GPR_ASSERT(port >= 0 && port < 65536); - ((struct sockaddr_in6 *)addr)->sin6_port = htons((uint16_t)port); - return 1; - default: - gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", - addr->sa_family); - return 0; - } -} diff --git a/src/core/lib/iomgr/sockaddr_utils.cc b/src/core/lib/iomgr/sockaddr_utils.cc new file mode 100644 index 0000000000..3f4145d104 --- /dev/null +++ b/src/core/lib/iomgr/sockaddr_utils.cc @@ -0,0 +1,262 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/sockaddr_utils.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/support/string.h" + +static const uint8_t kV4MappedPrefix[] = {0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0xff, 0xff}; + +int grpc_sockaddr_is_v4mapped(const grpc_resolved_address *resolved_addr, + grpc_resolved_address *resolved_addr4_out) { + GPR_ASSERT(resolved_addr != resolved_addr4_out); + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + struct sockaddr_in *addr4_out = + resolved_addr4_out == NULL + ? NULL + : (struct sockaddr_in *)resolved_addr4_out->addr; + if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + if (memcmp(addr6->sin6_addr.s6_addr, kV4MappedPrefix, + sizeof(kV4MappedPrefix)) == 0) { + if (resolved_addr4_out != NULL) { + /* Normalize ::ffff:0.0.0.0/96 to IPv4. */ + memset(resolved_addr4_out, 0, sizeof(*resolved_addr4_out)); + addr4_out->sin_family = AF_INET; + /* s6_addr32 would be nice, but it's non-standard. */ + memcpy(&addr4_out->sin_addr, &addr6->sin6_addr.s6_addr[12], 4); + addr4_out->sin_port = addr6->sin6_port; + resolved_addr4_out->len = sizeof(struct sockaddr_in); + } + return 1; + } + } + return 0; +} + +int grpc_sockaddr_to_v4mapped(const grpc_resolved_address *resolved_addr, + grpc_resolved_address *resolved_addr6_out) { + GPR_ASSERT(resolved_addr != resolved_addr6_out); + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + struct sockaddr_in6 *addr6_out = + (struct sockaddr_in6 *)resolved_addr6_out->addr; + if (addr->sa_family == AF_INET) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + memset(resolved_addr6_out, 0, sizeof(*resolved_addr6_out)); + addr6_out->sin6_family = AF_INET6; + memcpy(&addr6_out->sin6_addr.s6_addr[0], kV4MappedPrefix, 12); + memcpy(&addr6_out->sin6_addr.s6_addr[12], &addr4->sin_addr, 4); + addr6_out->sin6_port = addr4->sin_port; + resolved_addr6_out->len = sizeof(struct sockaddr_in6); + return 1; + } + return 0; +} + +int grpc_sockaddr_is_wildcard(const grpc_resolved_address *resolved_addr, + int *port_out) { + const struct sockaddr *addr; + grpc_resolved_address addr4_normalized; + if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr4_normalized)) { + resolved_addr = &addr4_normalized; + } + addr = (const struct sockaddr *)resolved_addr->addr; + if (addr->sa_family == AF_INET) { + /* Check for 0.0.0.0 */ + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + if (addr4->sin_addr.s_addr != 0) { + return 0; + } + *port_out = ntohs(addr4->sin_port); + return 1; + } else if (addr->sa_family == AF_INET6) { + /* Check for :: */ + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + int i; + for (i = 0; i < 16; i++) { + if (addr6->sin6_addr.s6_addr[i] != 0) { + return 0; + } + } + *port_out = ntohs(addr6->sin6_port); + return 1; + } else { + return 0; + } +} + +void grpc_sockaddr_make_wildcards(int port, grpc_resolved_address *wild4_out, + grpc_resolved_address *wild6_out) { + grpc_sockaddr_make_wildcard4(port, wild4_out); + grpc_sockaddr_make_wildcard6(port, wild6_out); +} + +void grpc_sockaddr_make_wildcard4(int port, + grpc_resolved_address *resolved_wild_out) { + struct sockaddr_in *wild_out = (struct sockaddr_in *)resolved_wild_out->addr; + GPR_ASSERT(port >= 0 && port < 65536); + memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); + wild_out->sin_family = AF_INET; + wild_out->sin_port = htons((uint16_t)port); + resolved_wild_out->len = sizeof(struct sockaddr_in); +} + +void grpc_sockaddr_make_wildcard6(int port, + grpc_resolved_address *resolved_wild_out) { + struct sockaddr_in6 *wild_out = + (struct sockaddr_in6 *)resolved_wild_out->addr; + GPR_ASSERT(port >= 0 && port < 65536); + memset(resolved_wild_out, 0, sizeof(*resolved_wild_out)); + wild_out->sin6_family = AF_INET6; + wild_out->sin6_port = htons((uint16_t)port); + resolved_wild_out->len = sizeof(struct sockaddr_in6); +} + +int grpc_sockaddr_to_string(char **out, + const grpc_resolved_address *resolved_addr, + int normalize) { + const struct sockaddr *addr; + const int save_errno = errno; + grpc_resolved_address addr_normalized; + char ntop_buf[INET6_ADDRSTRLEN]; + const void *ip = NULL; + int port; + uint32_t sin6_scope_id = 0; + int ret; + + *out = NULL; + if (normalize && grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { + resolved_addr = &addr_normalized; + } + addr = (const struct sockaddr *)resolved_addr->addr; + if (addr->sa_family == AF_INET) { + const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; + ip = &addr4->sin_addr; + port = ntohs(addr4->sin_port); + } else if (addr->sa_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; + ip = &addr6->sin6_addr; + port = ntohs(addr6->sin6_port); + sin6_scope_id = addr6->sin6_scope_id; + } + if (ip != NULL && + grpc_inet_ntop(addr->sa_family, ip, ntop_buf, sizeof(ntop_buf)) != NULL) { + if (sin6_scope_id != 0) { + char *host_with_scope; + /* Enclose sin6_scope_id with the format defined in RFC 6784 section 2. */ + gpr_asprintf(&host_with_scope, "%s%%25%" PRIu32, ntop_buf, sin6_scope_id); + ret = gpr_join_host_port(out, host_with_scope, port); + gpr_free(host_with_scope); + } else { + ret = gpr_join_host_port(out, ntop_buf, port); + } + } else { + ret = gpr_asprintf(out, "(sockaddr family=%d)", addr->sa_family); + } + /* This is probably redundant, but we wouldn't want to log the wrong error. */ + errno = save_errno; + return ret; +} + +char *grpc_sockaddr_to_uri(const grpc_resolved_address *resolved_addr) { + grpc_resolved_address addr_normalized; + if (grpc_sockaddr_is_v4mapped(resolved_addr, &addr_normalized)) { + resolved_addr = &addr_normalized; + } + const char *scheme = grpc_sockaddr_get_uri_scheme(resolved_addr); + if (scheme == NULL || strcmp("unix", scheme) == 0) { + return grpc_sockaddr_to_uri_unix_if_possible(resolved_addr); + } + char *path = NULL; + char *uri_str = NULL; + if (grpc_sockaddr_to_string(&path, resolved_addr, + false /* suppress errors */) && + scheme != NULL) { + gpr_asprintf(&uri_str, "%s:%s", scheme, path); + } + gpr_free(path); + return uri_str != NULL ? uri_str : NULL; +} + +const char *grpc_sockaddr_get_uri_scheme( + const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + switch (addr->sa_family) { + case AF_INET: + return "ipv4"; + case AF_INET6: + return "ipv6"; + case AF_UNIX: + return "unix"; + } + return NULL; +} + +int grpc_sockaddr_get_family(const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + return addr->sa_family; +} + +int grpc_sockaddr_get_port(const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + switch (addr->sa_family) { + case AF_INET: + return ntohs(((struct sockaddr_in *)addr)->sin_port); + case AF_INET6: + return ntohs(((struct sockaddr_in6 *)addr)->sin6_port); + default: + if (grpc_is_unix_socket(resolved_addr)) { + return 1; + } + gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_get_port", + addr->sa_family); + return 0; + } +} + +int grpc_sockaddr_set_port(const grpc_resolved_address *resolved_addr, + int port) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + switch (addr->sa_family) { + case AF_INET: + GPR_ASSERT(port >= 0 && port < 65536); + ((struct sockaddr_in *)addr)->sin_port = htons((uint16_t)port); + return 1; + case AF_INET6: + GPR_ASSERT(port >= 0 && port < 65536); + ((struct sockaddr_in6 *)addr)->sin6_port = htons((uint16_t)port); + return 1; + default: + gpr_log(GPR_ERROR, "Unknown socket family %d in grpc_sockaddr_set_port", + addr->sa_family); + return 0; + } +} diff --git a/src/core/lib/iomgr/socket_factory_posix.c b/src/core/lib/iomgr/socket_factory_posix.c deleted file mode 100644 index 8e907703ae..0000000000 --- a/src/core/lib/iomgr/socket_factory_posix.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/socket_factory_posix.h" - -#include -#include -#include - -void grpc_socket_factory_init(grpc_socket_factory *factory, - const grpc_socket_factory_vtable *vtable) { - factory->vtable = vtable; - gpr_ref_init(&factory->refcount, 1); -} - -int grpc_socket_factory_socket(grpc_socket_factory *factory, int domain, - int type, int protocol) { - return factory->vtable->socket(factory, domain, type, protocol); -} - -int grpc_socket_factory_bind(grpc_socket_factory *factory, int sockfd, - const grpc_resolved_address *addr) { - return factory->vtable->bind(factory, sockfd, addr); -} - -int grpc_socket_factory_compare(grpc_socket_factory *a, - grpc_socket_factory *b) { - int c = GPR_ICMP(a, b); - if (c != 0) { - grpc_socket_factory *sma = a; - grpc_socket_factory *smb = b; - c = GPR_ICMP(sma->vtable, smb->vtable); - if (c == 0) { - c = sma->vtable->compare(sma, smb); - } - } - return c; -} - -grpc_socket_factory *grpc_socket_factory_ref(grpc_socket_factory *factory) { - gpr_ref(&factory->refcount); - return factory; -} - -void grpc_socket_factory_unref(grpc_socket_factory *factory) { - if (gpr_unref(&factory->refcount)) { - factory->vtable->destroy(factory); - } -} - -static void *socket_factory_arg_copy(void *p) { - return grpc_socket_factory_ref((grpc_socket_factory *)p); -} - -static void socket_factory_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { - grpc_socket_factory_unref((grpc_socket_factory *)p); -} - -static int socket_factory_cmp(void *a, void *b) { - return grpc_socket_factory_compare((grpc_socket_factory *)a, - (grpc_socket_factory *)b); -} - -static const grpc_arg_pointer_vtable socket_factory_arg_vtable = { - socket_factory_arg_copy, socket_factory_arg_destroy, socket_factory_cmp}; - -grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory *factory) { - return grpc_channel_arg_pointer_create((char *)GRPC_ARG_SOCKET_FACTORY, - factory, &socket_factory_arg_vtable); -} - -#endif diff --git a/src/core/lib/iomgr/socket_factory_posix.cc b/src/core/lib/iomgr/socket_factory_posix.cc new file mode 100644 index 0000000000..8e907703ae --- /dev/null +++ b/src/core/lib/iomgr/socket_factory_posix.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/socket_factory_posix.h" + +#include +#include +#include + +void grpc_socket_factory_init(grpc_socket_factory *factory, + const grpc_socket_factory_vtable *vtable) { + factory->vtable = vtable; + gpr_ref_init(&factory->refcount, 1); +} + +int grpc_socket_factory_socket(grpc_socket_factory *factory, int domain, + int type, int protocol) { + return factory->vtable->socket(factory, domain, type, protocol); +} + +int grpc_socket_factory_bind(grpc_socket_factory *factory, int sockfd, + const grpc_resolved_address *addr) { + return factory->vtable->bind(factory, sockfd, addr); +} + +int grpc_socket_factory_compare(grpc_socket_factory *a, + grpc_socket_factory *b) { + int c = GPR_ICMP(a, b); + if (c != 0) { + grpc_socket_factory *sma = a; + grpc_socket_factory *smb = b; + c = GPR_ICMP(sma->vtable, smb->vtable); + if (c == 0) { + c = sma->vtable->compare(sma, smb); + } + } + return c; +} + +grpc_socket_factory *grpc_socket_factory_ref(grpc_socket_factory *factory) { + gpr_ref(&factory->refcount); + return factory; +} + +void grpc_socket_factory_unref(grpc_socket_factory *factory) { + if (gpr_unref(&factory->refcount)) { + factory->vtable->destroy(factory); + } +} + +static void *socket_factory_arg_copy(void *p) { + return grpc_socket_factory_ref((grpc_socket_factory *)p); +} + +static void socket_factory_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { + grpc_socket_factory_unref((grpc_socket_factory *)p); +} + +static int socket_factory_cmp(void *a, void *b) { + return grpc_socket_factory_compare((grpc_socket_factory *)a, + (grpc_socket_factory *)b); +} + +static const grpc_arg_pointer_vtable socket_factory_arg_vtable = { + socket_factory_arg_copy, socket_factory_arg_destroy, socket_factory_cmp}; + +grpc_arg grpc_socket_factory_to_arg(grpc_socket_factory *factory) { + return grpc_channel_arg_pointer_create((char *)GRPC_ARG_SOCKET_FACTORY, + factory, &socket_factory_arg_vtable); +} + +#endif diff --git a/src/core/lib/iomgr/socket_mutator.c b/src/core/lib/iomgr/socket_mutator.c deleted file mode 100644 index b0435d5a07..0000000000 --- a/src/core/lib/iomgr/socket_mutator.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/socket_mutator.h" - -#include "src/core/lib/channel/channel_args.h" - -#include -#include -#include - -void grpc_socket_mutator_init(grpc_socket_mutator *mutator, - const grpc_socket_mutator_vtable *vtable) { - mutator->vtable = vtable; - gpr_ref_init(&mutator->refcount, 1); -} - -grpc_socket_mutator *grpc_socket_mutator_ref(grpc_socket_mutator *mutator) { - gpr_ref(&mutator->refcount); - return mutator; -} - -bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator *mutator, int fd) { - return mutator->vtable->mutate_fd(fd, mutator); -} - -int grpc_socket_mutator_compare(grpc_socket_mutator *a, - grpc_socket_mutator *b) { - int c = GPR_ICMP(a, b); - if (c != 0) { - grpc_socket_mutator *sma = a; - grpc_socket_mutator *smb = b; - c = GPR_ICMP(sma->vtable, smb->vtable); - if (c == 0) { - c = sma->vtable->compare(sma, smb); - } - } - return c; -} - -void grpc_socket_mutator_unref(grpc_socket_mutator *mutator) { - if (gpr_unref(&mutator->refcount)) { - mutator->vtable->destory(mutator); - } -} - -static void *socket_mutator_arg_copy(void *p) { - return grpc_socket_mutator_ref((grpc_socket_mutator *)p); -} - -static void socket_mutator_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { - grpc_socket_mutator_unref((grpc_socket_mutator *)p); -} - -static int socket_mutator_cmp(void *a, void *b) { - return grpc_socket_mutator_compare((grpc_socket_mutator *)a, - (grpc_socket_mutator *)b); -} - -static const grpc_arg_pointer_vtable socket_mutator_arg_vtable = { - socket_mutator_arg_copy, socket_mutator_arg_destroy, socket_mutator_cmp}; - -grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator *mutator) { - return grpc_channel_arg_pointer_create((char *)GRPC_ARG_SOCKET_MUTATOR, - mutator, &socket_mutator_arg_vtable); -} diff --git a/src/core/lib/iomgr/socket_mutator.cc b/src/core/lib/iomgr/socket_mutator.cc new file mode 100644 index 0000000000..b0435d5a07 --- /dev/null +++ b/src/core/lib/iomgr/socket_mutator.cc @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/socket_mutator.h" + +#include "src/core/lib/channel/channel_args.h" + +#include +#include +#include + +void grpc_socket_mutator_init(grpc_socket_mutator *mutator, + const grpc_socket_mutator_vtable *vtable) { + mutator->vtable = vtable; + gpr_ref_init(&mutator->refcount, 1); +} + +grpc_socket_mutator *grpc_socket_mutator_ref(grpc_socket_mutator *mutator) { + gpr_ref(&mutator->refcount); + return mutator; +} + +bool grpc_socket_mutator_mutate_fd(grpc_socket_mutator *mutator, int fd) { + return mutator->vtable->mutate_fd(fd, mutator); +} + +int grpc_socket_mutator_compare(grpc_socket_mutator *a, + grpc_socket_mutator *b) { + int c = GPR_ICMP(a, b); + if (c != 0) { + grpc_socket_mutator *sma = a; + grpc_socket_mutator *smb = b; + c = GPR_ICMP(sma->vtable, smb->vtable); + if (c == 0) { + c = sma->vtable->compare(sma, smb); + } + } + return c; +} + +void grpc_socket_mutator_unref(grpc_socket_mutator *mutator) { + if (gpr_unref(&mutator->refcount)) { + mutator->vtable->destory(mutator); + } +} + +static void *socket_mutator_arg_copy(void *p) { + return grpc_socket_mutator_ref((grpc_socket_mutator *)p); +} + +static void socket_mutator_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { + grpc_socket_mutator_unref((grpc_socket_mutator *)p); +} + +static int socket_mutator_cmp(void *a, void *b) { + return grpc_socket_mutator_compare((grpc_socket_mutator *)a, + (grpc_socket_mutator *)b); +} + +static const grpc_arg_pointer_vtable socket_mutator_arg_vtable = { + socket_mutator_arg_copy, socket_mutator_arg_destroy, socket_mutator_cmp}; + +grpc_arg grpc_socket_mutator_to_arg(grpc_socket_mutator *mutator) { + return grpc_channel_arg_pointer_create((char *)GRPC_ARG_SOCKET_MUTATOR, + mutator, &socket_mutator_arg_vtable); +} diff --git a/src/core/lib/iomgr/socket_utils_common_posix.c b/src/core/lib/iomgr/socket_utils_common_posix.c deleted file mode 100644 index b8e2a0cdfd..0000000000 --- a/src/core/lib/iomgr/socket_utils_common_posix.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/socket_utils.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/support/string.h" - -/* set a socket to non blocking mode */ -grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking) { - int oldflags = fcntl(fd, F_GETFL, 0); - if (oldflags < 0) { - return GRPC_OS_ERROR(errno, "fcntl"); - } - - if (non_blocking) { - oldflags |= O_NONBLOCK; - } else { - oldflags &= ~O_NONBLOCK; - } - - if (fcntl(fd, F_SETFL, oldflags) != 0) { - return GRPC_OS_ERROR(errno, "fcntl"); - } - - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd) { -#ifdef GRPC_HAVE_SO_NOSIGPIPE - int val = 1; - int newval; - socklen_t intlen = sizeof(newval); - if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) { - return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)"); - } - if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) { - return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)"); - } - if ((newval != 0) != (val != 0)) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_NOSIGPIPE"); - } -#endif - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd) { -#ifdef GRPC_HAVE_IP_PKTINFO - int get_local_ip = 1; - if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, - sizeof(get_local_ip))) { - return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)"); - } -#endif - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) { -#ifdef GRPC_HAVE_IPV6_RECVPKTINFO - int get_local_ip = 1; - if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, - sizeof(get_local_ip))) { - return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)"); - } -#endif - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) { - return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes, - sizeof(buffer_size_bytes)) - ? GRPC_ERROR_NONE - : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)"); -} - -grpc_error *grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) { - return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes, - sizeof(buffer_size_bytes)) - ? GRPC_ERROR_NONE - : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)"); -} - -/* set a socket to close on exec */ -grpc_error *grpc_set_socket_cloexec(int fd, int close_on_exec) { - int oldflags = fcntl(fd, F_GETFD, 0); - if (oldflags < 0) { - return GRPC_OS_ERROR(errno, "fcntl"); - } - - if (close_on_exec) { - oldflags |= FD_CLOEXEC; - } else { - oldflags &= ~FD_CLOEXEC; - } - - if (fcntl(fd, F_SETFD, oldflags) != 0) { - return GRPC_OS_ERROR(errno, "fcntl"); - } - - return GRPC_ERROR_NONE; -} - -/* set a socket to reuse old addresses */ -grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse) { - int val = (reuse != 0); - int newval; - socklen_t intlen = sizeof(newval); - if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { - return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)"); - } - if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) { - return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)"); - } - if ((newval != 0) != val) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEADDR"); - } - - return GRPC_ERROR_NONE; -} - -/* set a socket to reuse old addresses */ -grpc_error *grpc_set_socket_reuse_port(int fd, int reuse) { -#ifndef SO_REUSEPORT - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "SO_REUSEPORT unavailable on compiling system"); -#else - int val = (reuse != 0); - int newval; - socklen_t intlen = sizeof(newval); - if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) { - return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)"); - } - if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) { - return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)"); - } - if ((newval != 0) != val) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEPORT"); - } - - return GRPC_ERROR_NONE; -#endif -} - -/* disable nagle */ -grpc_error *grpc_set_socket_low_latency(int fd, int low_latency) { - int val = (low_latency != 0); - int newval; - socklen_t intlen = sizeof(newval); - if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) { - return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)"); - } - if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) { - return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)"); - } - if ((newval != 0) != val) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set TCP_NODELAY"); - } - return GRPC_ERROR_NONE; -} - -/* set a socket using a grpc_socket_mutator */ -grpc_error *grpc_set_socket_with_mutator(int fd, grpc_socket_mutator *mutator) { - GPR_ASSERT(mutator); - if (!grpc_socket_mutator_mutate_fd(mutator, fd)) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("grpc_socket_mutator failed."); - } - return GRPC_ERROR_NONE; -} - -static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT; -static int g_ipv6_loopback_available; - -static void probe_ipv6_once(void) { - int fd = socket(AF_INET6, SOCK_STREAM, 0); - g_ipv6_loopback_available = 0; - if (fd < 0) { - gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed."); - } else { - struct sockaddr_in6 addr; - memset(&addr, 0, sizeof(addr)); - addr.sin6_family = AF_INET6; - addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */ - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) { - g_ipv6_loopback_available = 1; - } else { - gpr_log(GPR_INFO, - "Disabling AF_INET6 sockets because ::1 is not available."); - } - close(fd); - } -} - -int grpc_ipv6_loopback_available(void) { - gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once); - return g_ipv6_loopback_available; -} - -/* This should be 0 in production, but it may be enabled for testing or - debugging purposes, to simulate an environment where IPv6 sockets can't - also speak IPv4. */ -int grpc_forbid_dualstack_sockets_for_testing = 0; - -static int set_socket_dualstack(int fd) { - if (!grpc_forbid_dualstack_sockets_for_testing) { - const int off = 0; - return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); - } else { - /* Force an IPv6-only socket, for testing purposes. */ - const int on = 1; - setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - return 0; - } -} - -static grpc_error *error_for_fd(int fd, const grpc_resolved_address *addr) { - if (fd >= 0) return GRPC_ERROR_NONE; - char *addr_str; - grpc_sockaddr_to_string(&addr_str, addr, 0); - grpc_error *err = grpc_error_set_str(GRPC_OS_ERROR(errno, "socket"), - GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(addr_str)); - gpr_free(addr_str); - return err; -} - -grpc_error *grpc_create_dualstack_socket( - const grpc_resolved_address *resolved_addr, int type, int protocol, - grpc_dualstack_mode *dsmode, int *newfd) { - return grpc_create_dualstack_socket_using_factory(NULL, resolved_addr, type, - protocol, dsmode, newfd); -} - -static int create_socket(grpc_socket_factory *factory, int domain, int type, - int protocol) { - return (factory != NULL) - ? grpc_socket_factory_socket(factory, domain, type, protocol) - : socket(domain, type, protocol); -} - -grpc_error *grpc_create_dualstack_socket_using_factory( - grpc_socket_factory *factory, const grpc_resolved_address *resolved_addr, - int type, int protocol, grpc_dualstack_mode *dsmode, int *newfd) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - int family = addr->sa_family; - if (family == AF_INET6) { - if (grpc_ipv6_loopback_available()) { - *newfd = create_socket(factory, family, type, protocol); - } else { - *newfd = -1; - errno = EAFNOSUPPORT; - } - /* Check if we've got a valid dualstack socket. */ - if (*newfd >= 0 && set_socket_dualstack(*newfd)) { - *dsmode = GRPC_DSMODE_DUALSTACK; - return GRPC_ERROR_NONE; - } - /* If this isn't an IPv4 address, then return whatever we've got. */ - if (!grpc_sockaddr_is_v4mapped(resolved_addr, NULL)) { - *dsmode = GRPC_DSMODE_IPV6; - return error_for_fd(*newfd, resolved_addr); - } - /* Fall back to AF_INET. */ - if (*newfd >= 0) { - close(*newfd); - } - family = AF_INET; - } - *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE; - *newfd = create_socket(factory, family, type, protocol); - return error_for_fd(*newfd, resolved_addr); -} - -const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { - GPR_ASSERT(size <= (socklen_t)-1); - return inet_ntop(af, src, dst, (socklen_t)size); -} - -#endif diff --git a/src/core/lib/iomgr/socket_utils_common_posix.cc b/src/core/lib/iomgr/socket_utils_common_posix.cc new file mode 100644 index 0000000000..b8e2a0cdfd --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_common_posix.cc @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/socket_utils.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/support/string.h" + +/* set a socket to non blocking mode */ +grpc_error *grpc_set_socket_nonblocking(int fd, int non_blocking) { + int oldflags = fcntl(fd, F_GETFL, 0); + if (oldflags < 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + if (non_blocking) { + oldflags |= O_NONBLOCK; + } else { + oldflags &= ~O_NONBLOCK; + } + + if (fcntl(fd, F_SETFL, oldflags) != 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_set_socket_no_sigpipe_if_possible(int fd) { +#ifdef GRPC_HAVE_SO_NOSIGPIPE + int val = 1; + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(SO_NOSIGPIPE)"); + } + if (0 != getsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(SO_NOSIGPIPE)"); + } + if ((newval != 0) != (val != 0)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_NOSIGPIPE"); + } +#endif + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_set_socket_ip_pktinfo_if_possible(int fd) { +#ifdef GRPC_HAVE_IP_PKTINFO + int get_local_ip = 1; + if (0 != setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &get_local_ip, + sizeof(get_local_ip))) { + return GRPC_OS_ERROR(errno, "setsockopt(IP_PKTINFO)"); + } +#endif + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_set_socket_ipv6_recvpktinfo_if_possible(int fd) { +#ifdef GRPC_HAVE_IPV6_RECVPKTINFO + int get_local_ip = 1; + if (0 != setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &get_local_ip, + sizeof(get_local_ip))) { + return GRPC_OS_ERROR(errno, "setsockopt(IPV6_RECVPKTINFO)"); + } +#endif + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_set_socket_sndbuf(int fd, int buffer_size_bytes) { + return 0 == setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffer_size_bytes, + sizeof(buffer_size_bytes)) + ? GRPC_ERROR_NONE + : GRPC_OS_ERROR(errno, "setsockopt(SO_SNDBUF)"); +} + +grpc_error *grpc_set_socket_rcvbuf(int fd, int buffer_size_bytes) { + return 0 == setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffer_size_bytes, + sizeof(buffer_size_bytes)) + ? GRPC_ERROR_NONE + : GRPC_OS_ERROR(errno, "setsockopt(SO_RCVBUF)"); +} + +/* set a socket to close on exec */ +grpc_error *grpc_set_socket_cloexec(int fd, int close_on_exec) { + int oldflags = fcntl(fd, F_GETFD, 0); + if (oldflags < 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + if (close_on_exec) { + oldflags |= FD_CLOEXEC; + } else { + oldflags &= ~FD_CLOEXEC; + } + + if (fcntl(fd, F_SETFD, oldflags) != 0) { + return GRPC_OS_ERROR(errno, "fcntl"); + } + + return GRPC_ERROR_NONE; +} + +/* set a socket to reuse old addresses */ +grpc_error *grpc_set_socket_reuse_addr(int fd, int reuse) { + int val = (reuse != 0); + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEADDR)"); + } + if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEADDR)"); + } + if ((newval != 0) != val) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEADDR"); + } + + return GRPC_ERROR_NONE; +} + +/* set a socket to reuse old addresses */ +grpc_error *grpc_set_socket_reuse_port(int fd, int reuse) { +#ifndef SO_REUSEPORT + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "SO_REUSEPORT unavailable on compiling system"); +#else + int val = (reuse != 0); + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(SO_REUSEPORT)"); + } + if (0 != getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(SO_REUSEPORT)"); + } + if ((newval != 0) != val) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set SO_REUSEPORT"); + } + + return GRPC_ERROR_NONE; +#endif +} + +/* disable nagle */ +grpc_error *grpc_set_socket_low_latency(int fd, int low_latency) { + int val = (low_latency != 0); + int newval; + socklen_t intlen = sizeof(newval); + if (0 != setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))) { + return GRPC_OS_ERROR(errno, "setsockopt(TCP_NODELAY)"); + } + if (0 != getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &newval, &intlen)) { + return GRPC_OS_ERROR(errno, "getsockopt(TCP_NODELAY)"); + } + if ((newval != 0) != val) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set TCP_NODELAY"); + } + return GRPC_ERROR_NONE; +} + +/* set a socket using a grpc_socket_mutator */ +grpc_error *grpc_set_socket_with_mutator(int fd, grpc_socket_mutator *mutator) { + GPR_ASSERT(mutator); + if (!grpc_socket_mutator_mutate_fd(mutator, fd)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("grpc_socket_mutator failed."); + } + return GRPC_ERROR_NONE; +} + +static gpr_once g_probe_ipv6_once = GPR_ONCE_INIT; +static int g_ipv6_loopback_available; + +static void probe_ipv6_once(void) { + int fd = socket(AF_INET6, SOCK_STREAM, 0); + g_ipv6_loopback_available = 0; + if (fd < 0) { + gpr_log(GPR_INFO, "Disabling AF_INET6 sockets because socket() failed."); + } else { + struct sockaddr_in6 addr; + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + addr.sin6_addr.s6_addr[15] = 1; /* [::1]:0 */ + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + g_ipv6_loopback_available = 1; + } else { + gpr_log(GPR_INFO, + "Disabling AF_INET6 sockets because ::1 is not available."); + } + close(fd); + } +} + +int grpc_ipv6_loopback_available(void) { + gpr_once_init(&g_probe_ipv6_once, probe_ipv6_once); + return g_ipv6_loopback_available; +} + +/* This should be 0 in production, but it may be enabled for testing or + debugging purposes, to simulate an environment where IPv6 sockets can't + also speak IPv4. */ +int grpc_forbid_dualstack_sockets_for_testing = 0; + +static int set_socket_dualstack(int fd) { + if (!grpc_forbid_dualstack_sockets_for_testing) { + const int off = 0; + return 0 == setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)); + } else { + /* Force an IPv6-only socket, for testing purposes. */ + const int on = 1; + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + return 0; + } +} + +static grpc_error *error_for_fd(int fd, const grpc_resolved_address *addr) { + if (fd >= 0) return GRPC_ERROR_NONE; + char *addr_str; + grpc_sockaddr_to_string(&addr_str, addr, 0); + grpc_error *err = grpc_error_set_str(GRPC_OS_ERROR(errno, "socket"), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(addr_str)); + gpr_free(addr_str); + return err; +} + +grpc_error *grpc_create_dualstack_socket( + const grpc_resolved_address *resolved_addr, int type, int protocol, + grpc_dualstack_mode *dsmode, int *newfd) { + return grpc_create_dualstack_socket_using_factory(NULL, resolved_addr, type, + protocol, dsmode, newfd); +} + +static int create_socket(grpc_socket_factory *factory, int domain, int type, + int protocol) { + return (factory != NULL) + ? grpc_socket_factory_socket(factory, domain, type, protocol) + : socket(domain, type, protocol); +} + +grpc_error *grpc_create_dualstack_socket_using_factory( + grpc_socket_factory *factory, const grpc_resolved_address *resolved_addr, + int type, int protocol, grpc_dualstack_mode *dsmode, int *newfd) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + int family = addr->sa_family; + if (family == AF_INET6) { + if (grpc_ipv6_loopback_available()) { + *newfd = create_socket(factory, family, type, protocol); + } else { + *newfd = -1; + errno = EAFNOSUPPORT; + } + /* Check if we've got a valid dualstack socket. */ + if (*newfd >= 0 && set_socket_dualstack(*newfd)) { + *dsmode = GRPC_DSMODE_DUALSTACK; + return GRPC_ERROR_NONE; + } + /* If this isn't an IPv4 address, then return whatever we've got. */ + if (!grpc_sockaddr_is_v4mapped(resolved_addr, NULL)) { + *dsmode = GRPC_DSMODE_IPV6; + return error_for_fd(*newfd, resolved_addr); + } + /* Fall back to AF_INET. */ + if (*newfd >= 0) { + close(*newfd); + } + family = AF_INET; + } + *dsmode = family == AF_INET ? GRPC_DSMODE_IPV4 : GRPC_DSMODE_NONE; + *newfd = create_socket(factory, family, type, protocol); + return error_for_fd(*newfd, resolved_addr); +} + +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { + GPR_ASSERT(size <= (socklen_t)-1); + return inet_ntop(af, src, dst, (socklen_t)size); +} + +#endif diff --git a/src/core/lib/iomgr/socket_utils_linux.c b/src/core/lib/iomgr/socket_utils_linux.c deleted file mode 100644 index e7b094d216..0000000000 --- a/src/core/lib/iomgr/socket_utils_linux.c +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_LINUX_SOCKETUTILS - -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" - -#include - -#include -#include - -int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, - int cloexec) { - int flags = 0; - GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); - GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); - flags |= nonblock ? SOCK_NONBLOCK : 0; - flags |= cloexec ? SOCK_CLOEXEC : 0; - return accept4(sockfd, (struct sockaddr *)resolved_addr->addr, - (socklen_t *)&resolved_addr->len, flags); -} - -#endif diff --git a/src/core/lib/iomgr/socket_utils_linux.cc b/src/core/lib/iomgr/socket_utils_linux.cc new file mode 100644 index 0000000000..e7b094d216 --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_linux.cc @@ -0,0 +1,42 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_LINUX_SOCKETUTILS + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include + +#include +#include + +int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, + int cloexec) { + int flags = 0; + GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); + GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); + flags |= nonblock ? SOCK_NONBLOCK : 0; + flags |= cloexec ? SOCK_CLOEXEC : 0; + return accept4(sockfd, (struct sockaddr *)resolved_addr->addr, + (socklen_t *)&resolved_addr->len, flags); +} + +#endif diff --git a/src/core/lib/iomgr/socket_utils_posix.c b/src/core/lib/iomgr/socket_utils_posix.c deleted file mode 100644 index dfd1ffd1e3..0000000000 --- a/src/core/lib/iomgr/socket_utils_posix.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKETUTILS - -#include "src/core/lib/iomgr/socket_utils_posix.h" - -#include -#include -#include - -#include -#include "src/core/lib/iomgr/sockaddr.h" - -int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, - int cloexec) { - int fd, flags; - GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); - GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); - fd = accept(sockfd, (struct sockaddr *)resolved_addr->addr, - (socklen_t *)&resolved_addr->len); - if (fd >= 0) { - if (nonblock) { - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) goto close_and_error; - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) goto close_and_error; - } - if (cloexec) { - flags = fcntl(fd, F_GETFD, 0); - if (flags < 0) goto close_and_error; - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) goto close_and_error; - } - } - return fd; - -close_and_error: - close(fd); - return -1; -} - -#endif /* GRPC_POSIX_SOCKETUTILS */ diff --git a/src/core/lib/iomgr/socket_utils_posix.cc b/src/core/lib/iomgr/socket_utils_posix.cc new file mode 100644 index 0000000000..dfd1ffd1e3 --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_posix.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKETUTILS + +#include "src/core/lib/iomgr/socket_utils_posix.h" + +#include +#include +#include + +#include +#include "src/core/lib/iomgr/sockaddr.h" + +int grpc_accept4(int sockfd, grpc_resolved_address *resolved_addr, int nonblock, + int cloexec) { + int fd, flags; + GPR_ASSERT(sizeof(socklen_t) <= sizeof(size_t)); + GPR_ASSERT(resolved_addr->len <= (socklen_t)-1); + fd = accept(sockfd, (struct sockaddr *)resolved_addr->addr, + (socklen_t *)&resolved_addr->len); + if (fd >= 0) { + if (nonblock) { + flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) goto close_and_error; + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) goto close_and_error; + } + if (cloexec) { + flags = fcntl(fd, F_GETFD, 0); + if (flags < 0) goto close_and_error; + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != 0) goto close_and_error; + } + } + return fd; + +close_and_error: + close(fd); + return -1; +} + +#endif /* GRPC_POSIX_SOCKETUTILS */ diff --git a/src/core/lib/iomgr/socket_utils_uv.c b/src/core/lib/iomgr/socket_utils_uv.c deleted file mode 100644 index 0f7de4dfad..0000000000 --- a/src/core/lib/iomgr/socket_utils_uv.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include - -#include "src/core/lib/iomgr/socket_utils.h" - -#include - -const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { - uv_inet_ntop(af, src, dst, size); - return dst; -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/socket_utils_uv.cc b/src/core/lib/iomgr/socket_utils_uv.cc new file mode 100644 index 0000000000..0f7de4dfad --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_uv.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include + +#include "src/core/lib/iomgr/socket_utils.h" + +#include + +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { + uv_inet_ntop(af, src, dst, size); + return dst; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/socket_utils_windows.c b/src/core/lib/iomgr/socket_utils_windows.c deleted file mode 100644 index 6e85e4b61f..0000000000 --- a/src/core/lib/iomgr/socket_utils_windows.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINDOWS_SOCKETUTILS - -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/socket_utils.h" - -#include - -const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { - /* Windows InetNtopA wants a mutable ip pointer */ - return InetNtopA(af, (void *)src, dst, size); -} - -#endif /* GRPC_WINDOWS_SOCKETUTILS */ diff --git a/src/core/lib/iomgr/socket_utils_windows.cc b/src/core/lib/iomgr/socket_utils_windows.cc new file mode 100644 index 0000000000..6e85e4b61f --- /dev/null +++ b/src/core/lib/iomgr/socket_utils_windows.cc @@ -0,0 +1,33 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINDOWS_SOCKETUTILS + +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/socket_utils.h" + +#include + +const char *grpc_inet_ntop(int af, const void *src, char *dst, size_t size) { + /* Windows InetNtopA wants a mutable ip pointer */ + return InetNtopA(af, (void *)src, dst, size); +} + +#endif /* GRPC_WINDOWS_SOCKETUTILS */ diff --git a/src/core/lib/iomgr/socket_windows.c b/src/core/lib/iomgr/socket_windows.c deleted file mode 100644 index a0d731b942..0000000000 --- a/src/core/lib/iomgr/socket_windows.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include - -// must be included after winsock2.h -#include - -#include -#include -#include -#include - -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/iomgr/pollset.h" -#include "src/core/lib/iomgr/pollset_windows.h" -#include "src/core/lib/iomgr/socket_windows.h" - -grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) { - char *final_name; - grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket)); - memset(r, 0, sizeof(grpc_winsocket)); - r->socket = socket; - gpr_mu_init(&r->state_mu); - gpr_asprintf(&final_name, "%s:socket=0x%p", name, r); - grpc_iomgr_register_object(&r->iomgr_object, final_name); - gpr_free(final_name); - grpc_iocp_add_socket(r); - return r; -} - -/* Schedule a shutdown of the socket operations. Will call the pending - operations to abort them. We need to do that this way because of the - various callsites of that function, which happens to be in various - mutex hold states, and that'd be unsafe to call them directly. */ -void grpc_winsocket_shutdown(grpc_winsocket *winsocket) { - /* Grab the function pointer for DisconnectEx for that specific socket. - It may change depending on the interface. */ - int status; - GUID guid = WSAID_DISCONNECTEX; - LPFN_DISCONNECTEX DisconnectEx; - DWORD ioctl_num_bytes; - - gpr_mu_lock(&winsocket->state_mu); - if (winsocket->shutdown_called) { - gpr_mu_unlock(&winsocket->state_mu); - return; - } - winsocket->shutdown_called = true; - gpr_mu_unlock(&winsocket->state_mu); - - status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER, - &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx), - &ioctl_num_bytes, NULL, NULL); - - if (status == 0) { - DisconnectEx(winsocket->socket, NULL, 0, 0); - } else { - char *utf8_message = gpr_format_message(WSAGetLastError()); - gpr_log(GPR_INFO, "Unable to retrieve DisconnectEx pointer : %s", - utf8_message); - gpr_free(utf8_message); - } - closesocket(winsocket->socket); -} - -static void destroy(grpc_winsocket *winsocket) { - grpc_iomgr_unregister_object(&winsocket->iomgr_object); - gpr_mu_destroy(&winsocket->state_mu); - gpr_free(winsocket); -} - -static bool check_destroyable(grpc_winsocket *winsocket) { - return winsocket->destroy_called == true && - winsocket->write_info.closure == NULL && - winsocket->read_info.closure == NULL; -} - -void grpc_winsocket_destroy(grpc_winsocket *winsocket) { - gpr_mu_lock(&winsocket->state_mu); - GPR_ASSERT(!winsocket->destroy_called); - winsocket->destroy_called = true; - bool should_destroy = check_destroyable(winsocket); - gpr_mu_unlock(&winsocket->state_mu); - if (should_destroy) destroy(winsocket); -} - -/* Calling notify_on_read or write means either of two things: --) The IOCP already completed in the background, and we need to call -the callback now. --) The IOCP hasn't completed yet, and we're queuing it for later. */ -static void socket_notify_on_iocp(grpc_exec_ctx *exec_ctx, - grpc_winsocket *socket, grpc_closure *closure, - grpc_winsocket_callback_info *info) { - GPR_ASSERT(info->closure == NULL); - gpr_mu_lock(&socket->state_mu); - if (info->has_pending_iocp) { - info->has_pending_iocp = 0; - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); - } else { - info->closure = closure; - } - gpr_mu_unlock(&socket->state_mu); -} - -void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx, - grpc_winsocket *socket, - grpc_closure *closure) { - socket_notify_on_iocp(exec_ctx, socket, closure, &socket->write_info); -} - -void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, - grpc_closure *closure) { - socket_notify_on_iocp(exec_ctx, socket, closure, &socket->read_info); -} - -void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, - grpc_winsocket_callback_info *info) { - GPR_ASSERT(!info->has_pending_iocp); - gpr_mu_lock(&socket->state_mu); - if (info->closure) { - GRPC_CLOSURE_SCHED(exec_ctx, info->closure, GRPC_ERROR_NONE); - info->closure = NULL; - } else { - info->has_pending_iocp = 1; - } - bool should_destroy = check_destroyable(socket); - gpr_mu_unlock(&socket->state_mu); - if (should_destroy) destroy(socket); -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/socket_windows.cc b/src/core/lib/iomgr/socket_windows.cc new file mode 100644 index 0000000000..a0d731b942 --- /dev/null +++ b/src/core/lib/iomgr/socket_windows.cc @@ -0,0 +1,152 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include + +// must be included after winsock2.h +#include + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/socket_windows.h" + +grpc_winsocket *grpc_winsocket_create(SOCKET socket, const char *name) { + char *final_name; + grpc_winsocket *r = gpr_malloc(sizeof(grpc_winsocket)); + memset(r, 0, sizeof(grpc_winsocket)); + r->socket = socket; + gpr_mu_init(&r->state_mu); + gpr_asprintf(&final_name, "%s:socket=0x%p", name, r); + grpc_iomgr_register_object(&r->iomgr_object, final_name); + gpr_free(final_name); + grpc_iocp_add_socket(r); + return r; +} + +/* Schedule a shutdown of the socket operations. Will call the pending + operations to abort them. We need to do that this way because of the + various callsites of that function, which happens to be in various + mutex hold states, and that'd be unsafe to call them directly. */ +void grpc_winsocket_shutdown(grpc_winsocket *winsocket) { + /* Grab the function pointer for DisconnectEx for that specific socket. + It may change depending on the interface. */ + int status; + GUID guid = WSAID_DISCONNECTEX; + LPFN_DISCONNECTEX DisconnectEx; + DWORD ioctl_num_bytes; + + gpr_mu_lock(&winsocket->state_mu); + if (winsocket->shutdown_called) { + gpr_mu_unlock(&winsocket->state_mu); + return; + } + winsocket->shutdown_called = true; + gpr_mu_unlock(&winsocket->state_mu); + + status = WSAIoctl(winsocket->socket, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guid, sizeof(guid), &DisconnectEx, sizeof(DisconnectEx), + &ioctl_num_bytes, NULL, NULL); + + if (status == 0) { + DisconnectEx(winsocket->socket, NULL, 0, 0); + } else { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_INFO, "Unable to retrieve DisconnectEx pointer : %s", + utf8_message); + gpr_free(utf8_message); + } + closesocket(winsocket->socket); +} + +static void destroy(grpc_winsocket *winsocket) { + grpc_iomgr_unregister_object(&winsocket->iomgr_object); + gpr_mu_destroy(&winsocket->state_mu); + gpr_free(winsocket); +} + +static bool check_destroyable(grpc_winsocket *winsocket) { + return winsocket->destroy_called == true && + winsocket->write_info.closure == NULL && + winsocket->read_info.closure == NULL; +} + +void grpc_winsocket_destroy(grpc_winsocket *winsocket) { + gpr_mu_lock(&winsocket->state_mu); + GPR_ASSERT(!winsocket->destroy_called); + winsocket->destroy_called = true; + bool should_destroy = check_destroyable(winsocket); + gpr_mu_unlock(&winsocket->state_mu); + if (should_destroy) destroy(winsocket); +} + +/* Calling notify_on_read or write means either of two things: +-) The IOCP already completed in the background, and we need to call +the callback now. +-) The IOCP hasn't completed yet, and we're queuing it for later. */ +static void socket_notify_on_iocp(grpc_exec_ctx *exec_ctx, + grpc_winsocket *socket, grpc_closure *closure, + grpc_winsocket_callback_info *info) { + GPR_ASSERT(info->closure == NULL); + gpr_mu_lock(&socket->state_mu); + if (info->has_pending_iocp) { + info->has_pending_iocp = 0; + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); + } else { + info->closure = closure; + } + gpr_mu_unlock(&socket->state_mu); +} + +void grpc_socket_notify_on_write(grpc_exec_ctx *exec_ctx, + grpc_winsocket *socket, + grpc_closure *closure) { + socket_notify_on_iocp(exec_ctx, socket, closure, &socket->write_info); +} + +void grpc_socket_notify_on_read(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, + grpc_closure *closure) { + socket_notify_on_iocp(exec_ctx, socket, closure, &socket->read_info); +} + +void grpc_socket_become_ready(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, + grpc_winsocket_callback_info *info) { + GPR_ASSERT(!info->has_pending_iocp); + gpr_mu_lock(&socket->state_mu); + if (info->closure) { + GRPC_CLOSURE_SCHED(exec_ctx, info->closure, GRPC_ERROR_NONE); + info->closure = NULL; + } else { + info->has_pending_iocp = 1; + } + bool should_destroy = check_destroyable(socket); + gpr_mu_unlock(&socket->state_mu); + if (should_destroy) destroy(socket); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_client_posix.c b/src/core/lib/iomgr/tcp_client_posix.c deleted file mode 100644 index 39dbb506e2..0000000000 --- a/src/core/lib/iomgr/tcp_client_posix.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/tcp_client_posix.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/iomgr_posix.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/socket_mutator.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" -#include "src/core/lib/iomgr/tcp_posix.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/support/string.h" - -extern grpc_tracer_flag grpc_tcp_trace; - -typedef struct { - gpr_mu mu; - grpc_fd *fd; - gpr_timespec deadline; - grpc_timer alarm; - grpc_closure on_alarm; - int refs; - grpc_closure write_closure; - grpc_pollset_set *interested_parties; - char *addr_str; - grpc_endpoint **ep; - grpc_closure *closure; - grpc_channel_args *channel_args; -} async_connect; - -static grpc_error *prepare_socket(const grpc_resolved_address *addr, int fd, - const grpc_channel_args *channel_args) { - grpc_error *err = GRPC_ERROR_NONE; - - GPR_ASSERT(fd >= 0); - - err = grpc_set_socket_nonblocking(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - err = grpc_set_socket_cloexec(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - if (!grpc_is_unix_socket(addr)) { - err = grpc_set_socket_low_latency(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - } - err = grpc_set_socket_no_sigpipe_if_possible(fd); - if (err != GRPC_ERROR_NONE) goto error; - if (channel_args) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_SOCKET_MUTATOR)) { - GPR_ASSERT(channel_args->args[i].type == GRPC_ARG_POINTER); - grpc_socket_mutator *mutator = - (grpc_socket_mutator *)channel_args->args[i].value.pointer.p; - err = grpc_set_socket_with_mutator(fd, mutator); - if (err != GRPC_ERROR_NONE) goto error; - } - } - } - goto done; - -error: - if (fd >= 0) { - close(fd); - } -done: - return err; -} - -static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { - int done; - async_connect *ac = (async_connect *)acp; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str, - str); - } - gpr_mu_lock(&ac->mu); - if (ac->fd != NULL) { - grpc_fd_shutdown(exec_ctx, ac->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "connect() timed out")); - } - done = (--ac->refs == 0); - gpr_mu_unlock(&ac->mu); - if (done) { - gpr_mu_destroy(&ac->mu); - gpr_free(ac->addr_str); - grpc_channel_args_destroy(exec_ctx, ac->channel_args); - gpr_free(ac); - } -} - -grpc_endpoint *grpc_tcp_client_create_from_fd( - grpc_exec_ctx *exec_ctx, grpc_fd *fd, const grpc_channel_args *channel_args, - const char *addr_str) { - return grpc_tcp_create(exec_ctx, fd, channel_args, addr_str); -} - -static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { - async_connect *ac = (async_connect *)acp; - int so_error = 0; - socklen_t so_error_size; - int err; - int done; - grpc_endpoint **ep = ac->ep; - grpc_closure *closure = ac->closure; - grpc_fd *fd; - - GRPC_ERROR_REF(error); - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s", - ac->addr_str, str); - } - - gpr_mu_lock(&ac->mu); - GPR_ASSERT(ac->fd); - fd = ac->fd; - ac->fd = NULL; - gpr_mu_unlock(&ac->mu); - - grpc_timer_cancel(exec_ctx, &ac->alarm); - - gpr_mu_lock(&ac->mu); - if (error != GRPC_ERROR_NONE) { - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string("Timeout occurred")); - goto finish; - } - - do { - so_error_size = sizeof(so_error); - err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error, - &so_error_size); - } while (err < 0 && errno == EINTR); - if (err < 0) { - error = GRPC_OS_ERROR(errno, "getsockopt"); - goto finish; - } - - switch (so_error) { - case 0: - grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); - *ep = grpc_tcp_client_create_from_fd(exec_ctx, fd, ac->channel_args, - ac->addr_str); - fd = NULL; - break; - case ENOBUFS: - /* We will get one of these errors if we have run out of - memory in the kernel for the data structures allocated - when you connect a socket. If this happens it is very - likely that if we wait a little bit then try again the - connection will work (since other programs or this - program will close their network connections and free up - memory). This does _not_ indicate that there is anything - wrong with the server we are connecting to, this is a - local problem. - - If you are looking at this code, then chances are that - your program or another program on the same computer - opened too many network connections. The "easy" fix: - don't do that! */ - gpr_log(GPR_ERROR, "kernel out of buffers"); - gpr_mu_unlock(&ac->mu); - grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure); - return; - case ECONNREFUSED: - /* This error shouldn't happen for anything other than connect(). */ - error = GRPC_OS_ERROR(so_error, "connect"); - break; - default: - /* We don't really know which syscall triggered the problem here, - so punt by reporting getsockopt(). */ - error = GRPC_OS_ERROR(so_error, "getsockopt(SO_ERROR)"); - break; - } - -finish: - if (fd != NULL) { - grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); - grpc_fd_orphan(exec_ctx, fd, NULL, NULL, false /* already_closed */, - "tcp_client_orphan"); - fd = NULL; - } - done = (--ac->refs == 0); - gpr_mu_unlock(&ac->mu); - if (error != GRPC_ERROR_NONE) { - char *error_descr; - grpc_slice str; - bool ret = grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, &str); - GPR_ASSERT(ret); - char *desc = grpc_slice_to_c_string(str); - gpr_asprintf(&error_descr, "Failed to connect to remote host: %s", desc); - error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION, - grpc_slice_from_copied_string(error_descr)); - gpr_free(error_descr); - gpr_free(desc); - error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(ac->addr_str)); - } - if (done) { - gpr_mu_destroy(&ac->mu); - gpr_free(ac->addr_str); - grpc_channel_args_destroy(exec_ctx, ac->channel_args); - gpr_free(ac); - } - GRPC_CLOSURE_SCHED(exec_ctx, closure, error); -} - -static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, - grpc_closure *closure, grpc_endpoint **ep, - grpc_pollset_set *interested_parties, - const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) { - int fd; - grpc_dualstack_mode dsmode; - int err; - async_connect *ac; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address addr4_copy; - grpc_fd *fdobj; - char *name; - char *addr_str; - grpc_error *error; - - *ep = NULL; - - /* Use dualstack sockets where available. */ - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - error = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd); - if (error != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, error); - return; - } - if (dsmode == GRPC_DSMODE_IPV4) { - /* If we got an AF_INET socket, map the address back to IPv4. */ - GPR_ASSERT(grpc_sockaddr_is_v4mapped(addr, &addr4_copy)); - addr = &addr4_copy; - } - if ((error = prepare_socket(addr, fd, channel_args)) != GRPC_ERROR_NONE) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, error); - return; - } - - do { - GPR_ASSERT(addr->len < ~(socklen_t)0); - err = - connect(fd, (const struct sockaddr *)addr->addr, (socklen_t)addr->len); - } while (err < 0 && errno == EINTR); - - addr_str = grpc_sockaddr_to_uri(addr); - gpr_asprintf(&name, "tcp-client:%s", addr_str); - - fdobj = grpc_fd_create(fd, name); - - if (err >= 0) { - *ep = - grpc_tcp_client_create_from_fd(exec_ctx, fdobj, channel_args, addr_str); - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); - goto done; - } - - if (errno != EWOULDBLOCK && errno != EINPROGRESS) { - grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, false /* already_closed */, - "tcp_client_connect_error"); - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_OS_ERROR(errno, "connect")); - goto done; - } - - grpc_pollset_set_add_fd(exec_ctx, interested_parties, fdobj); - - ac = (async_connect *)gpr_malloc(sizeof(async_connect)); - ac->closure = closure; - ac->ep = ep; - ac->fd = fdobj; - ac->interested_parties = interested_parties; - ac->addr_str = addr_str; - addr_str = NULL; - gpr_mu_init(&ac->mu); - ac->refs = 2; - GRPC_CLOSURE_INIT(&ac->write_closure, on_writable, ac, - grpc_schedule_on_exec_ctx); - ac->channel_args = grpc_channel_args_copy(channel_args); - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting fd %p", - ac->addr_str, fdobj); - } - - gpr_mu_lock(&ac->mu); - GRPC_CLOSURE_INIT(&ac->on_alarm, tc_on_alarm, ac, grpc_schedule_on_exec_ctx); - grpc_timer_init(exec_ctx, &ac->alarm, - gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), - &ac->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure); - gpr_mu_unlock(&ac->mu); - -done: - gpr_free(name); - gpr_free(addr_str); -} - -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, - grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_endpoint **ep, - grpc_pollset_set *interested_parties, - const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) { - grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, - channel_args, addr, deadline); -} - -#endif diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc new file mode 100644 index 0000000000..39dbb506e2 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client_posix.cc @@ -0,0 +1,356 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/tcp_client_posix.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/iomgr_posix.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_mutator.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/support/string.h" + +extern grpc_tracer_flag grpc_tcp_trace; + +typedef struct { + gpr_mu mu; + grpc_fd *fd; + gpr_timespec deadline; + grpc_timer alarm; + grpc_closure on_alarm; + int refs; + grpc_closure write_closure; + grpc_pollset_set *interested_parties; + char *addr_str; + grpc_endpoint **ep; + grpc_closure *closure; + grpc_channel_args *channel_args; +} async_connect; + +static grpc_error *prepare_socket(const grpc_resolved_address *addr, int fd, + const grpc_channel_args *channel_args) { + grpc_error *err = GRPC_ERROR_NONE; + + GPR_ASSERT(fd >= 0); + + err = grpc_set_socket_nonblocking(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_cloexec(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + if (!grpc_is_unix_socket(addr)) { + err = grpc_set_socket_low_latency(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + } + err = grpc_set_socket_no_sigpipe_if_possible(fd); + if (err != GRPC_ERROR_NONE) goto error; + if (channel_args) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_SOCKET_MUTATOR)) { + GPR_ASSERT(channel_args->args[i].type == GRPC_ARG_POINTER); + grpc_socket_mutator *mutator = + (grpc_socket_mutator *)channel_args->args[i].value.pointer.p; + err = grpc_set_socket_with_mutator(fd, mutator); + if (err != GRPC_ERROR_NONE) goto error; + } + } + } + goto done; + +error: + if (fd >= 0) { + close(fd); + } +done: + return err; +} + +static void tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { + int done; + async_connect *ac = (async_connect *)acp; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", ac->addr_str, + str); + } + gpr_mu_lock(&ac->mu); + if (ac->fd != NULL) { + grpc_fd_shutdown(exec_ctx, ac->fd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "connect() timed out")); + } + done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_str); + grpc_channel_args_destroy(exec_ctx, ac->channel_args); + gpr_free(ac); + } +} + +grpc_endpoint *grpc_tcp_client_create_from_fd( + grpc_exec_ctx *exec_ctx, grpc_fd *fd, const grpc_channel_args *channel_args, + const char *addr_str) { + return grpc_tcp_create(exec_ctx, fd, channel_args, addr_str); +} + +static void on_writable(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { + async_connect *ac = (async_connect *)acp; + int so_error = 0; + socklen_t so_error_size; + int err; + int done; + grpc_endpoint **ep = ac->ep; + grpc_closure *closure = ac->closure; + grpc_fd *fd; + + GRPC_ERROR_REF(error); + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_writable: error=%s", + ac->addr_str, str); + } + + gpr_mu_lock(&ac->mu); + GPR_ASSERT(ac->fd); + fd = ac->fd; + ac->fd = NULL; + gpr_mu_unlock(&ac->mu); + + grpc_timer_cancel(exec_ctx, &ac->alarm); + + gpr_mu_lock(&ac->mu); + if (error != GRPC_ERROR_NONE) { + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string("Timeout occurred")); + goto finish; + } + + do { + so_error_size = sizeof(so_error); + err = getsockopt(grpc_fd_wrapped_fd(fd), SOL_SOCKET, SO_ERROR, &so_error, + &so_error_size); + } while (err < 0 && errno == EINTR); + if (err < 0) { + error = GRPC_OS_ERROR(errno, "getsockopt"); + goto finish; + } + + switch (so_error) { + case 0: + grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); + *ep = grpc_tcp_client_create_from_fd(exec_ctx, fd, ac->channel_args, + ac->addr_str); + fd = NULL; + break; + case ENOBUFS: + /* We will get one of these errors if we have run out of + memory in the kernel for the data structures allocated + when you connect a socket. If this happens it is very + likely that if we wait a little bit then try again the + connection will work (since other programs or this + program will close their network connections and free up + memory). This does _not_ indicate that there is anything + wrong with the server we are connecting to, this is a + local problem. + + If you are looking at this code, then chances are that + your program or another program on the same computer + opened too many network connections. The "easy" fix: + don't do that! */ + gpr_log(GPR_ERROR, "kernel out of buffers"); + gpr_mu_unlock(&ac->mu); + grpc_fd_notify_on_write(exec_ctx, fd, &ac->write_closure); + return; + case ECONNREFUSED: + /* This error shouldn't happen for anything other than connect(). */ + error = GRPC_OS_ERROR(so_error, "connect"); + break; + default: + /* We don't really know which syscall triggered the problem here, + so punt by reporting getsockopt(). */ + error = GRPC_OS_ERROR(so_error, "getsockopt(SO_ERROR)"); + break; + } + +finish: + if (fd != NULL) { + grpc_pollset_set_del_fd(exec_ctx, ac->interested_parties, fd); + grpc_fd_orphan(exec_ctx, fd, NULL, NULL, false /* already_closed */, + "tcp_client_orphan"); + fd = NULL; + } + done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (error != GRPC_ERROR_NONE) { + char *error_descr; + grpc_slice str; + bool ret = grpc_error_get_str(error, GRPC_ERROR_STR_DESCRIPTION, &str); + GPR_ASSERT(ret); + char *desc = grpc_slice_to_c_string(str); + gpr_asprintf(&error_descr, "Failed to connect to remote host: %s", desc); + error = grpc_error_set_str(error, GRPC_ERROR_STR_DESCRIPTION, + grpc_slice_from_copied_string(error_descr)); + gpr_free(error_descr); + gpr_free(desc); + error = grpc_error_set_str(error, GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(ac->addr_str)); + } + if (done) { + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_str); + grpc_channel_args_destroy(exec_ctx, ac->channel_args); + gpr_free(ac); + } + GRPC_CLOSURE_SCHED(exec_ctx, closure, error); +} + +static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, + grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) { + int fd; + grpc_dualstack_mode dsmode; + int err; + async_connect *ac; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address addr4_copy; + grpc_fd *fdobj; + char *name; + char *addr_str; + grpc_error *error; + + *ep = NULL; + + /* Use dualstack sockets where available. */ + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + error = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode, &fd); + if (error != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, error); + return; + } + if (dsmode == GRPC_DSMODE_IPV4) { + /* If we got an AF_INET socket, map the address back to IPv4. */ + GPR_ASSERT(grpc_sockaddr_is_v4mapped(addr, &addr4_copy)); + addr = &addr4_copy; + } + if ((error = prepare_socket(addr, fd, channel_args)) != GRPC_ERROR_NONE) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, error); + return; + } + + do { + GPR_ASSERT(addr->len < ~(socklen_t)0); + err = + connect(fd, (const struct sockaddr *)addr->addr, (socklen_t)addr->len); + } while (err < 0 && errno == EINTR); + + addr_str = grpc_sockaddr_to_uri(addr); + gpr_asprintf(&name, "tcp-client:%s", addr_str); + + fdobj = grpc_fd_create(fd, name); + + if (err >= 0) { + *ep = + grpc_tcp_client_create_from_fd(exec_ctx, fdobj, channel_args, addr_str); + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); + goto done; + } + + if (errno != EWOULDBLOCK && errno != EINPROGRESS) { + grpc_fd_orphan(exec_ctx, fdobj, NULL, NULL, false /* already_closed */, + "tcp_client_connect_error"); + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_OS_ERROR(errno, "connect")); + goto done; + } + + grpc_pollset_set_add_fd(exec_ctx, interested_parties, fdobj); + + ac = (async_connect *)gpr_malloc(sizeof(async_connect)); + ac->closure = closure; + ac->ep = ep; + ac->fd = fdobj; + ac->interested_parties = interested_parties; + ac->addr_str = addr_str; + addr_str = NULL; + gpr_mu_init(&ac->mu); + ac->refs = 2; + GRPC_CLOSURE_INIT(&ac->write_closure, on_writable, ac, + grpc_schedule_on_exec_ctx); + ac->channel_args = grpc_channel_args_copy(channel_args); + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting fd %p", + ac->addr_str, fdobj); + } + + gpr_mu_lock(&ac->mu); + GRPC_CLOSURE_INIT(&ac->on_alarm, tc_on_alarm, ac, grpc_schedule_on_exec_ctx); + grpc_timer_init(exec_ctx, &ac->alarm, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + &ac->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_fd_notify_on_write(exec_ctx, ac->fd, &ac->write_closure); + gpr_mu_unlock(&ac->mu); + +done: + gpr_free(name); + gpr_free(addr_str); +} + +// overridden by api_fuzzer.c +void (*grpc_tcp_client_connect_impl)( + grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) = tcp_client_connect_impl; + +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) { + grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, + channel_args, addr, deadline); +} + +#endif diff --git a/src/core/lib/iomgr/tcp_client_uv.c b/src/core/lib/iomgr/tcp_client_uv.c deleted file mode 100644 index 786c456b73..0000000000 --- a/src/core/lib/iomgr/tcp_client_uv.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include - -#include -#include - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/iomgr/tcp_uv.h" -#include "src/core/lib/iomgr/timer.h" - -extern grpc_tracer_flag grpc_tcp_trace; - -typedef struct grpc_uv_tcp_connect { - uv_connect_t connect_req; - grpc_timer alarm; - grpc_closure on_alarm; - uv_tcp_t *tcp_handle; - grpc_closure *closure; - grpc_endpoint **endpoint; - int refs; - char *addr_name; - grpc_resource_quota *resource_quota; -} grpc_uv_tcp_connect; - -static void uv_tcp_connect_cleanup(grpc_exec_ctx *exec_ctx, - grpc_uv_tcp_connect *connect) { - grpc_resource_quota_unref_internal(exec_ctx, connect->resource_quota); - gpr_free(connect->addr_name); - gpr_free(connect); -} - -static void tcp_close_callback(uv_handle_t *handle) { gpr_free(handle); } - -static void uv_tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, - grpc_error *error) { - int done; - grpc_uv_tcp_connect *connect = acp; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", - connect->addr_name, str); - } - if (error == GRPC_ERROR_NONE) { - /* error == NONE implies that the timer ran out, and wasn't cancelled. If - it was cancelled, then the handler that cancelled it also should close - the handle, if applicable */ - uv_close((uv_handle_t *)connect->tcp_handle, tcp_close_callback); - } - done = (--connect->refs == 0); - if (done) { - uv_tcp_connect_cleanup(exec_ctx, connect); - } -} - -static void uv_tc_on_connect(uv_connect_t *req, int status) { - grpc_uv_tcp_connect *connect = req->data; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_error *error = GRPC_ERROR_NONE; - int done; - grpc_closure *closure = connect->closure; - grpc_timer_cancel(&exec_ctx, &connect->alarm); - if (status == 0) { - *connect->endpoint = grpc_tcp_create( - connect->tcp_handle, connect->resource_quota, connect->addr_name); - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to connect to remote host"); - error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, -status); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - if (status == UV_ECANCELED) { - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string("Timeout occurred")); - // This should only happen if the handle is already closed - } else { - error = grpc_error_set_str( - error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - uv_close((uv_handle_t *)connect->tcp_handle, tcp_close_callback); - } - } - done = (--connect->refs == 0); - if (done) { - grpc_exec_ctx_flush(&exec_ctx); - uv_tcp_connect_cleanup(&exec_ctx, connect); - } - GRPC_CLOSURE_SCHED(&exec_ctx, closure, error); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, - grpc_closure *closure, grpc_endpoint **ep, - grpc_pollset_set *interested_parties, - const grpc_channel_args *channel_args, - const grpc_resolved_address *resolved_addr, - gpr_timespec deadline) { - grpc_uv_tcp_connect *connect; - grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); - (void)channel_args; - (void)interested_parties; - - GRPC_UV_ASSERT_SAME_THREAD(); - - if (channel_args != NULL) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - resource_quota = grpc_resource_quota_ref_internal( - channel_args->args[i].value.pointer.p); - } - } - } - - connect = gpr_zalloc(sizeof(grpc_uv_tcp_connect)); - connect->closure = closure; - connect->endpoint = ep; - connect->tcp_handle = gpr_malloc(sizeof(uv_tcp_t)); - connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); - connect->resource_quota = resource_quota; - uv_tcp_init(uv_default_loop(), connect->tcp_handle); - connect->connect_req.data = connect; - connect->refs = 1; - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting", - connect->addr_name); - } - - // TODO(murgatroid99): figure out what the return value here means - uv_tcp_connect(&connect->connect_req, connect->tcp_handle, - (const struct sockaddr *)resolved_addr->addr, - uv_tc_on_connect); - GRPC_CLOSURE_INIT(&connect->on_alarm, uv_tc_on_alarm, connect, - grpc_schedule_on_exec_ctx); - grpc_timer_init(exec_ctx, &connect->alarm, - gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), - &connect->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC)); -} - -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, - grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_endpoint **ep, - grpc_pollset_set *interested_parties, - const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) { - grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, - channel_args, addr, deadline); -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc new file mode 100644 index 0000000000..786c456b73 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client_uv.cc @@ -0,0 +1,183 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include + +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_uv.h" +#include "src/core/lib/iomgr/timer.h" + +extern grpc_tracer_flag grpc_tcp_trace; + +typedef struct grpc_uv_tcp_connect { + uv_connect_t connect_req; + grpc_timer alarm; + grpc_closure on_alarm; + uv_tcp_t *tcp_handle; + grpc_closure *closure; + grpc_endpoint **endpoint; + int refs; + char *addr_name; + grpc_resource_quota *resource_quota; +} grpc_uv_tcp_connect; + +static void uv_tcp_connect_cleanup(grpc_exec_ctx *exec_ctx, + grpc_uv_tcp_connect *connect) { + grpc_resource_quota_unref_internal(exec_ctx, connect->resource_quota); + gpr_free(connect->addr_name); + gpr_free(connect); +} + +static void tcp_close_callback(uv_handle_t *handle) { gpr_free(handle); } + +static void uv_tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, + grpc_error *error) { + int done; + grpc_uv_tcp_connect *connect = acp; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", + connect->addr_name, str); + } + if (error == GRPC_ERROR_NONE) { + /* error == NONE implies that the timer ran out, and wasn't cancelled. If + it was cancelled, then the handler that cancelled it also should close + the handle, if applicable */ + uv_close((uv_handle_t *)connect->tcp_handle, tcp_close_callback); + } + done = (--connect->refs == 0); + if (done) { + uv_tcp_connect_cleanup(exec_ctx, connect); + } +} + +static void uv_tc_on_connect(uv_connect_t *req, int status) { + grpc_uv_tcp_connect *connect = req->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_error *error = GRPC_ERROR_NONE; + int done; + grpc_closure *closure = connect->closure; + grpc_timer_cancel(&exec_ctx, &connect->alarm); + if (status == 0) { + *connect->endpoint = grpc_tcp_create( + connect->tcp_handle, connect->resource_quota, connect->addr_name); + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to connect to remote host"); + error = grpc_error_set_int(error, GRPC_ERROR_INT_ERRNO, -status); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + if (status == UV_ECANCELED) { + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string("Timeout occurred")); + // This should only happen if the handle is already closed + } else { + error = grpc_error_set_str( + error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + uv_close((uv_handle_t *)connect->tcp_handle, tcp_close_callback); + } + } + done = (--connect->refs == 0); + if (done) { + grpc_exec_ctx_flush(&exec_ctx); + uv_tcp_connect_cleanup(&exec_ctx, connect); + } + GRPC_CLOSURE_SCHED(&exec_ctx, closure, error); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, + grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_channel_args *channel_args, + const grpc_resolved_address *resolved_addr, + gpr_timespec deadline) { + grpc_uv_tcp_connect *connect; + grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); + (void)channel_args; + (void)interested_parties; + + GRPC_UV_ASSERT_SAME_THREAD(); + + if (channel_args != NULL) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + channel_args->args[i].value.pointer.p); + } + } + } + + connect = gpr_zalloc(sizeof(grpc_uv_tcp_connect)); + connect->closure = closure; + connect->endpoint = ep; + connect->tcp_handle = gpr_malloc(sizeof(uv_tcp_t)); + connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); + connect->resource_quota = resource_quota; + uv_tcp_init(uv_default_loop(), connect->tcp_handle); + connect->connect_req.data = connect; + connect->refs = 1; + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: asynchronously connecting", + connect->addr_name); + } + + // TODO(murgatroid99): figure out what the return value here means + uv_tcp_connect(&connect->connect_req, connect->tcp_handle, + (const struct sockaddr *)resolved_addr->addr, + uv_tc_on_connect); + GRPC_CLOSURE_INIT(&connect->on_alarm, uv_tc_on_alarm, connect, + grpc_schedule_on_exec_ctx); + grpc_timer_init(exec_ctx, &connect->alarm, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + &connect->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC)); +} + +// overridden by api_fuzzer.c +void (*grpc_tcp_client_connect_impl)( + grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) = tcp_client_connect_impl; + +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) { + grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, + channel_args, addr, deadline); +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_client_windows.c b/src/core/lib/iomgr/tcp_client_windows.c deleted file mode 100644 index fc62105cc9..0000000000 --- a/src/core/lib/iomgr/tcp_client_windows.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include "src/core/lib/iomgr/sockaddr_windows.h" - -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/socket_windows.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/iomgr/tcp_windows.h" -#include "src/core/lib/iomgr/timer.h" - -typedef struct { - grpc_closure *on_done; - gpr_mu mu; - grpc_winsocket *socket; - gpr_timespec deadline; - grpc_timer alarm; - grpc_closure on_alarm; - char *addr_name; - int refs; - grpc_closure on_connect; - grpc_endpoint **endpoint; - grpc_channel_args *channel_args; -} async_connect; - -static void async_connect_unlock_and_cleanup(grpc_exec_ctx *exec_ctx, - async_connect *ac, - grpc_winsocket *socket) { - int done = (--ac->refs == 0); - gpr_mu_unlock(&ac->mu); - if (done) { - grpc_channel_args_destroy(exec_ctx, ac->channel_args); - gpr_mu_destroy(&ac->mu); - gpr_free(ac->addr_name); - gpr_free(ac); - } - if (socket != NULL) grpc_winsocket_destroy(socket); -} - -static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { - async_connect *ac = acp; - gpr_mu_lock(&ac->mu); - grpc_winsocket *socket = ac->socket; - ac->socket = NULL; - if (socket != NULL) { - grpc_winsocket_shutdown(socket); - } - async_connect_unlock_and_cleanup(exec_ctx, ac, socket); -} - -static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { - async_connect *ac = acp; - grpc_endpoint **ep = ac->endpoint; - GPR_ASSERT(*ep == NULL); - grpc_closure *on_done = ac->on_done; - - GRPC_ERROR_REF(error); - - gpr_mu_lock(&ac->mu); - grpc_winsocket *socket = ac->socket; - ac->socket = NULL; - gpr_mu_unlock(&ac->mu); - - grpc_timer_cancel(exec_ctx, &ac->alarm); - - gpr_mu_lock(&ac->mu); - - if (error == GRPC_ERROR_NONE) { - if (socket != NULL) { - DWORD transfered_bytes = 0; - DWORD flags; - BOOL wsa_success = - WSAGetOverlappedResult(socket->socket, &socket->write_info.overlapped, - &transfered_bytes, FALSE, &flags); - GPR_ASSERT(transfered_bytes == 0); - if (!wsa_success) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx"); - } else { - *ep = - grpc_tcp_create(exec_ctx, socket, ac->channel_args, ac->addr_name); - socket = NULL; - } - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket is null"); - } - } - - async_connect_unlock_and_cleanup(exec_ctx, ac, socket); - /* If the connection was aborted, the callback was already called when - the deadline was met. */ - GRPC_CLOSURE_SCHED(exec_ctx, on_done, error); -} - -/* Tries to issue one async connection, then schedules both an IOCP - notification request for the connection, and one timeout alert. */ -static void tcp_client_connect_impl( - grpc_exec_ctx *exec_ctx, grpc_closure *on_done, grpc_endpoint **endpoint, - grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, gpr_timespec deadline) { - SOCKET sock = INVALID_SOCKET; - BOOL success; - int status; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address local_address; - async_connect *ac; - grpc_winsocket *socket = NULL; - LPFN_CONNECTEX ConnectEx; - GUID guid = WSAID_CONNECTEX; - DWORD ioctl_num_bytes; - grpc_winsocket_callback_info *info; - grpc_error *error = GRPC_ERROR_NONE; - - *endpoint = NULL; - - /* Use dualstack sockets where available. */ - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, - WSA_FLAG_OVERLAPPED); - if (sock == INVALID_SOCKET) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); - goto failure; - } - - error = grpc_tcp_prepare_socket(sock); - if (error != GRPC_ERROR_NONE) { - goto failure; - } - - /* Grab the function pointer for ConnectEx for that specific socket. - It may change depending on the interface. */ - status = - WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), - &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL); - - if (status != 0) { - error = GRPC_WSA_ERROR(WSAGetLastError(), - "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); - goto failure; - } - - grpc_sockaddr_make_wildcard6(0, &local_address); - - status = bind(sock, (struct sockaddr *)&local_address.addr, - (int)local_address.len); - if (status != 0) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); - goto failure; - } - - socket = grpc_winsocket_create(sock, "client"); - info = &socket->write_info; - success = ConnectEx(sock, (struct sockaddr *)&addr->addr, (int)addr->len, - NULL, 0, NULL, &info->overlapped); - - /* It wouldn't be unusual to get a success immediately. But we'll still get - an IOCP notification, so let's ignore it. */ - if (!success) { - int last_error = WSAGetLastError(); - if (last_error != ERROR_IO_PENDING) { - error = GRPC_WSA_ERROR(last_error, "ConnectEx"); - goto failure; - } - } - - ac = gpr_malloc(sizeof(async_connect)); - ac->on_done = on_done; - ac->socket = socket; - gpr_mu_init(&ac->mu); - ac->refs = 2; - ac->addr_name = grpc_sockaddr_to_uri(addr); - ac->endpoint = endpoint; - ac->channel_args = grpc_channel_args_copy(channel_args); - GRPC_CLOSURE_INIT(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx); - - GRPC_CLOSURE_INIT(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx); - grpc_timer_init(exec_ctx, &ac->alarm, deadline, &ac->on_alarm, - gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect); - return; - -failure: - GPR_ASSERT(error != GRPC_ERROR_NONE); - char *target_uri = grpc_sockaddr_to_uri(addr); - grpc_error *final_error = grpc_error_set_str( - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Failed to connect", - &error, 1), - GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(target_uri)); - GRPC_ERROR_UNREF(error); - if (socket != NULL) { - grpc_winsocket_destroy(socket); - } else if (sock != INVALID_SOCKET) { - closesocket(sock); - } - GRPC_CLOSURE_SCHED(exec_ctx, on_done, final_error); -} - -// overridden by api_fuzzer.c -void (*grpc_tcp_client_connect_impl)( - grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, - grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) = tcp_client_connect_impl; - -void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, - grpc_endpoint **ep, - grpc_pollset_set *interested_parties, - const grpc_channel_args *channel_args, - const grpc_resolved_address *addr, - gpr_timespec deadline) { - grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, - channel_args, addr, deadline); -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc new file mode 100644 index 0000000000..fc62105cc9 --- /dev/null +++ b/src/core/lib/iomgr/tcp_client_windows.cc @@ -0,0 +1,245 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_windows.h" +#include "src/core/lib/iomgr/timer.h" + +typedef struct { + grpc_closure *on_done; + gpr_mu mu; + grpc_winsocket *socket; + gpr_timespec deadline; + grpc_timer alarm; + grpc_closure on_alarm; + char *addr_name; + int refs; + grpc_closure on_connect; + grpc_endpoint **endpoint; + grpc_channel_args *channel_args; +} async_connect; + +static void async_connect_unlock_and_cleanup(grpc_exec_ctx *exec_ctx, + async_connect *ac, + grpc_winsocket *socket) { + int done = (--ac->refs == 0); + gpr_mu_unlock(&ac->mu); + if (done) { + grpc_channel_args_destroy(exec_ctx, ac->channel_args); + gpr_mu_destroy(&ac->mu); + gpr_free(ac->addr_name); + gpr_free(ac); + } + if (socket != NULL) grpc_winsocket_destroy(socket); +} + +static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { + async_connect *ac = acp; + gpr_mu_lock(&ac->mu); + grpc_winsocket *socket = ac->socket; + ac->socket = NULL; + if (socket != NULL) { + grpc_winsocket_shutdown(socket); + } + async_connect_unlock_and_cleanup(exec_ctx, ac, socket); +} + +static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { + async_connect *ac = acp; + grpc_endpoint **ep = ac->endpoint; + GPR_ASSERT(*ep == NULL); + grpc_closure *on_done = ac->on_done; + + GRPC_ERROR_REF(error); + + gpr_mu_lock(&ac->mu); + grpc_winsocket *socket = ac->socket; + ac->socket = NULL; + gpr_mu_unlock(&ac->mu); + + grpc_timer_cancel(exec_ctx, &ac->alarm); + + gpr_mu_lock(&ac->mu); + + if (error == GRPC_ERROR_NONE) { + if (socket != NULL) { + DWORD transfered_bytes = 0; + DWORD flags; + BOOL wsa_success = + WSAGetOverlappedResult(socket->socket, &socket->write_info.overlapped, + &transfered_bytes, FALSE, &flags); + GPR_ASSERT(transfered_bytes == 0); + if (!wsa_success) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "ConnectEx"); + } else { + *ep = + grpc_tcp_create(exec_ctx, socket, ac->channel_args, ac->addr_name); + socket = NULL; + } + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("socket is null"); + } + } + + async_connect_unlock_and_cleanup(exec_ctx, ac, socket); + /* If the connection was aborted, the callback was already called when + the deadline was met. */ + GRPC_CLOSURE_SCHED(exec_ctx, on_done, error); +} + +/* Tries to issue one async connection, then schedules both an IOCP + notification request for the connection, and one timeout alert. */ +static void tcp_client_connect_impl( + grpc_exec_ctx *exec_ctx, grpc_closure *on_done, grpc_endpoint **endpoint, + grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, gpr_timespec deadline) { + SOCKET sock = INVALID_SOCKET; + BOOL success; + int status; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address local_address; + async_connect *ac; + grpc_winsocket *socket = NULL; + LPFN_CONNECTEX ConnectEx; + GUID guid = WSAID_CONNECTEX; + DWORD ioctl_num_bytes; + grpc_winsocket_callback_info *info; + grpc_error *error = GRPC_ERROR_NONE; + + *endpoint = NULL; + + /* Use dualstack sockets where available. */ + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); + goto failure; + } + + error = grpc_tcp_prepare_socket(sock); + if (error != GRPC_ERROR_NONE) { + goto failure; + } + + /* Grab the function pointer for ConnectEx for that specific socket. + It may change depending on the interface. */ + status = + WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + &ConnectEx, sizeof(ConnectEx), &ioctl_num_bytes, NULL, NULL); + + if (status != 0) { + error = GRPC_WSA_ERROR(WSAGetLastError(), + "WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER)"); + goto failure; + } + + grpc_sockaddr_make_wildcard6(0, &local_address); + + status = bind(sock, (struct sockaddr *)&local_address.addr, + (int)local_address.len); + if (status != 0) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); + goto failure; + } + + socket = grpc_winsocket_create(sock, "client"); + info = &socket->write_info; + success = ConnectEx(sock, (struct sockaddr *)&addr->addr, (int)addr->len, + NULL, 0, NULL, &info->overlapped); + + /* It wouldn't be unusual to get a success immediately. But we'll still get + an IOCP notification, so let's ignore it. */ + if (!success) { + int last_error = WSAGetLastError(); + if (last_error != ERROR_IO_PENDING) { + error = GRPC_WSA_ERROR(last_error, "ConnectEx"); + goto failure; + } + } + + ac = gpr_malloc(sizeof(async_connect)); + ac->on_done = on_done; + ac->socket = socket; + gpr_mu_init(&ac->mu); + ac->refs = 2; + ac->addr_name = grpc_sockaddr_to_uri(addr); + ac->endpoint = endpoint; + ac->channel_args = grpc_channel_args_copy(channel_args); + GRPC_CLOSURE_INIT(&ac->on_connect, on_connect, ac, grpc_schedule_on_exec_ctx); + + GRPC_CLOSURE_INIT(&ac->on_alarm, on_alarm, ac, grpc_schedule_on_exec_ctx); + grpc_timer_init(exec_ctx, &ac->alarm, deadline, &ac->on_alarm, + gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_socket_notify_on_write(exec_ctx, socket, &ac->on_connect); + return; + +failure: + GPR_ASSERT(error != GRPC_ERROR_NONE); + char *target_uri = grpc_sockaddr_to_uri(addr); + grpc_error *final_error = grpc_error_set_str( + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING("Failed to connect", + &error, 1), + GRPC_ERROR_STR_TARGET_ADDRESS, grpc_slice_from_copied_string(target_uri)); + GRPC_ERROR_UNREF(error); + if (socket != NULL) { + grpc_winsocket_destroy(socket); + } else if (sock != INVALID_SOCKET) { + closesocket(sock); + } + GRPC_CLOSURE_SCHED(exec_ctx, on_done, final_error); +} + +// overridden by api_fuzzer.c +void (*grpc_tcp_client_connect_impl)( + grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, + grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) = tcp_client_connect_impl; + +void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, + grpc_endpoint **ep, + grpc_pollset_set *interested_parties, + const grpc_channel_args *channel_args, + const grpc_resolved_address *addr, + gpr_timespec deadline) { + grpc_tcp_client_connect_impl(exec_ctx, closure, ep, interested_parties, + channel_args, addr, deadline); +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_posix.c b/src/core/lib/iomgr/tcp_posix.c deleted file mode 100644 index 7e271294fd..0000000000 --- a/src/core/lib/iomgr/tcp_posix.c +++ /dev/null @@ -1,819 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/network_status_tracker.h" -#include "src/core/lib/iomgr/tcp_posix.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -#ifdef GRPC_HAVE_MSG_NOSIGNAL -#define SENDMSG_FLAGS MSG_NOSIGNAL -#else -#define SENDMSG_FLAGS 0 -#endif - -#ifdef GRPC_MSG_IOVLEN_TYPE -typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type; -#else -typedef size_t msg_iovlen_type; -#endif - -grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp"); - -typedef struct { - grpc_endpoint base; - grpc_fd *em_fd; - int fd; - bool finished_edge; - double target_length; - double bytes_read_this_round; - gpr_refcount refcount; - gpr_atm shutdown_count; - - int min_read_chunk_size; - int max_read_chunk_size; - - /* garbage after the last read */ - grpc_slice_buffer last_read_buffer; - - grpc_slice_buffer *incoming_buffer; - grpc_slice_buffer *outgoing_buffer; - /** slice within outgoing_buffer to write next */ - size_t outgoing_slice_idx; - /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */ - size_t outgoing_byte_idx; - - grpc_closure *read_cb; - grpc_closure *write_cb; - grpc_closure *release_fd_cb; - int *release_fd; - - grpc_closure read_done_closure; - grpc_closure write_done_closure; - - char *peer_string; - - grpc_resource_user *resource_user; - grpc_resource_user_slice_allocator slice_allocator; -} grpc_tcp; - -typedef struct backup_poller { - gpr_mu *pollset_mu; - grpc_closure run_poller; -} backup_poller; - -#define BACKUP_POLLER_POLLSET(b) ((grpc_pollset *)((b) + 1)) - -static gpr_atm g_uncovered_notifications_pending; -static gpr_atm g_backup_poller; /* backup_poller* */ - -static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - grpc_error *error); -static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - grpc_error *error); -static void tcp_drop_uncovered_then_handle_write(grpc_exec_ctx *exec_ctx, - void *arg /* grpc_tcp */, - grpc_error *error); - -static void done_poller(grpc_exec_ctx *exec_ctx, void *bp, - grpc_error *error_ignored) { - backup_poller *p = (backup_poller *)bp; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p destroy", p); - } - grpc_pollset_destroy(exec_ctx, BACKUP_POLLER_POLLSET(p)); - gpr_free(p); -} - -static void run_poller(grpc_exec_ctx *exec_ctx, void *bp, - grpc_error *error_ignored) { - backup_poller *p = (backup_poller *)bp; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p run", p); - } - gpr_mu_lock(p->pollset_mu); - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - gpr_timespec deadline = - gpr_time_add(now, gpr_time_from_seconds(10, GPR_TIMESPAN)); - GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS(exec_ctx); - GRPC_LOG_IF_ERROR("backup_poller:pollset_work", - grpc_pollset_work(exec_ctx, BACKUP_POLLER_POLLSET(p), NULL, - now, deadline)); - gpr_mu_unlock(p->pollset_mu); - /* last "uncovered" notification is the ref that keeps us polling, if we get - * there try a cas to release it */ - if (gpr_atm_no_barrier_load(&g_uncovered_notifications_pending) == 1 && - gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) { - gpr_mu_lock(p->pollset_mu); - bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok); - } - gpr_mu_unlock(p->pollset_mu); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p shutdown", p); - } - grpc_pollset_shutdown(exec_ctx, BACKUP_POLLER_POLLSET(p), - GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p, - grpc_schedule_on_exec_ctx)); - } else { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p reschedule", p); - } - GRPC_CLOSURE_SCHED(exec_ctx, &p->run_poller, GRPC_ERROR_NONE); - } -} - -static void drop_uncovered(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - backup_poller *p = (backup_poller *)gpr_atm_acq_load(&g_backup_poller); - gpr_atm old_count = - gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, -1); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p uncover cnt %d->%d", p, (int)old_count, - (int)old_count - 1); - } - GPR_ASSERT(old_count != 1); -} - -static void cover_self(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - backup_poller *p; - gpr_atm old_count = - gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER: cover cnt %d->%d", (int)old_count, - 2 + (int)old_count); - } - if (old_count == 0) { - GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED(exec_ctx); - p = (backup_poller *)gpr_malloc(sizeof(*p) + grpc_pollset_size()); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p create", p); - } - grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu); - gpr_atm_rel_store(&g_backup_poller, (gpr_atm)p); - GRPC_CLOSURE_SCHED( - exec_ctx, - GRPC_CLOSURE_INIT(&p->run_poller, run_poller, p, - grpc_executor_scheduler(GRPC_EXECUTOR_LONG)), - GRPC_ERROR_NONE); - } else { - while ((p = (backup_poller *)gpr_atm_acq_load(&g_backup_poller)) == NULL) { - // spin waiting for backup poller - } - } - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p add %p", p, tcp); - } - grpc_pollset_add_fd(exec_ctx, BACKUP_POLLER_POLLSET(p), tcp->em_fd); - if (old_count != 0) { - drop_uncovered(exec_ctx, tcp); - } -} - -static void notify_on_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p notify_on_read", tcp); - } - GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_done_closure); -} - -static void notify_on_write(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p notify_on_write", tcp); - } - cover_self(exec_ctx, tcp); - GRPC_CLOSURE_INIT(&tcp->write_done_closure, - tcp_drop_uncovered_then_handle_write, tcp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_done_closure); -} - -static void tcp_drop_uncovered_then_handle_write(grpc_exec_ctx *exec_ctx, - void *arg, grpc_error *error) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p got_write: %s", arg, grpc_error_string(error)); - } - drop_uncovered(exec_ctx, (grpc_tcp *)arg); - tcp_handle_write(exec_ctx, arg, error); -} - -static void add_to_estimate(grpc_tcp *tcp, size_t bytes) { - tcp->bytes_read_this_round += (double)bytes; -} - -static void finish_estimate(grpc_tcp *tcp) { - /* If we read >80% of the target buffer in one read loop, increase the size - of the target buffer to either the amount read, or twice its previous - value */ - if (tcp->bytes_read_this_round > tcp->target_length * 0.8) { - tcp->target_length = - GPR_MAX(2 * tcp->target_length, tcp->bytes_read_this_round); - } else { - tcp->target_length = - 0.99 * tcp->target_length + 0.01 * tcp->bytes_read_this_round; - } - tcp->bytes_read_this_round = 0; -} - -static size_t get_target_read_size(grpc_tcp *tcp) { - grpc_resource_quota *rq = grpc_resource_user_quota(tcp->resource_user); - double pressure = grpc_resource_quota_get_memory_pressure(rq); - double target = - tcp->target_length * (pressure > 0.8 ? (1.0 - pressure) / 0.2 : 1.0); - size_t sz = (((size_t)GPR_CLAMP(target, tcp->min_read_chunk_size, - tcp->max_read_chunk_size)) + - 255) & - ~(size_t)255; - /* don't use more than 1/16th of the overall resource quota for a single read - * alloc */ - size_t rqmax = grpc_resource_quota_peek_size(rq); - if (sz > rqmax / 16 && rqmax > 1024) { - sz = rqmax / 16; - } - return sz; -} - -static grpc_error *tcp_annotate_error(grpc_error *src_error, grpc_tcp *tcp) { - return grpc_error_set_str( - grpc_error_set_int(src_error, GRPC_ERROR_INT_FD, tcp->fd), - GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(tcp->peer_string)); -} - -static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - grpc_error *error); -static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - grpc_error *error); - -static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_error *why) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_fd_shutdown(exec_ctx, tcp->em_fd, why); - grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); -} - -static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd, - false /* already_closed */, "tcp_unref_orphan"); - grpc_slice_buffer_destroy_internal(exec_ctx, &tcp->last_read_buffer); - grpc_resource_user_unref(exec_ctx, tcp->resource_user); - gpr_free(tcp->peer_string); - gpr_free(tcp); -} - -#ifndef NDEBUG -#define TCP_UNREF(cl, tcp, reason) \ - tcp_unref((cl), (tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val - 1); - } - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val + 1); - } - gpr_ref(&tcp->refcount); -} -#else -#define TCP_UNREF(cl, tcp, reason) tcp_unref((cl), (tcp)) -#define TCP_REF(tcp, reason) tcp_ref((tcp)) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } -#endif - -static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { - grpc_network_status_unregister_endpoint(ep); - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &tcp->last_read_buffer); - TCP_UNREF(exec_ctx, tcp, "destroy"); -} - -static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, - grpc_error *error) { - grpc_closure *cb = tcp->read_cb; - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg); - size_t i; - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "read: error=%s", str); - - for (i = 0; i < tcp->incoming_buffer->count; i++) { - char *dump = grpc_dump_slice(tcp->incoming_buffer->slices[i], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); - gpr_free(dump); - } - } - - tcp->read_cb = NULL; - tcp->incoming_buffer = NULL; - GRPC_CLOSURE_RUN(exec_ctx, cb, error); -} - -#define MAX_READ_IOVEC 4 -static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - struct msghdr msg; - struct iovec iov[MAX_READ_IOVEC]; - ssize_t read_bytes; - size_t i; - - GPR_ASSERT(!tcp->finished_edge); - GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC); - GPR_TIMER_BEGIN("tcp_continue_read", 0); - - for (i = 0; i < tcp->incoming_buffer->count; i++) { - iov[i].iov_base = GRPC_SLICE_START_PTR(tcp->incoming_buffer->slices[i]); - iov[i].iov_len = GRPC_SLICE_LENGTH(tcp->incoming_buffer->slices[i]); - } - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = (msg_iovlen_type)tcp->incoming_buffer->count; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - GRPC_STATS_INC_TCP_READ_OFFER(exec_ctx, tcp->incoming_buffer->length); - GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(exec_ctx, tcp->incoming_buffer->count); - - GPR_TIMER_BEGIN("recvmsg", 0); - do { - GRPC_STATS_INC_SYSCALL_READ(exec_ctx); - read_bytes = recvmsg(tcp->fd, &msg, 0); - } while (read_bytes < 0 && errno == EINTR); - GPR_TIMER_END("recvmsg", read_bytes >= 0); - - if (read_bytes < 0) { - /* NB: After calling call_read_cb a parallel call of the read handler may - * be running. */ - if (errno == EAGAIN) { - finish_estimate(tcp); - /* We've consumed the edge, request a new one */ - notify_on_read(exec_ctx, tcp); - } else { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, - tcp->incoming_buffer); - call_read_cb(exec_ctx, tcp, - tcp_annotate_error(GRPC_OS_ERROR(errno, "recvmsg"), tcp)); - TCP_UNREF(exec_ctx, tcp, "read"); - } - } else if (read_bytes == 0) { - /* 0 read size ==> end of stream */ - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer); - call_read_cb( - exec_ctx, tcp, - tcp_annotate_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), tcp)); - TCP_UNREF(exec_ctx, tcp, "read"); - } else { - GRPC_STATS_INC_TCP_READ_SIZE(exec_ctx, read_bytes); - add_to_estimate(tcp, (size_t)read_bytes); - GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length); - if ((size_t)read_bytes < tcp->incoming_buffer->length) { - grpc_slice_buffer_trim_end( - tcp->incoming_buffer, - tcp->incoming_buffer->length - (size_t)read_bytes, - &tcp->last_read_buffer); - } - GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length); - call_read_cb(exec_ctx, tcp, GRPC_ERROR_NONE); - TCP_UNREF(exec_ctx, tcp, "read"); - } - - GPR_TIMER_END("tcp_continue_read", 0); -} - -static void tcp_read_allocation_done(grpc_exec_ctx *exec_ctx, void *tcpp, - grpc_error *error) { - grpc_tcp *tcp = (grpc_tcp *)tcpp; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp, - grpc_error_string(error)); - } - if (error != GRPC_ERROR_NONE) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer); - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, - &tcp->last_read_buffer); - call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error)); - TCP_UNREF(exec_ctx, tcp, "read"); - } else { - tcp_do_read(exec_ctx, tcp); - } -} - -static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - size_t target_read_size = get_target_read_size(tcp); - if (tcp->incoming_buffer->length < target_read_size && - tcp->incoming_buffer->count < MAX_READ_IOVEC) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p alloc_slices", tcp); - } - grpc_resource_user_alloc_slices(exec_ctx, &tcp->slice_allocator, - target_read_size, 1, tcp->incoming_buffer); - } else { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p do_read", tcp); - } - tcp_do_read(exec_ctx, tcp); - } -} - -static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - grpc_error *error) { - grpc_tcp *tcp = (grpc_tcp *)arg; - GPR_ASSERT(!tcp->finished_edge); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "TCP:%p got_read: %s", tcp, grpc_error_string(error)); - } - - if (error != GRPC_ERROR_NONE) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer); - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, - &tcp->last_read_buffer); - call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error)); - TCP_UNREF(exec_ctx, tcp, "read"); - } else { - tcp_continue_read(exec_ctx, tcp); - } -} - -static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_slice_buffer *incoming_buffer, grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - GPR_ASSERT(tcp->read_cb == NULL); - tcp->read_cb = cb; - tcp->incoming_buffer = incoming_buffer; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, incoming_buffer); - grpc_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer); - TCP_REF(tcp, "read"); - if (tcp->finished_edge) { - tcp->finished_edge = false; - notify_on_read(exec_ctx, tcp); - } else { - GRPC_CLOSURE_SCHED(exec_ctx, &tcp->read_done_closure, GRPC_ERROR_NONE); - } -} - -/* returns true if done, false if pending; if returning true, *error is set */ -#define MAX_WRITE_IOVEC 1000 -static bool tcp_flush(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, - grpc_error **error) { - struct msghdr msg; - struct iovec iov[MAX_WRITE_IOVEC]; - msg_iovlen_type iov_size; - ssize_t sent_length; - size_t sending_length; - size_t trailing; - size_t unwind_slice_idx; - size_t unwind_byte_idx; - - for (;;) { - sending_length = 0; - unwind_slice_idx = tcp->outgoing_slice_idx; - unwind_byte_idx = tcp->outgoing_byte_idx; - for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count && - iov_size != MAX_WRITE_IOVEC; - iov_size++) { - iov[iov_size].iov_base = - GRPC_SLICE_START_PTR( - tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) + - tcp->outgoing_byte_idx; - iov[iov_size].iov_len = - GRPC_SLICE_LENGTH( - tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) - - tcp->outgoing_byte_idx; - sending_length += iov[iov_size].iov_len; - tcp->outgoing_slice_idx++; - tcp->outgoing_byte_idx = 0; - } - GPR_ASSERT(iov_size > 0); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = iov; - msg.msg_iovlen = iov_size; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - - GRPC_STATS_INC_TCP_WRITE_SIZE(exec_ctx, sending_length); - GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(exec_ctx, iov_size); - - GPR_TIMER_BEGIN("sendmsg", 1); - do { - /* TODO(klempner): Cork if this is a partial write */ - GRPC_STATS_INC_SYSCALL_WRITE(exec_ctx); - sent_length = sendmsg(tcp->fd, &msg, SENDMSG_FLAGS); - } while (sent_length < 0 && errno == EINTR); - GPR_TIMER_END("sendmsg", 0); - - if (sent_length < 0) { - if (errno == EAGAIN) { - tcp->outgoing_slice_idx = unwind_slice_idx; - tcp->outgoing_byte_idx = unwind_byte_idx; - return false; - } else if (errno == EPIPE) { - *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNAVAILABLE); - return true; - } else { - *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp); - return true; - } - } - - GPR_ASSERT(tcp->outgoing_byte_idx == 0); - trailing = sending_length - (size_t)sent_length; - while (trailing > 0) { - size_t slice_length; - - tcp->outgoing_slice_idx--; - slice_length = GRPC_SLICE_LENGTH( - tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]); - if (slice_length > trailing) { - tcp->outgoing_byte_idx = slice_length - trailing; - break; - } else { - trailing -= slice_length; - } - } - - if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { - *error = GRPC_ERROR_NONE; - return true; - } - }; -} - -static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, - grpc_error *error) { - grpc_tcp *tcp = (grpc_tcp *)arg; - grpc_closure *cb; - - if (error != GRPC_ERROR_NONE) { - cb = tcp->write_cb; - tcp->write_cb = NULL; - cb->cb(exec_ctx, cb->cb_arg, error); - TCP_UNREF(exec_ctx, tcp, "write"); - return; - } - - if (!tcp_flush(exec_ctx, tcp, &error)) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "write: delayed"); - } - notify_on_write(exec_ctx, tcp); - } else { - cb = tcp->write_cb; - tcp->write_cb = NULL; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "write: %s", str); - } - - GRPC_CLOSURE_RUN(exec_ctx, cb, error); - TCP_UNREF(exec_ctx, tcp, "write"); - } -} - -static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_slice_buffer *buf, grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_error *error = GRPC_ERROR_NONE; - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - size_t i; - - for (i = 0; i < buf->count; i++) { - char *data = - grpc_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); - gpr_free(data); - } - } - - GPR_TIMER_BEGIN("tcp_write", 0); - GPR_ASSERT(tcp->write_cb == NULL); - - if (buf->length == 0) { - GPR_TIMER_END("tcp_write", 0); - GRPC_CLOSURE_SCHED( - exec_ctx, cb, - grpc_fd_is_shutdown(tcp->em_fd) - ? tcp_annotate_error(GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"), - tcp) - : GRPC_ERROR_NONE); - return; - } - tcp->outgoing_buffer = buf; - tcp->outgoing_slice_idx = 0; - tcp->outgoing_byte_idx = 0; - - if (!tcp_flush(exec_ctx, tcp, &error)) { - TCP_REF(tcp, "write"); - tcp->write_cb = cb; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "write: delayed"); - } - notify_on_write(exec_ctx, tcp); - } else { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "write: %s", str); - } - GRPC_CLOSURE_SCHED(exec_ctx, cb, error); - } - - GPR_TIMER_END("tcp_write", 0); -} - -static void tcp_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset *pollset) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_pollset_add_fd(exec_ctx, pollset, tcp->em_fd); -} - -static void tcp_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset_set *pollset_set) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_pollset_set_add_fd(exec_ctx, pollset_set, tcp->em_fd); -} - -static char *tcp_get_peer(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return gpr_strdup(tcp->peer_string); -} - -static int tcp_get_fd(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return tcp->fd; -} - -static grpc_resource_user *tcp_get_resource_user(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return tcp->resource_user; -} - -static const grpc_endpoint_vtable vtable = { - tcp_read, tcp_write, tcp_add_to_pollset, tcp_add_to_pollset_set, - tcp_shutdown, tcp_destroy, tcp_get_resource_user, tcp_get_peer, - tcp_get_fd}; - -#define MAX_CHUNK_SIZE 32 * 1024 * 1024 - -grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_fd *em_fd, - const grpc_channel_args *channel_args, - const char *peer_string) { - int tcp_read_chunk_size = GRPC_TCP_DEFAULT_READ_SLICE_SIZE; - int tcp_max_read_chunk_size = 4 * 1024 * 1024; - int tcp_min_read_chunk_size = 256; - grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); - if (channel_args != NULL) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == - strcmp(channel_args->args[i].key, GRPC_ARG_TCP_READ_CHUNK_SIZE)) { - grpc_integer_options options = {(int)tcp_read_chunk_size, 1, - MAX_CHUNK_SIZE}; - tcp_read_chunk_size = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE)) { - grpc_integer_options options = {(int)tcp_read_chunk_size, 1, - MAX_CHUNK_SIZE}; - tcp_min_read_chunk_size = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - } else if (0 == strcmp(channel_args->args[i].key, - GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE)) { - grpc_integer_options options = {(int)tcp_read_chunk_size, 1, - MAX_CHUNK_SIZE}; - tcp_max_read_chunk_size = - grpc_channel_arg_get_integer(&channel_args->args[i], options); - } else if (0 == - strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - resource_quota = grpc_resource_quota_ref_internal( - (grpc_resource_quota *)channel_args->args[i].value.pointer.p); - } - } - } - - if (tcp_min_read_chunk_size > tcp_max_read_chunk_size) { - tcp_min_read_chunk_size = tcp_max_read_chunk_size; - } - tcp_read_chunk_size = GPR_CLAMP(tcp_read_chunk_size, tcp_min_read_chunk_size, - tcp_max_read_chunk_size); - - grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); - tcp->base.vtable = &vtable; - tcp->peer_string = gpr_strdup(peer_string); - tcp->fd = grpc_fd_wrapped_fd(em_fd); - tcp->read_cb = NULL; - tcp->write_cb = NULL; - tcp->release_fd_cb = NULL; - tcp->release_fd = NULL; - tcp->incoming_buffer = NULL; - tcp->target_length = (double)tcp_read_chunk_size; - tcp->min_read_chunk_size = tcp_min_read_chunk_size; - tcp->max_read_chunk_size = tcp_max_read_chunk_size; - tcp->bytes_read_this_round = 0; - tcp->finished_edge = true; - /* paired with unref in grpc_tcp_destroy */ - gpr_ref_init(&tcp->refcount, 1); - gpr_atm_no_barrier_store(&tcp->shutdown_count, 0); - tcp->em_fd = em_fd; - grpc_slice_buffer_init(&tcp->last_read_buffer); - tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); - grpc_resource_user_slice_allocator_init( - &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); - /* Tell network status tracker about new endpoint */ - grpc_network_status_register_endpoint(&tcp->base); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - - return &tcp->base; -} - -int grpc_tcp_fd(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - GPR_ASSERT(ep->vtable == &vtable); - return grpc_fd_wrapped_fd(tcp->em_fd); -} - -void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - int *fd, grpc_closure *done) { - grpc_network_status_unregister_endpoint(ep); - grpc_tcp *tcp = (grpc_tcp *)ep; - GPR_ASSERT(ep->vtable == &vtable); - tcp->release_fd = fd; - tcp->release_fd_cb = done; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &tcp->last_read_buffer); - TCP_UNREF(exec_ctx, tcp, "destroy"); -} - -#endif diff --git a/src/core/lib/iomgr/tcp_posix.cc b/src/core/lib/iomgr/tcp_posix.cc new file mode 100644 index 0000000000..7e271294fd --- /dev/null +++ b/src/core/lib/iomgr/tcp_posix.cc @@ -0,0 +1,819 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/tcp_posix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +#ifdef GRPC_HAVE_MSG_NOSIGNAL +#define SENDMSG_FLAGS MSG_NOSIGNAL +#else +#define SENDMSG_FLAGS 0 +#endif + +#ifdef GRPC_MSG_IOVLEN_TYPE +typedef GRPC_MSG_IOVLEN_TYPE msg_iovlen_type; +#else +typedef size_t msg_iovlen_type; +#endif + +grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp"); + +typedef struct { + grpc_endpoint base; + grpc_fd *em_fd; + int fd; + bool finished_edge; + double target_length; + double bytes_read_this_round; + gpr_refcount refcount; + gpr_atm shutdown_count; + + int min_read_chunk_size; + int max_read_chunk_size; + + /* garbage after the last read */ + grpc_slice_buffer last_read_buffer; + + grpc_slice_buffer *incoming_buffer; + grpc_slice_buffer *outgoing_buffer; + /** slice within outgoing_buffer to write next */ + size_t outgoing_slice_idx; + /** byte within outgoing_buffer->slices[outgoing_slice_idx] to write next */ + size_t outgoing_byte_idx; + + grpc_closure *read_cb; + grpc_closure *write_cb; + grpc_closure *release_fd_cb; + int *release_fd; + + grpc_closure read_done_closure; + grpc_closure write_done_closure; + + char *peer_string; + + grpc_resource_user *resource_user; + grpc_resource_user_slice_allocator slice_allocator; +} grpc_tcp; + +typedef struct backup_poller { + gpr_mu *pollset_mu; + grpc_closure run_poller; +} backup_poller; + +#define BACKUP_POLLER_POLLSET(b) ((grpc_pollset *)((b) + 1)) + +static gpr_atm g_uncovered_notifications_pending; +static gpr_atm g_backup_poller; /* backup_poller* */ + +static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + grpc_error *error); +static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + grpc_error *error); +static void tcp_drop_uncovered_then_handle_write(grpc_exec_ctx *exec_ctx, + void *arg /* grpc_tcp */, + grpc_error *error); + +static void done_poller(grpc_exec_ctx *exec_ctx, void *bp, + grpc_error *error_ignored) { + backup_poller *p = (backup_poller *)bp; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p destroy", p); + } + grpc_pollset_destroy(exec_ctx, BACKUP_POLLER_POLLSET(p)); + gpr_free(p); +} + +static void run_poller(grpc_exec_ctx *exec_ctx, void *bp, + grpc_error *error_ignored) { + backup_poller *p = (backup_poller *)bp; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p run", p); + } + gpr_mu_lock(p->pollset_mu); + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + gpr_timespec deadline = + gpr_time_add(now, gpr_time_from_seconds(10, GPR_TIMESPAN)); + GRPC_STATS_INC_TCP_BACKUP_POLLER_POLLS(exec_ctx); + GRPC_LOG_IF_ERROR("backup_poller:pollset_work", + grpc_pollset_work(exec_ctx, BACKUP_POLLER_POLLSET(p), NULL, + now, deadline)); + gpr_mu_unlock(p->pollset_mu); + /* last "uncovered" notification is the ref that keeps us polling, if we get + * there try a cas to release it */ + if (gpr_atm_no_barrier_load(&g_uncovered_notifications_pending) == 1 && + gpr_atm_full_cas(&g_uncovered_notifications_pending, 1, 0)) { + gpr_mu_lock(p->pollset_mu); + bool cas_ok = gpr_atm_full_cas(&g_backup_poller, (gpr_atm)p, 0); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p done cas_ok=%d", p, cas_ok); + } + gpr_mu_unlock(p->pollset_mu); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p shutdown", p); + } + grpc_pollset_shutdown(exec_ctx, BACKUP_POLLER_POLLSET(p), + GRPC_CLOSURE_INIT(&p->run_poller, done_poller, p, + grpc_schedule_on_exec_ctx)); + } else { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p reschedule", p); + } + GRPC_CLOSURE_SCHED(exec_ctx, &p->run_poller, GRPC_ERROR_NONE); + } +} + +static void drop_uncovered(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + backup_poller *p = (backup_poller *)gpr_atm_acq_load(&g_backup_poller); + gpr_atm old_count = + gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, -1); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p uncover cnt %d->%d", p, (int)old_count, + (int)old_count - 1); + } + GPR_ASSERT(old_count != 1); +} + +static void cover_self(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + backup_poller *p; + gpr_atm old_count = + gpr_atm_no_barrier_fetch_add(&g_uncovered_notifications_pending, 2); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER: cover cnt %d->%d", (int)old_count, + 2 + (int)old_count); + } + if (old_count == 0) { + GRPC_STATS_INC_TCP_BACKUP_POLLERS_CREATED(exec_ctx); + p = (backup_poller *)gpr_malloc(sizeof(*p) + grpc_pollset_size()); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p create", p); + } + grpc_pollset_init(BACKUP_POLLER_POLLSET(p), &p->pollset_mu); + gpr_atm_rel_store(&g_backup_poller, (gpr_atm)p); + GRPC_CLOSURE_SCHED( + exec_ctx, + GRPC_CLOSURE_INIT(&p->run_poller, run_poller, p, + grpc_executor_scheduler(GRPC_EXECUTOR_LONG)), + GRPC_ERROR_NONE); + } else { + while ((p = (backup_poller *)gpr_atm_acq_load(&g_backup_poller)) == NULL) { + // spin waiting for backup poller + } + } + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "BACKUP_POLLER:%p add %p", p, tcp); + } + grpc_pollset_add_fd(exec_ctx, BACKUP_POLLER_POLLSET(p), tcp->em_fd); + if (old_count != 0) { + drop_uncovered(exec_ctx, tcp); + } +} + +static void notify_on_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p notify_on_read", tcp); + } + GRPC_CLOSURE_INIT(&tcp->read_done_closure, tcp_handle_read, tcp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(exec_ctx, tcp->em_fd, &tcp->read_done_closure); +} + +static void notify_on_write(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p notify_on_write", tcp); + } + cover_self(exec_ctx, tcp); + GRPC_CLOSURE_INIT(&tcp->write_done_closure, + tcp_drop_uncovered_then_handle_write, tcp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_write(exec_ctx, tcp->em_fd, &tcp->write_done_closure); +} + +static void tcp_drop_uncovered_then_handle_write(grpc_exec_ctx *exec_ctx, + void *arg, grpc_error *error) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p got_write: %s", arg, grpc_error_string(error)); + } + drop_uncovered(exec_ctx, (grpc_tcp *)arg); + tcp_handle_write(exec_ctx, arg, error); +} + +static void add_to_estimate(grpc_tcp *tcp, size_t bytes) { + tcp->bytes_read_this_round += (double)bytes; +} + +static void finish_estimate(grpc_tcp *tcp) { + /* If we read >80% of the target buffer in one read loop, increase the size + of the target buffer to either the amount read, or twice its previous + value */ + if (tcp->bytes_read_this_round > tcp->target_length * 0.8) { + tcp->target_length = + GPR_MAX(2 * tcp->target_length, tcp->bytes_read_this_round); + } else { + tcp->target_length = + 0.99 * tcp->target_length + 0.01 * tcp->bytes_read_this_round; + } + tcp->bytes_read_this_round = 0; +} + +static size_t get_target_read_size(grpc_tcp *tcp) { + grpc_resource_quota *rq = grpc_resource_user_quota(tcp->resource_user); + double pressure = grpc_resource_quota_get_memory_pressure(rq); + double target = + tcp->target_length * (pressure > 0.8 ? (1.0 - pressure) / 0.2 : 1.0); + size_t sz = (((size_t)GPR_CLAMP(target, tcp->min_read_chunk_size, + tcp->max_read_chunk_size)) + + 255) & + ~(size_t)255; + /* don't use more than 1/16th of the overall resource quota for a single read + * alloc */ + size_t rqmax = grpc_resource_quota_peek_size(rq); + if (sz > rqmax / 16 && rqmax > 1024) { + sz = rqmax / 16; + } + return sz; +} + +static grpc_error *tcp_annotate_error(grpc_error *src_error, grpc_tcp *tcp) { + return grpc_error_set_str( + grpc_error_set_int(src_error, GRPC_ERROR_INT_FD, tcp->fd), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(tcp->peer_string)); +} + +static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + grpc_error *error); +static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + grpc_error *error); + +static void tcp_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_error *why) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_fd_shutdown(exec_ctx, tcp->em_fd, why); + grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); +} + +static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + grpc_fd_orphan(exec_ctx, tcp->em_fd, tcp->release_fd_cb, tcp->release_fd, + false /* already_closed */, "tcp_unref_orphan"); + grpc_slice_buffer_destroy_internal(exec_ctx, &tcp->last_read_buffer); + grpc_resource_user_unref(exec_ctx, tcp->resource_user); + gpr_free(tcp->peer_string); + gpr_free(tcp); +} + +#ifndef NDEBUG +#define TCP_UNREF(cl, tcp, reason) \ + tcp_unref((cl), (tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, + const char *reason, const char *file, int line) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val + 1); + } + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(cl, tcp, reason) tcp_unref((cl), (tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void tcp_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &tcp->last_read_buffer); + TCP_UNREF(exec_ctx, tcp, "destroy"); +} + +static void call_read_cb(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, + grpc_error *error) { + grpc_closure *cb = tcp->read_cb; + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p call_cb %p %p:%p", tcp, cb, cb->cb, cb->cb_arg); + size_t i; + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "read: error=%s", str); + + for (i = 0; i < tcp->incoming_buffer->count; i++) { + char *dump = grpc_dump_slice(tcp->incoming_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, dump); + gpr_free(dump); + } + } + + tcp->read_cb = NULL; + tcp->incoming_buffer = NULL; + GRPC_CLOSURE_RUN(exec_ctx, cb, error); +} + +#define MAX_READ_IOVEC 4 +static void tcp_do_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + struct msghdr msg; + struct iovec iov[MAX_READ_IOVEC]; + ssize_t read_bytes; + size_t i; + + GPR_ASSERT(!tcp->finished_edge); + GPR_ASSERT(tcp->incoming_buffer->count <= MAX_READ_IOVEC); + GPR_TIMER_BEGIN("tcp_continue_read", 0); + + for (i = 0; i < tcp->incoming_buffer->count; i++) { + iov[i].iov_base = GRPC_SLICE_START_PTR(tcp->incoming_buffer->slices[i]); + iov[i].iov_len = GRPC_SLICE_LENGTH(tcp->incoming_buffer->slices[i]); + } + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = (msg_iovlen_type)tcp->incoming_buffer->count; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + GRPC_STATS_INC_TCP_READ_OFFER(exec_ctx, tcp->incoming_buffer->length); + GRPC_STATS_INC_TCP_READ_OFFER_IOV_SIZE(exec_ctx, tcp->incoming_buffer->count); + + GPR_TIMER_BEGIN("recvmsg", 0); + do { + GRPC_STATS_INC_SYSCALL_READ(exec_ctx); + read_bytes = recvmsg(tcp->fd, &msg, 0); + } while (read_bytes < 0 && errno == EINTR); + GPR_TIMER_END("recvmsg", read_bytes >= 0); + + if (read_bytes < 0) { + /* NB: After calling call_read_cb a parallel call of the read handler may + * be running. */ + if (errno == EAGAIN) { + finish_estimate(tcp); + /* We've consumed the edge, request a new one */ + notify_on_read(exec_ctx, tcp); + } else { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + tcp->incoming_buffer); + call_read_cb(exec_ctx, tcp, + tcp_annotate_error(GRPC_OS_ERROR(errno, "recvmsg"), tcp)); + TCP_UNREF(exec_ctx, tcp, "read"); + } + } else if (read_bytes == 0) { + /* 0 read size ==> end of stream */ + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer); + call_read_cb( + exec_ctx, tcp, + tcp_annotate_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Socket closed"), tcp)); + TCP_UNREF(exec_ctx, tcp, "read"); + } else { + GRPC_STATS_INC_TCP_READ_SIZE(exec_ctx, read_bytes); + add_to_estimate(tcp, (size_t)read_bytes); + GPR_ASSERT((size_t)read_bytes <= tcp->incoming_buffer->length); + if ((size_t)read_bytes < tcp->incoming_buffer->length) { + grpc_slice_buffer_trim_end( + tcp->incoming_buffer, + tcp->incoming_buffer->length - (size_t)read_bytes, + &tcp->last_read_buffer); + } + GPR_ASSERT((size_t)read_bytes == tcp->incoming_buffer->length); + call_read_cb(exec_ctx, tcp, GRPC_ERROR_NONE); + TCP_UNREF(exec_ctx, tcp, "read"); + } + + GPR_TIMER_END("tcp_continue_read", 0); +} + +static void tcp_read_allocation_done(grpc_exec_ctx *exec_ctx, void *tcpp, + grpc_error *error) { + grpc_tcp *tcp = (grpc_tcp *)tcpp; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p read_allocation_done: %s", tcp, + grpc_error_string(error)); + } + if (error != GRPC_ERROR_NONE) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer); + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + &tcp->last_read_buffer); + call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error)); + TCP_UNREF(exec_ctx, tcp, "read"); + } else { + tcp_do_read(exec_ctx, tcp); + } +} + +static void tcp_continue_read(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + size_t target_read_size = get_target_read_size(tcp); + if (tcp->incoming_buffer->length < target_read_size && + tcp->incoming_buffer->count < MAX_READ_IOVEC) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p alloc_slices", tcp); + } + grpc_resource_user_alloc_slices(exec_ctx, &tcp->slice_allocator, + target_read_size, 1, tcp->incoming_buffer); + } else { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p do_read", tcp); + } + tcp_do_read(exec_ctx, tcp); + } +} + +static void tcp_handle_read(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + grpc_error *error) { + grpc_tcp *tcp = (grpc_tcp *)arg; + GPR_ASSERT(!tcp->finished_edge); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "TCP:%p got_read: %s", tcp, grpc_error_string(error)); + } + + if (error != GRPC_ERROR_NONE) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, tcp->incoming_buffer); + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, + &tcp->last_read_buffer); + call_read_cb(exec_ctx, tcp, GRPC_ERROR_REF(error)); + TCP_UNREF(exec_ctx, tcp, "read"); + } else { + tcp_continue_read(exec_ctx, tcp); + } +} + +static void tcp_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *incoming_buffer, grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + GPR_ASSERT(tcp->read_cb == NULL); + tcp->read_cb = cb; + tcp->incoming_buffer = incoming_buffer; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, incoming_buffer); + grpc_slice_buffer_swap(incoming_buffer, &tcp->last_read_buffer); + TCP_REF(tcp, "read"); + if (tcp->finished_edge) { + tcp->finished_edge = false; + notify_on_read(exec_ctx, tcp); + } else { + GRPC_CLOSURE_SCHED(exec_ctx, &tcp->read_done_closure, GRPC_ERROR_NONE); + } +} + +/* returns true if done, false if pending; if returning true, *error is set */ +#define MAX_WRITE_IOVEC 1000 +static bool tcp_flush(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, + grpc_error **error) { + struct msghdr msg; + struct iovec iov[MAX_WRITE_IOVEC]; + msg_iovlen_type iov_size; + ssize_t sent_length; + size_t sending_length; + size_t trailing; + size_t unwind_slice_idx; + size_t unwind_byte_idx; + + for (;;) { + sending_length = 0; + unwind_slice_idx = tcp->outgoing_slice_idx; + unwind_byte_idx = tcp->outgoing_byte_idx; + for (iov_size = 0; tcp->outgoing_slice_idx != tcp->outgoing_buffer->count && + iov_size != MAX_WRITE_IOVEC; + iov_size++) { + iov[iov_size].iov_base = + GRPC_SLICE_START_PTR( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) + + tcp->outgoing_byte_idx; + iov[iov_size].iov_len = + GRPC_SLICE_LENGTH( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]) - + tcp->outgoing_byte_idx; + sending_length += iov[iov_size].iov_len; + tcp->outgoing_slice_idx++; + tcp->outgoing_byte_idx = 0; + } + GPR_ASSERT(iov_size > 0); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = iov_size; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + GRPC_STATS_INC_TCP_WRITE_SIZE(exec_ctx, sending_length); + GRPC_STATS_INC_TCP_WRITE_IOV_SIZE(exec_ctx, iov_size); + + GPR_TIMER_BEGIN("sendmsg", 1); + do { + /* TODO(klempner): Cork if this is a partial write */ + GRPC_STATS_INC_SYSCALL_WRITE(exec_ctx); + sent_length = sendmsg(tcp->fd, &msg, SENDMSG_FLAGS); + } while (sent_length < 0 && errno == EINTR); + GPR_TIMER_END("sendmsg", 0); + + if (sent_length < 0) { + if (errno == EAGAIN) { + tcp->outgoing_slice_idx = unwind_slice_idx; + tcp->outgoing_byte_idx = unwind_byte_idx; + return false; + } else if (errno == EPIPE) { + *error = grpc_error_set_int(GRPC_OS_ERROR(errno, "sendmsg"), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAVAILABLE); + return true; + } else { + *error = tcp_annotate_error(GRPC_OS_ERROR(errno, "sendmsg"), tcp); + return true; + } + } + + GPR_ASSERT(tcp->outgoing_byte_idx == 0); + trailing = sending_length - (size_t)sent_length; + while (trailing > 0) { + size_t slice_length; + + tcp->outgoing_slice_idx--; + slice_length = GRPC_SLICE_LENGTH( + tcp->outgoing_buffer->slices[tcp->outgoing_slice_idx]); + if (slice_length > trailing) { + tcp->outgoing_byte_idx = slice_length - trailing; + break; + } else { + trailing -= slice_length; + } + } + + if (tcp->outgoing_slice_idx == tcp->outgoing_buffer->count) { + *error = GRPC_ERROR_NONE; + return true; + } + }; +} + +static void tcp_handle_write(grpc_exec_ctx *exec_ctx, void *arg /* grpc_tcp */, + grpc_error *error) { + grpc_tcp *tcp = (grpc_tcp *)arg; + grpc_closure *cb; + + if (error != GRPC_ERROR_NONE) { + cb = tcp->write_cb; + tcp->write_cb = NULL; + cb->cb(exec_ctx, cb->cb_arg, error); + TCP_UNREF(exec_ctx, tcp, "write"); + return; + } + + if (!tcp_flush(exec_ctx, tcp, &error)) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "write: delayed"); + } + notify_on_write(exec_ctx, tcp); + } else { + cb = tcp->write_cb; + tcp->write_cb = NULL; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write: %s", str); + } + + GRPC_CLOSURE_RUN(exec_ctx, cb, error); + TCP_UNREF(exec_ctx, tcp, "write"); + } +} + +static void tcp_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *buf, grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_error *error = GRPC_ERROR_NONE; + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + size_t i; + + for (i = 0; i < buf->count; i++) { + char *data = + grpc_dump_slice(buf->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); + gpr_free(data); + } + } + + GPR_TIMER_BEGIN("tcp_write", 0); + GPR_ASSERT(tcp->write_cb == NULL); + + if (buf->length == 0) { + GPR_TIMER_END("tcp_write", 0); + GRPC_CLOSURE_SCHED( + exec_ctx, cb, + grpc_fd_is_shutdown(tcp->em_fd) + ? tcp_annotate_error(GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"), + tcp) + : GRPC_ERROR_NONE); + return; + } + tcp->outgoing_buffer = buf; + tcp->outgoing_slice_idx = 0; + tcp->outgoing_byte_idx = 0; + + if (!tcp_flush(exec_ctx, tcp, &error)) { + TCP_REF(tcp, "write"); + tcp->write_cb = cb; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "write: delayed"); + } + notify_on_write(exec_ctx, tcp); + } else { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write: %s", str); + } + GRPC_CLOSURE_SCHED(exec_ctx, cb, error); + } + + GPR_TIMER_END("tcp_write", 0); +} + +static void tcp_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_pollset_add_fd(exec_ctx, pollset, tcp->em_fd); +} + +static void tcp_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pollset_set) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_pollset_set_add_fd(exec_ctx, pollset_set, tcp->em_fd); +} + +static char *tcp_get_peer(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return gpr_strdup(tcp->peer_string); +} + +static int tcp_get_fd(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return tcp->fd; +} + +static grpc_resource_user *tcp_get_resource_user(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return tcp->resource_user; +} + +static const grpc_endpoint_vtable vtable = { + tcp_read, tcp_write, tcp_add_to_pollset, tcp_add_to_pollset_set, + tcp_shutdown, tcp_destroy, tcp_get_resource_user, tcp_get_peer, + tcp_get_fd}; + +#define MAX_CHUNK_SIZE 32 * 1024 * 1024 + +grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_fd *em_fd, + const grpc_channel_args *channel_args, + const char *peer_string) { + int tcp_read_chunk_size = GRPC_TCP_DEFAULT_READ_SLICE_SIZE; + int tcp_max_read_chunk_size = 4 * 1024 * 1024; + int tcp_min_read_chunk_size = 256; + grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); + if (channel_args != NULL) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_TCP_READ_CHUNK_SIZE)) { + grpc_integer_options options = {(int)tcp_read_chunk_size, 1, + MAX_CHUNK_SIZE}; + tcp_read_chunk_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_TCP_MIN_READ_CHUNK_SIZE)) { + grpc_integer_options options = {(int)tcp_read_chunk_size, 1, + MAX_CHUNK_SIZE}; + tcp_min_read_chunk_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } else if (0 == strcmp(channel_args->args[i].key, + GRPC_ARG_TCP_MAX_READ_CHUNK_SIZE)) { + grpc_integer_options options = {(int)tcp_read_chunk_size, 1, + MAX_CHUNK_SIZE}; + tcp_max_read_chunk_size = + grpc_channel_arg_get_integer(&channel_args->args[i], options); + } else if (0 == + strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + (grpc_resource_quota *)channel_args->args[i].value.pointer.p); + } + } + } + + if (tcp_min_read_chunk_size > tcp_max_read_chunk_size) { + tcp_min_read_chunk_size = tcp_max_read_chunk_size; + } + tcp_read_chunk_size = GPR_CLAMP(tcp_read_chunk_size, tcp_min_read_chunk_size, + tcp_max_read_chunk_size); + + grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); + tcp->base.vtable = &vtable; + tcp->peer_string = gpr_strdup(peer_string); + tcp->fd = grpc_fd_wrapped_fd(em_fd); + tcp->read_cb = NULL; + tcp->write_cb = NULL; + tcp->release_fd_cb = NULL; + tcp->release_fd = NULL; + tcp->incoming_buffer = NULL; + tcp->target_length = (double)tcp_read_chunk_size; + tcp->min_read_chunk_size = tcp_min_read_chunk_size; + tcp->max_read_chunk_size = tcp_max_read_chunk_size; + tcp->bytes_read_this_round = 0; + tcp->finished_edge = true; + /* paired with unref in grpc_tcp_destroy */ + gpr_ref_init(&tcp->refcount, 1); + gpr_atm_no_barrier_store(&tcp->shutdown_count, 0); + tcp->em_fd = em_fd; + grpc_slice_buffer_init(&tcp->last_read_buffer); + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + grpc_resource_user_slice_allocator_init( + &tcp->slice_allocator, tcp->resource_user, tcp_read_allocation_done, tcp); + /* Tell network status tracker about new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + + return &tcp->base; +} + +int grpc_tcp_fd(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + GPR_ASSERT(ep->vtable == &vtable); + return grpc_fd_wrapped_fd(tcp->em_fd); +} + +void grpc_tcp_destroy_and_release_fd(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + int *fd, grpc_closure *done) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp *tcp = (grpc_tcp *)ep; + GPR_ASSERT(ep->vtable == &vtable); + tcp->release_fd = fd; + tcp->release_fd_cb = done; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &tcp->last_read_buffer); + TCP_UNREF(exec_ctx, tcp, "destroy"); +} + +#endif diff --git a/src/core/lib/iomgr/tcp_server_posix.c b/src/core/lib/iomgr/tcp_server_posix.c deleted file mode 100644 index 06612d639c..0000000000 --- a/src/core/lib/iomgr/tcp_server_posix.c +++ /dev/null @@ -1,565 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/tcp_server.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" -#include "src/core/lib/iomgr/tcp_posix.h" -#include "src/core/lib/iomgr/tcp_server_utils_posix.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/support/string.h" - -static gpr_once check_init = GPR_ONCE_INIT; -static bool has_so_reuseport = false; - -static void init(void) { -#ifndef GPR_MANYLINUX1 - int s = socket(AF_INET, SOCK_STREAM, 0); - if (s >= 0) { - has_so_reuseport = GRPC_LOG_IF_ERROR("check for SO_REUSEPORT", - grpc_set_socket_reuse_port(s, 1)); - close(s); - } -#endif -} - -grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, - grpc_closure *shutdown_complete, - const grpc_channel_args *args, - grpc_tcp_server **server) { - gpr_once_init(&check_init, init); - - grpc_tcp_server *s = (grpc_tcp_server *)gpr_zalloc(sizeof(grpc_tcp_server)); - s->so_reuseport = has_so_reuseport; - s->expand_wildcard_addrs = false; - for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) { - if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) { - if (args->args[i].type == GRPC_ARG_INTEGER) { - s->so_reuseport = - has_so_reuseport && (args->args[i].value.integer != 0); - } else { - gpr_free(s); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING(GRPC_ARG_ALLOW_REUSEPORT - " must be an integer"); - } - } else if (0 == strcmp(GRPC_ARG_EXPAND_WILDCARD_ADDRS, args->args[i].key)) { - if (args->args[i].type == GRPC_ARG_INTEGER) { - s->expand_wildcard_addrs = (args->args[i].value.integer != 0); - } else { - gpr_free(s); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - GRPC_ARG_EXPAND_WILDCARD_ADDRS " must be an integer"); - } - } - } - gpr_ref_init(&s->refs, 1); - gpr_mu_init(&s->mu); - s->active_ports = 0; - s->destroyed_ports = 0; - s->shutdown = false; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->head = NULL; - s->tail = NULL; - s->nports = 0; - s->channel_args = grpc_channel_args_copy(args); - gpr_atm_no_barrier_store(&s->next_pollset_to_assign, 0); - *server = s; - return GRPC_ERROR_NONE; -} - -static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - gpr_mu_lock(&s->mu); - GPR_ASSERT(s->shutdown); - gpr_mu_unlock(&s->mu); - if (s->shutdown_complete != NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); - } - - gpr_mu_destroy(&s->mu); - - while (s->head) { - grpc_tcp_listener *sp = s->head; - s->head = sp->next; - gpr_free(sp); - } - grpc_channel_args_destroy(exec_ctx, s->channel_args); - - gpr_free(s); -} - -static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, - grpc_error *error) { - grpc_tcp_server *s = (grpc_tcp_server *)server; - gpr_mu_lock(&s->mu); - s->destroyed_ports++; - if (s->destroyed_ports == s->nports) { - gpr_mu_unlock(&s->mu); - finish_shutdown(exec_ctx, s); - } else { - GPR_ASSERT(s->destroyed_ports < s->nports); - gpr_mu_unlock(&s->mu); - } -} - -/* called when all listening endpoints have been shutdown, so no further - events will be received on them - at this point it's safe to destroy - things */ -static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - /* delete ALL the things */ - gpr_mu_lock(&s->mu); - - GPR_ASSERT(s->shutdown); - - if (s->head) { - grpc_tcp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_unlink_if_unix_domain_socket(&sp->addr); - GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, - grpc_schedule_on_exec_ctx); - grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, - false /* already_closed */, "tcp_listener_shutdown"); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - finish_shutdown(exec_ctx, s); - } -} - -static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - gpr_mu_lock(&s->mu); - - GPR_ASSERT(!s->shutdown); - s->shutdown = true; - - /* shutdown all fd's */ - if (s->active_ports) { - grpc_tcp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_fd_shutdown(exec_ctx, sp->emfd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Server destroyed")); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - deactivated_all_ports(exec_ctx, s); - } -} - -/* event manager callback when reads are ready */ -static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) { - grpc_tcp_listener *sp = (grpc_tcp_listener *)arg; - grpc_pollset *read_notifier_pollset; - if (err != GRPC_ERROR_NONE) { - goto error; - } - - read_notifier_pollset = - sp->server->pollsets[(size_t)gpr_atm_no_barrier_fetch_add( - &sp->server->next_pollset_to_assign, 1) % - sp->server->pollset_count]; - - /* loop until accept4 returns EAGAIN, and then re-arm notification */ - for (;;) { - grpc_resolved_address addr; - char *addr_str; - char *name; - addr.len = sizeof(struct sockaddr_storage); - /* Note: If we ever decide to return this address to the user, remember to - strip off the ::ffff:0.0.0.0/96 prefix first. */ - int fd = grpc_accept4(sp->fd, &addr, 1, 1); - if (fd < 0) { - switch (errno) { - case EINTR: - continue; - case EAGAIN: - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); - return; - default: - gpr_mu_lock(&sp->server->mu); - if (!sp->server->shutdown_listeners) { - gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno)); - } else { - /* if we have shutdown listeners, accept4 could fail, and we - needn't notify users */ - } - gpr_mu_unlock(&sp->server->mu); - goto error; - } - } - - grpc_set_socket_no_sigpipe_if_possible(fd); - - addr_str = grpc_sockaddr_to_uri(&addr); - gpr_asprintf(&name, "tcp-server-connection:%s", addr_str); - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str); - } - - grpc_fd *fdobj = grpc_fd_create(fd, name); - - grpc_pollset_add_fd(exec_ctx, read_notifier_pollset, fdobj); - - // Create acceptor. - grpc_tcp_server_acceptor *acceptor = - (grpc_tcp_server_acceptor *)gpr_malloc(sizeof(*acceptor)); - acceptor->from_server = sp->server; - acceptor->port_index = sp->port_index; - acceptor->fd_index = sp->fd_index; - - sp->server->on_accept_cb( - exec_ctx, sp->server->on_accept_cb_arg, - grpc_tcp_create(exec_ctx, fdobj, sp->server->channel_args, addr_str), - read_notifier_pollset, acceptor); - - gpr_free(name); - gpr_free(addr_str); - } - - GPR_UNREACHABLE_CODE(return ); - -error: - gpr_mu_lock(&sp->server->mu); - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(exec_ctx, sp->server); - } else { - gpr_mu_unlock(&sp->server->mu); - } -} - -/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ -static grpc_error *add_wildcard_addrs_to_server(grpc_tcp_server *s, - unsigned port_index, - int requested_port, - int *out_port) { - grpc_resolved_address wild4; - grpc_resolved_address wild6; - unsigned fd_index = 0; - grpc_dualstack_mode dsmode; - grpc_tcp_listener *sp = NULL; - grpc_tcp_listener *sp2 = NULL; - grpc_error *v6_err = GRPC_ERROR_NONE; - grpc_error *v4_err = GRPC_ERROR_NONE; - *out_port = -1; - - if (grpc_tcp_server_have_ifaddrs() && s->expand_wildcard_addrs) { - return grpc_tcp_server_add_all_local_addrs(s, port_index, requested_port, - out_port); - } - - grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6); - /* Try listening on IPv6 first. */ - if ((v6_err = grpc_tcp_server_add_addr(s, &wild6, port_index, fd_index, - &dsmode, &sp)) == GRPC_ERROR_NONE) { - ++fd_index; - requested_port = *out_port = sp->port; - if (dsmode == GRPC_DSMODE_DUALSTACK || dsmode == GRPC_DSMODE_IPV4) { - return GRPC_ERROR_NONE; - } - } - /* If we got a v6-only socket or nothing, try adding 0.0.0.0. */ - grpc_sockaddr_set_port(&wild4, requested_port); - if ((v4_err = grpc_tcp_server_add_addr(s, &wild4, port_index, fd_index, - &dsmode, &sp2)) == GRPC_ERROR_NONE) { - *out_port = sp2->port; - if (sp != NULL) { - sp2->is_sibling = 1; - sp->sibling = sp2; - } - } - if (*out_port > 0) { - if (v6_err != GRPC_ERROR_NONE) { - gpr_log(GPR_INFO, - "Failed to add :: listener, " - "the environment may not support IPv6: %s", - grpc_error_string(v6_err)); - GRPC_ERROR_UNREF(v6_err); - } - if (v4_err != GRPC_ERROR_NONE) { - gpr_log(GPR_INFO, - "Failed to add 0.0.0.0 listener, " - "the environment may not support IPv4: %s", - grpc_error_string(v4_err)); - GRPC_ERROR_UNREF(v4_err); - } - return GRPC_ERROR_NONE; - } else { - grpc_error *root_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to add any wildcard listeners"); - GPR_ASSERT(v6_err != GRPC_ERROR_NONE && v4_err != GRPC_ERROR_NONE); - root_err = grpc_error_add_child(root_err, v6_err); - root_err = grpc_error_add_child(root_err, v4_err); - return root_err; - } -} - -static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) { - grpc_tcp_listener *sp = NULL; - char *addr_str; - char *name; - grpc_error *err; - - for (grpc_tcp_listener *l = listener->next; l && l->is_sibling; l = l->next) { - l->fd_index += count; - } - - for (unsigned i = 0; i < count; i++) { - int fd = -1; - int port = -1; - grpc_dualstack_mode dsmode; - err = grpc_create_dualstack_socket(&listener->addr, SOCK_STREAM, 0, &dsmode, - &fd); - if (err != GRPC_ERROR_NONE) return err; - err = grpc_tcp_server_prepare_socket(fd, &listener->addr, true, &port); - if (err != GRPC_ERROR_NONE) return err; - listener->server->nports++; - grpc_sockaddr_to_string(&addr_str, &listener->addr, 1); - gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i); - sp = (grpc_tcp_listener *)gpr_malloc(sizeof(grpc_tcp_listener)); - sp->next = listener->next; - listener->next = sp; - /* sp (the new listener) is a sibling of 'listener' (the original - listener). */ - sp->is_sibling = 1; - sp->sibling = listener->sibling; - listener->sibling = sp; - sp->server = listener->server; - sp->fd = fd; - sp->emfd = grpc_fd_create(fd, name); - memcpy(&sp->addr, &listener->addr, sizeof(grpc_resolved_address)); - sp->port = port; - sp->port_index = listener->port_index; - sp->fd_index = listener->fd_index + count - i; - GPR_ASSERT(sp->emfd); - while (listener->server->tail->next != NULL) { - listener->server->tail = listener->server->tail->next; - } - gpr_free(addr_str); - gpr_free(name); - } - - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, - const grpc_resolved_address *addr, - int *out_port) { - grpc_tcp_listener *sp; - grpc_resolved_address sockname_temp; - grpc_resolved_address addr6_v4mapped; - int requested_port = grpc_sockaddr_get_port(addr); - unsigned port_index = 0; - grpc_dualstack_mode dsmode; - grpc_error *err; - *out_port = -1; - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - grpc_unlink_if_unix_domain_socket(addr); - - /* Check if this is a wildcard port, and if so, try to keep the port the same - as some previously created listener. */ - if (requested_port == 0) { - for (sp = s->head; sp; sp = sp->next) { - sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == getsockname(sp->fd, (struct sockaddr *)&sockname_temp.addr, - (socklen_t *)&sockname_temp.len)) { - int used_port = grpc_sockaddr_get_port(&sockname_temp); - if (used_port > 0) { - memcpy(&sockname_temp, addr, sizeof(grpc_resolved_address)); - grpc_sockaddr_set_port(&sockname_temp, used_port); - requested_port = used_port; - addr = &sockname_temp; - break; - } - } - } - } - if (grpc_sockaddr_is_wildcard(addr, &requested_port)) { - return add_wildcard_addrs_to_server(s, port_index, requested_port, - out_port); - } - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - if ((err = grpc_tcp_server_add_addr(s, addr, port_index, 0, &dsmode, &sp)) == - GRPC_ERROR_NONE) { - *out_port = sp->port; - } - return err; -} - -/* Return listener at port_index or NULL. Should only be called with s->mu - locked. */ -static grpc_tcp_listener *get_port_index(grpc_tcp_server *s, - unsigned port_index) { - unsigned num_ports = 0; - grpc_tcp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - if (!sp->is_sibling) { - if (++num_ports > port_index) { - return sp; - } - } - } - return NULL; -} - -unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server *s, - unsigned port_index) { - unsigned num_fds = 0; - gpr_mu_lock(&s->mu); - grpc_tcp_listener *sp = get_port_index(s, port_index); - for (; sp; sp = sp->sibling) { - ++num_fds; - } - gpr_mu_unlock(&s->mu); - return num_fds; -} - -int grpc_tcp_server_port_fd(grpc_tcp_server *s, unsigned port_index, - unsigned fd_index) { - gpr_mu_lock(&s->mu); - grpc_tcp_listener *sp = get_port_index(s, port_index); - for (; sp; sp = sp->sibling, --fd_index) { - if (fd_index == 0) { - gpr_mu_unlock(&s->mu); - return sp->fd; - } - } - gpr_mu_unlock(&s->mu); - return -1; -} - -void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, - grpc_pollset **pollsets, size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void *on_accept_cb_arg) { - size_t i; - grpc_tcp_listener *sp; - GPR_ASSERT(on_accept_cb); - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->on_accept_cb); - GPR_ASSERT(s->active_ports == 0); - s->on_accept_cb = on_accept_cb; - s->on_accept_cb_arg = on_accept_cb_arg; - s->pollsets = pollsets; - s->pollset_count = pollset_count; - sp = s->head; - while (sp != NULL) { - if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr) && - pollset_count > 1) { - GPR_ASSERT(GRPC_LOG_IF_ERROR( - "clone_port", clone_port(sp, (unsigned)(pollset_count - 1)))); - for (i = 0; i < pollset_count; i++) { - grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); - GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); - s->active_ports++; - sp = sp->next; - } - } else { - for (i = 0; i < pollset_count; i++) { - grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); - } - GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); - s->active_ports++; - sp = sp->next; - } - } - gpr_mu_unlock(&s->mu); -} - -grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { - gpr_ref_non_zero(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, - grpc_closure *shutdown_starting) { - gpr_mu_lock(&s->mu); - grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, - GRPC_ERROR_NONE); - gpr_mu_unlock(&s->mu); -} - -void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - if (gpr_unref(&s->refs)) { - grpc_tcp_server_shutdown_listeners(exec_ctx, s); - gpr_mu_lock(&s->mu); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &s->shutdown_starting); - gpr_mu_unlock(&s->mu); - tcp_server_destroy(exec_ctx, s); - } -} - -void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, - grpc_tcp_server *s) { - gpr_mu_lock(&s->mu); - s->shutdown_listeners = true; - /* shutdown all fd's */ - if (s->active_ports) { - grpc_tcp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_fd_shutdown(exec_ctx, sp->emfd, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown")); - } - } - gpr_mu_unlock(&s->mu); -} - -#endif diff --git a/src/core/lib/iomgr/tcp_server_posix.cc b/src/core/lib/iomgr/tcp_server_posix.cc new file mode 100644 index 0000000000..06612d639c --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_posix.cc @@ -0,0 +1,565 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/tcp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/tcp_posix.h" +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/support/string.h" + +static gpr_once check_init = GPR_ONCE_INIT; +static bool has_so_reuseport = false; + +static void init(void) { +#ifndef GPR_MANYLINUX1 + int s = socket(AF_INET, SOCK_STREAM, 0); + if (s >= 0) { + has_so_reuseport = GRPC_LOG_IF_ERROR("check for SO_REUSEPORT", + grpc_set_socket_reuse_port(s, 1)); + close(s); + } +#endif +} + +grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, + grpc_closure *shutdown_complete, + const grpc_channel_args *args, + grpc_tcp_server **server) { + gpr_once_init(&check_init, init); + + grpc_tcp_server *s = (grpc_tcp_server *)gpr_zalloc(sizeof(grpc_tcp_server)); + s->so_reuseport = has_so_reuseport; + s->expand_wildcard_addrs = false; + for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) { + if (0 == strcmp(GRPC_ARG_ALLOW_REUSEPORT, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_INTEGER) { + s->so_reuseport = + has_so_reuseport && (args->args[i].value.integer != 0); + } else { + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING(GRPC_ARG_ALLOW_REUSEPORT + " must be an integer"); + } + } else if (0 == strcmp(GRPC_ARG_EXPAND_WILDCARD_ADDRS, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_INTEGER) { + s->expand_wildcard_addrs = (args->args[i].value.integer != 0); + } else { + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + GRPC_ARG_EXPAND_WILDCARD_ADDRS " must be an integer"); + } + } + } + gpr_ref_init(&s->refs, 1); + gpr_mu_init(&s->mu); + s->active_ports = 0; + s->destroyed_ports = 0; + s->shutdown = false; + s->shutdown_starting.head = NULL; + s->shutdown_starting.tail = NULL; + s->shutdown_complete = shutdown_complete; + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; + s->head = NULL; + s->tail = NULL; + s->nports = 0; + s->channel_args = grpc_channel_args_copy(args); + gpr_atm_no_barrier_store(&s->next_pollset_to_assign, 0); + *server = s; + return GRPC_ERROR_NONE; +} + +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + gpr_mu_lock(&s->mu); + GPR_ASSERT(s->shutdown); + gpr_mu_unlock(&s->mu); + if (s->shutdown_complete != NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); + } + + gpr_mu_destroy(&s->mu); + + while (s->head) { + grpc_tcp_listener *sp = s->head; + s->head = sp->next; + gpr_free(sp); + } + grpc_channel_args_destroy(exec_ctx, s->channel_args); + + gpr_free(s); +} + +static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, + grpc_error *error) { + grpc_tcp_server *s = (grpc_tcp_server *)server; + gpr_mu_lock(&s->mu); + s->destroyed_ports++; + if (s->destroyed_ports == s->nports) { + gpr_mu_unlock(&s->mu); + finish_shutdown(exec_ctx, s); + } else { + GPR_ASSERT(s->destroyed_ports < s->nports); + gpr_mu_unlock(&s->mu); + } +} + +/* called when all listening endpoints have been shutdown, so no further + events will be received on them - at this point it's safe to destroy + things */ +static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + /* delete ALL the things */ + gpr_mu_lock(&s->mu); + + GPR_ASSERT(s->shutdown); + + if (s->head) { + grpc_tcp_listener *sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_unlink_if_unix_domain_socket(&sp->addr); + GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, + grpc_schedule_on_exec_ctx); + grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, + false /* already_closed */, "tcp_listener_shutdown"); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + finish_shutdown(exec_ctx, s); + } +} + +static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + gpr_mu_lock(&s->mu); + + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + + /* shutdown all fd's */ + if (s->active_ports) { + grpc_tcp_listener *sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_fd_shutdown(exec_ctx, sp->emfd, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Server destroyed")); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + deactivated_all_ports(exec_ctx, s); + } +} + +/* event manager callback when reads are ready */ +static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *err) { + grpc_tcp_listener *sp = (grpc_tcp_listener *)arg; + grpc_pollset *read_notifier_pollset; + if (err != GRPC_ERROR_NONE) { + goto error; + } + + read_notifier_pollset = + sp->server->pollsets[(size_t)gpr_atm_no_barrier_fetch_add( + &sp->server->next_pollset_to_assign, 1) % + sp->server->pollset_count]; + + /* loop until accept4 returns EAGAIN, and then re-arm notification */ + for (;;) { + grpc_resolved_address addr; + char *addr_str; + char *name; + addr.len = sizeof(struct sockaddr_storage); + /* Note: If we ever decide to return this address to the user, remember to + strip off the ::ffff:0.0.0.0/96 prefix first. */ + int fd = grpc_accept4(sp->fd, &addr, 1, 1); + if (fd < 0) { + switch (errno) { + case EINTR: + continue; + case EAGAIN: + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + return; + default: + gpr_mu_lock(&sp->server->mu); + if (!sp->server->shutdown_listeners) { + gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno)); + } else { + /* if we have shutdown listeners, accept4 could fail, and we + needn't notify users */ + } + gpr_mu_unlock(&sp->server->mu); + goto error; + } + } + + grpc_set_socket_no_sigpipe_if_possible(fd); + + addr_str = grpc_sockaddr_to_uri(&addr); + gpr_asprintf(&name, "tcp-server-connection:%s", addr_str); + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str); + } + + grpc_fd *fdobj = grpc_fd_create(fd, name); + + grpc_pollset_add_fd(exec_ctx, read_notifier_pollset, fdobj); + + // Create acceptor. + grpc_tcp_server_acceptor *acceptor = + (grpc_tcp_server_acceptor *)gpr_malloc(sizeof(*acceptor)); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = sp->fd_index; + + sp->server->on_accept_cb( + exec_ctx, sp->server->on_accept_cb_arg, + grpc_tcp_create(exec_ctx, fdobj, sp->server->channel_args, addr_str), + read_notifier_pollset, acceptor); + + gpr_free(name); + gpr_free(addr_str); + } + + GPR_UNREACHABLE_CODE(return ); + +error: + gpr_mu_lock(&sp->server->mu); + if (0 == --sp->server->active_ports && sp->server->shutdown) { + gpr_mu_unlock(&sp->server->mu); + deactivated_all_ports(exec_ctx, sp->server); + } else { + gpr_mu_unlock(&sp->server->mu); + } +} + +/* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ +static grpc_error *add_wildcard_addrs_to_server(grpc_tcp_server *s, + unsigned port_index, + int requested_port, + int *out_port) { + grpc_resolved_address wild4; + grpc_resolved_address wild6; + unsigned fd_index = 0; + grpc_dualstack_mode dsmode; + grpc_tcp_listener *sp = NULL; + grpc_tcp_listener *sp2 = NULL; + grpc_error *v6_err = GRPC_ERROR_NONE; + grpc_error *v4_err = GRPC_ERROR_NONE; + *out_port = -1; + + if (grpc_tcp_server_have_ifaddrs() && s->expand_wildcard_addrs) { + return grpc_tcp_server_add_all_local_addrs(s, port_index, requested_port, + out_port); + } + + grpc_sockaddr_make_wildcards(requested_port, &wild4, &wild6); + /* Try listening on IPv6 first. */ + if ((v6_err = grpc_tcp_server_add_addr(s, &wild6, port_index, fd_index, + &dsmode, &sp)) == GRPC_ERROR_NONE) { + ++fd_index; + requested_port = *out_port = sp->port; + if (dsmode == GRPC_DSMODE_DUALSTACK || dsmode == GRPC_DSMODE_IPV4) { + return GRPC_ERROR_NONE; + } + } + /* If we got a v6-only socket or nothing, try adding 0.0.0.0. */ + grpc_sockaddr_set_port(&wild4, requested_port); + if ((v4_err = grpc_tcp_server_add_addr(s, &wild4, port_index, fd_index, + &dsmode, &sp2)) == GRPC_ERROR_NONE) { + *out_port = sp2->port; + if (sp != NULL) { + sp2->is_sibling = 1; + sp->sibling = sp2; + } + } + if (*out_port > 0) { + if (v6_err != GRPC_ERROR_NONE) { + gpr_log(GPR_INFO, + "Failed to add :: listener, " + "the environment may not support IPv6: %s", + grpc_error_string(v6_err)); + GRPC_ERROR_UNREF(v6_err); + } + if (v4_err != GRPC_ERROR_NONE) { + gpr_log(GPR_INFO, + "Failed to add 0.0.0.0 listener, " + "the environment may not support IPv4: %s", + grpc_error_string(v4_err)); + GRPC_ERROR_UNREF(v4_err); + } + return GRPC_ERROR_NONE; + } else { + grpc_error *root_err = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to add any wildcard listeners"); + GPR_ASSERT(v6_err != GRPC_ERROR_NONE && v4_err != GRPC_ERROR_NONE); + root_err = grpc_error_add_child(root_err, v6_err); + root_err = grpc_error_add_child(root_err, v4_err); + return root_err; + } +} + +static grpc_error *clone_port(grpc_tcp_listener *listener, unsigned count) { + grpc_tcp_listener *sp = NULL; + char *addr_str; + char *name; + grpc_error *err; + + for (grpc_tcp_listener *l = listener->next; l && l->is_sibling; l = l->next) { + l->fd_index += count; + } + + for (unsigned i = 0; i < count; i++) { + int fd = -1; + int port = -1; + grpc_dualstack_mode dsmode; + err = grpc_create_dualstack_socket(&listener->addr, SOCK_STREAM, 0, &dsmode, + &fd); + if (err != GRPC_ERROR_NONE) return err; + err = grpc_tcp_server_prepare_socket(fd, &listener->addr, true, &port); + if (err != GRPC_ERROR_NONE) return err; + listener->server->nports++; + grpc_sockaddr_to_string(&addr_str, &listener->addr, 1); + gpr_asprintf(&name, "tcp-server-listener:%s/clone-%d", addr_str, i); + sp = (grpc_tcp_listener *)gpr_malloc(sizeof(grpc_tcp_listener)); + sp->next = listener->next; + listener->next = sp; + /* sp (the new listener) is a sibling of 'listener' (the original + listener). */ + sp->is_sibling = 1; + sp->sibling = listener->sibling; + listener->sibling = sp; + sp->server = listener->server; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name); + memcpy(&sp->addr, &listener->addr, sizeof(grpc_resolved_address)); + sp->port = port; + sp->port_index = listener->port_index; + sp->fd_index = listener->fd_index + count - i; + GPR_ASSERT(sp->emfd); + while (listener->server->tail->next != NULL) { + listener->server->tail = listener->server->tail->next; + } + gpr_free(addr_str); + gpr_free(name); + } + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *out_port) { + grpc_tcp_listener *sp; + grpc_resolved_address sockname_temp; + grpc_resolved_address addr6_v4mapped; + int requested_port = grpc_sockaddr_get_port(addr); + unsigned port_index = 0; + grpc_dualstack_mode dsmode; + grpc_error *err; + *out_port = -1; + if (s->tail != NULL) { + port_index = s->tail->port_index + 1; + } + grpc_unlink_if_unix_domain_socket(addr); + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (requested_port == 0) { + for (sp = s->head; sp; sp = sp->next) { + sockname_temp.len = sizeof(struct sockaddr_storage); + if (0 == getsockname(sp->fd, (struct sockaddr *)&sockname_temp.addr, + (socklen_t *)&sockname_temp.len)) { + int used_port = grpc_sockaddr_get_port(&sockname_temp); + if (used_port > 0) { + memcpy(&sockname_temp, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(&sockname_temp, used_port); + requested_port = used_port; + addr = &sockname_temp; + break; + } + } + } + } + if (grpc_sockaddr_is_wildcard(addr, &requested_port)) { + return add_wildcard_addrs_to_server(s, port_index, requested_port, + out_port); + } + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + if ((err = grpc_tcp_server_add_addr(s, addr, port_index, 0, &dsmode, &sp)) == + GRPC_ERROR_NONE) { + *out_port = sp->port; + } + return err; +} + +/* Return listener at port_index or NULL. Should only be called with s->mu + locked. */ +static grpc_tcp_listener *get_port_index(grpc_tcp_server *s, + unsigned port_index) { + unsigned num_ports = 0; + grpc_tcp_listener *sp; + for (sp = s->head; sp; sp = sp->next) { + if (!sp->is_sibling) { + if (++num_ports > port_index) { + return sp; + } + } + } + return NULL; +} + +unsigned grpc_tcp_server_port_fd_count(grpc_tcp_server *s, + unsigned port_index) { + unsigned num_fds = 0; + gpr_mu_lock(&s->mu); + grpc_tcp_listener *sp = get_port_index(s, port_index); + for (; sp; sp = sp->sibling) { + ++num_fds; + } + gpr_mu_unlock(&s->mu); + return num_fds; +} + +int grpc_tcp_server_port_fd(grpc_tcp_server *s, unsigned port_index, + unsigned fd_index) { + gpr_mu_lock(&s->mu); + grpc_tcp_listener *sp = get_port_index(s, port_index); + for (; sp; sp = sp->sibling, --fd_index) { + if (fd_index == 0) { + gpr_mu_unlock(&s->mu); + return sp->fd; + } + } + gpr_mu_unlock(&s->mu); + return -1; +} + +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, + grpc_pollset **pollsets, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void *on_accept_cb_arg) { + size_t i; + grpc_tcp_listener *sp; + GPR_ASSERT(on_accept_cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->on_accept_cb); + GPR_ASSERT(s->active_ports == 0); + s->on_accept_cb = on_accept_cb; + s->on_accept_cb_arg = on_accept_cb_arg; + s->pollsets = pollsets; + s->pollset_count = pollset_count; + sp = s->head; + while (sp != NULL) { + if (s->so_reuseport && !grpc_is_unix_socket(&sp->addr) && + pollset_count > 1) { + GPR_ASSERT(GRPC_LOG_IF_ERROR( + "clone_port", clone_port(sp, (unsigned)(pollset_count - 1)))); + for (i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); + GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + s->active_ports++; + sp = sp->next; + } + } else { + for (i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); + } + GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + s->active_ports++; + sp = sp->next; + } + } + gpr_mu_unlock(&s->mu); +} + +grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { + gpr_ref_non_zero(&s->refs); + return s; +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, + grpc_closure *shutdown_starting) { + gpr_mu_lock(&s->mu); + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); + gpr_mu_unlock(&s->mu); +} + +void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + if (gpr_unref(&s->refs)) { + grpc_tcp_server_shutdown_listeners(exec_ctx, s); + gpr_mu_lock(&s->mu); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &s->shutdown_starting); + gpr_mu_unlock(&s->mu); + tcp_server_destroy(exec_ctx, s); + } +} + +void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, + grpc_tcp_server *s) { + gpr_mu_lock(&s->mu); + s->shutdown_listeners = true; + /* shutdown all fd's */ + if (s->active_ports) { + grpc_tcp_listener *sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_fd_shutdown(exec_ctx, sp->emfd, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown")); + } + } + gpr_mu_unlock(&s->mu); +} + +#endif diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.c b/src/core/lib/iomgr/tcp_server_utils_posix_common.c deleted file mode 100644 index a828bee074..0000000000 --- a/src/core/lib/iomgr/tcp_server_utils_posix_common.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/tcp_server_utils_posix.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" - -#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 - -static gpr_once s_init_max_accept_queue_size = GPR_ONCE_INIT; -static int s_max_accept_queue_size; - -/* get max listen queue size on linux */ -static void init_max_accept_queue_size(void) { - int n = SOMAXCONN; - char buf[64]; - FILE *fp = fopen("/proc/sys/net/core/somaxconn", "r"); - if (fp == NULL) { - /* 2.4 kernel. */ - s_max_accept_queue_size = SOMAXCONN; - return; - } - if (fgets(buf, sizeof buf, fp)) { - char *end; - long i = strtol(buf, &end, 10); - if (i > 0 && i <= INT_MAX && end && *end == 0) { - n = (int)i; - } - } - fclose(fp); - s_max_accept_queue_size = n; - - if (s_max_accept_queue_size < MIN_SAFE_ACCEPT_QUEUE_SIZE) { - gpr_log(GPR_INFO, - "Suspiciously small accept queue (%d) will probably lead to " - "connection drops", - s_max_accept_queue_size); - } -} - -static int get_max_accept_queue_size(void) { - gpr_once_init(&s_init_max_accept_queue_size, init_max_accept_queue_size); - return s_max_accept_queue_size; -} - -static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd, - const grpc_resolved_address *addr, - unsigned port_index, unsigned fd_index, - grpc_tcp_listener **listener) { - grpc_tcp_listener *sp = NULL; - int port = -1; - char *addr_str; - char *name; - - grpc_error *err = - grpc_tcp_server_prepare_socket(fd, addr, s->so_reuseport, &port); - if (err == GRPC_ERROR_NONE) { - GPR_ASSERT(port > 0); - grpc_sockaddr_to_string(&addr_str, addr, 1); - gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); - gpr_mu_lock(&s->mu); - s->nports++; - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = (grpc_tcp_listener *)gpr_malloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->fd = fd; - sp->emfd = grpc_fd_create(fd, name); - memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); - sp->port = port; - sp->port_index = port_index; - sp->fd_index = fd_index; - sp->is_sibling = 0; - sp->sibling = NULL; - GPR_ASSERT(sp->emfd); - gpr_mu_unlock(&s->mu); - gpr_free(addr_str); - gpr_free(name); - } - - *listener = sp; - return err; -} - -/* If successful, add a listener to s for addr, set *dsmode for the socket, and - return the *listener. */ -grpc_error *grpc_tcp_server_add_addr(grpc_tcp_server *s, - const grpc_resolved_address *addr, - unsigned port_index, unsigned fd_index, - grpc_dualstack_mode *dsmode, - grpc_tcp_listener **listener) { - grpc_resolved_address addr4_copy; - int fd; - grpc_error *err = - grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, dsmode, &fd); - if (err != GRPC_ERROR_NONE) { - return err; - } - if (*dsmode == GRPC_DSMODE_IPV4 && - grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { - addr = &addr4_copy; - } - return add_socket_to_server(s, fd, addr, port_index, fd_index, listener); -} - -/* Prepare a recently-created socket for listening. */ -grpc_error *grpc_tcp_server_prepare_socket(int fd, - const grpc_resolved_address *addr, - bool so_reuseport, int *port) { - grpc_resolved_address sockname_temp; - grpc_error *err = GRPC_ERROR_NONE; - - GPR_ASSERT(fd >= 0); - - if (so_reuseport && !grpc_is_unix_socket(addr)) { - err = grpc_set_socket_reuse_port(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - } - - err = grpc_set_socket_nonblocking(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - err = grpc_set_socket_cloexec(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - if (!grpc_is_unix_socket(addr)) { - err = grpc_set_socket_low_latency(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - err = grpc_set_socket_reuse_addr(fd, 1); - if (err != GRPC_ERROR_NONE) goto error; - } - err = grpc_set_socket_no_sigpipe_if_possible(fd); - if (err != GRPC_ERROR_NONE) goto error; - - GPR_ASSERT(addr->len < ~(socklen_t)0); - if (bind(fd, (struct sockaddr *)addr->addr, (socklen_t)addr->len) < 0) { - err = GRPC_OS_ERROR(errno, "bind"); - goto error; - } - - if (listen(fd, get_max_accept_queue_size()) < 0) { - err = GRPC_OS_ERROR(errno, "listen"); - goto error; - } - - sockname_temp.len = sizeof(struct sockaddr_storage); - - if (getsockname(fd, (struct sockaddr *)sockname_temp.addr, - (socklen_t *)&sockname_temp.len) < 0) { - err = GRPC_OS_ERROR(errno, "getsockname"); - goto error; - } - - *port = grpc_sockaddr_get_port(&sockname_temp); - return GRPC_ERROR_NONE; - -error: - GPR_ASSERT(err != GRPC_ERROR_NONE); - if (fd >= 0) { - close(fd); - } - grpc_error *ret = - grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Unable to configure socket", &err, 1), - GRPC_ERROR_INT_FD, fd); - GRPC_ERROR_UNREF(err); - return ret; -} - -#endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_common.cc b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc new file mode 100644 index 0000000000..a828bee074 --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_utils_posix_common.cc @@ -0,0 +1,206 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 + +static gpr_once s_init_max_accept_queue_size = GPR_ONCE_INIT; +static int s_max_accept_queue_size; + +/* get max listen queue size on linux */ +static void init_max_accept_queue_size(void) { + int n = SOMAXCONN; + char buf[64]; + FILE *fp = fopen("/proc/sys/net/core/somaxconn", "r"); + if (fp == NULL) { + /* 2.4 kernel. */ + s_max_accept_queue_size = SOMAXCONN; + return; + } + if (fgets(buf, sizeof buf, fp)) { + char *end; + long i = strtol(buf, &end, 10); + if (i > 0 && i <= INT_MAX && end && *end == 0) { + n = (int)i; + } + } + fclose(fp); + s_max_accept_queue_size = n; + + if (s_max_accept_queue_size < MIN_SAFE_ACCEPT_QUEUE_SIZE) { + gpr_log(GPR_INFO, + "Suspiciously small accept queue (%d) will probably lead to " + "connection drops", + s_max_accept_queue_size); + } +} + +static int get_max_accept_queue_size(void) { + gpr_once_init(&s_init_max_accept_queue_size, init_max_accept_queue_size); + return s_max_accept_queue_size; +} + +static grpc_error *add_socket_to_server(grpc_tcp_server *s, int fd, + const grpc_resolved_address *addr, + unsigned port_index, unsigned fd_index, + grpc_tcp_listener **listener) { + grpc_tcp_listener *sp = NULL; + int port = -1; + char *addr_str; + char *name; + + grpc_error *err = + grpc_tcp_server_prepare_socket(fd, addr, s->so_reuseport, &port); + if (err == GRPC_ERROR_NONE) { + GPR_ASSERT(port > 0); + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_asprintf(&name, "tcp-server-listener:%s", addr_str); + gpr_mu_lock(&s->mu); + s->nports++; + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = (grpc_tcp_listener *)gpr_malloc(sizeof(grpc_tcp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name); + memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); + sp->port = port; + sp->port_index = port_index; + sp->fd_index = fd_index; + sp->is_sibling = 0; + sp->sibling = NULL; + GPR_ASSERT(sp->emfd); + gpr_mu_unlock(&s->mu); + gpr_free(addr_str); + gpr_free(name); + } + + *listener = sp; + return err; +} + +/* If successful, add a listener to s for addr, set *dsmode for the socket, and + return the *listener. */ +grpc_error *grpc_tcp_server_add_addr(grpc_tcp_server *s, + const grpc_resolved_address *addr, + unsigned port_index, unsigned fd_index, + grpc_dualstack_mode *dsmode, + grpc_tcp_listener **listener) { + grpc_resolved_address addr4_copy; + int fd; + grpc_error *err = + grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, dsmode, &fd); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (*dsmode == GRPC_DSMODE_IPV4 && + grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { + addr = &addr4_copy; + } + return add_socket_to_server(s, fd, addr, port_index, fd_index, listener); +} + +/* Prepare a recently-created socket for listening. */ +grpc_error *grpc_tcp_server_prepare_socket(int fd, + const grpc_resolved_address *addr, + bool so_reuseport, int *port) { + grpc_resolved_address sockname_temp; + grpc_error *err = GRPC_ERROR_NONE; + + GPR_ASSERT(fd >= 0); + + if (so_reuseport && !grpc_is_unix_socket(addr)) { + err = grpc_set_socket_reuse_port(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + } + + err = grpc_set_socket_nonblocking(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_cloexec(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + if (!grpc_is_unix_socket(addr)) { + err = grpc_set_socket_low_latency(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + err = grpc_set_socket_reuse_addr(fd, 1); + if (err != GRPC_ERROR_NONE) goto error; + } + err = grpc_set_socket_no_sigpipe_if_possible(fd); + if (err != GRPC_ERROR_NONE) goto error; + + GPR_ASSERT(addr->len < ~(socklen_t)0); + if (bind(fd, (struct sockaddr *)addr->addr, (socklen_t)addr->len) < 0) { + err = GRPC_OS_ERROR(errno, "bind"); + goto error; + } + + if (listen(fd, get_max_accept_queue_size()) < 0) { + err = GRPC_OS_ERROR(errno, "listen"); + goto error; + } + + sockname_temp.len = sizeof(struct sockaddr_storage); + + if (getsockname(fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len) < 0) { + err = GRPC_OS_ERROR(errno, "getsockname"); + goto error; + } + + *port = grpc_sockaddr_get_port(&sockname_temp); + return GRPC_ERROR_NONE; + +error: + GPR_ASSERT(err != GRPC_ERROR_NONE); + if (fd >= 0) { + close(fd); + } + grpc_error *ret = + grpc_error_set_int(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Unable to configure socket", &err, 1), + GRPC_ERROR_INT_FD, fd); + GRPC_ERROR_UNREF(err); + return ret; +} + +#endif /* GRPC_POSIX_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c deleted file mode 100644 index a243b69f35..0000000000 --- a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_HAVE_IFADDRS - -#include "src/core/lib/iomgr/tcp_server_utils_posix.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" - -/* Return the listener in s with address addr or NULL. */ -static grpc_tcp_listener *find_listener_with_addr(grpc_tcp_server *s, - grpc_resolved_address *addr) { - grpc_tcp_listener *l; - gpr_mu_lock(&s->mu); - for (l = s->head; l != NULL; l = l->next) { - if (l->addr.len != addr->len) { - continue; - } - if (memcmp(l->addr.addr, addr->addr, addr->len) == 0) { - break; - } - } - gpr_mu_unlock(&s->mu); - return l; -} - -/* Bind to "::" to get a port number not used by any address. */ -static grpc_error *get_unused_port(int *port) { - grpc_resolved_address wild; - grpc_sockaddr_make_wildcard6(0, &wild); - grpc_dualstack_mode dsmode; - int fd; - grpc_error *err = - grpc_create_dualstack_socket(&wild, SOCK_STREAM, 0, &dsmode, &fd); - if (err != GRPC_ERROR_NONE) { - return err; - } - if (dsmode == GRPC_DSMODE_IPV4) { - grpc_sockaddr_make_wildcard4(0, &wild); - } - if (bind(fd, (const struct sockaddr *)wild.addr, (socklen_t)wild.len) != 0) { - err = GRPC_OS_ERROR(errno, "bind"); - close(fd); - return err; - } - if (getsockname(fd, (struct sockaddr *)wild.addr, (socklen_t *)&wild.len) != - 0) { - err = GRPC_OS_ERROR(errno, "getsockname"); - close(fd); - return err; - } - close(fd); - *port = grpc_sockaddr_get_port(&wild); - return *port <= 0 ? GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad port") - : GRPC_ERROR_NONE; -} - -grpc_error *grpc_tcp_server_add_all_local_addrs(grpc_tcp_server *s, - unsigned port_index, - int requested_port, - int *out_port) { - struct ifaddrs *ifa = NULL; - struct ifaddrs *ifa_it; - unsigned fd_index = 0; - grpc_tcp_listener *sp = NULL; - grpc_error *err = GRPC_ERROR_NONE; - if (requested_port == 0) { - /* Note: There could be a race where some local addrs can listen on the - selected port and some can't. The sane way to handle this would be to - retry by recreating the whole grpc_tcp_server. Backing out individual - listeners and orphaning the FDs looks like too much trouble. */ - if ((err = get_unused_port(&requested_port)) != GRPC_ERROR_NONE) { - return err; - } else if (requested_port <= 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad get_unused_port()"); - } - gpr_log(GPR_DEBUG, "Picked unused port %d", requested_port); - } - if (getifaddrs(&ifa) != 0 || ifa == NULL) { - return GRPC_OS_ERROR(errno, "getifaddrs"); - } - for (ifa_it = ifa; ifa_it != NULL; ifa_it = ifa_it->ifa_next) { - grpc_resolved_address addr; - char *addr_str = NULL; - grpc_dualstack_mode dsmode; - grpc_tcp_listener *new_sp = NULL; - const char *ifa_name = (ifa_it->ifa_name ? ifa_it->ifa_name : ""); - if (ifa_it->ifa_addr == NULL) { - continue; - } else if (ifa_it->ifa_addr->sa_family == AF_INET) { - addr.len = sizeof(struct sockaddr_in); - } else if (ifa_it->ifa_addr->sa_family == AF_INET6) { - addr.len = sizeof(struct sockaddr_in6); - } else { - continue; - } - memcpy(addr.addr, ifa_it->ifa_addr, addr.len); - if (!grpc_sockaddr_set_port(&addr, requested_port)) { - /* Should never happen, because we check sa_family above. */ - err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set port"); - break; - } - if (grpc_sockaddr_to_string(&addr_str, &addr, 0) < 0) { - addr_str = gpr_strdup(""); - } - gpr_log(GPR_DEBUG, - "Adding local addr from interface %s flags 0x%x to server: %s", - ifa_name, ifa_it->ifa_flags, addr_str); - /* We could have multiple interfaces with the same address (e.g., bonding), - so look for duplicates. */ - if (find_listener_with_addr(s, &addr) != NULL) { - gpr_log(GPR_DEBUG, "Skipping duplicate addr %s on interface %s", addr_str, - ifa_name); - gpr_free(addr_str); - continue; - } - if ((err = grpc_tcp_server_add_addr(s, &addr, port_index, fd_index, &dsmode, - &new_sp)) != GRPC_ERROR_NONE) { - char *err_str = NULL; - grpc_error *root_err; - if (gpr_asprintf(&err_str, "Failed to add listener: %s", addr_str) < 0) { - err_str = gpr_strdup("Failed to add listener"); - } - root_err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_str); - gpr_free(err_str); - gpr_free(addr_str); - err = grpc_error_add_child(root_err, err); - break; - } else { - GPR_ASSERT(requested_port == new_sp->port); - ++fd_index; - if (sp != NULL) { - new_sp->is_sibling = 1; - sp->sibling = new_sp; - } - sp = new_sp; - } - gpr_free(addr_str); - } - freeifaddrs(ifa); - if (err != GRPC_ERROR_NONE) { - return err; - } else if (sp == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No local addresses"); - } else { - *out_port = sp->port; - return GRPC_ERROR_NONE; - } -} - -bool grpc_tcp_server_have_ifaddrs(void) { return true; } - -#endif /* GRPC_HAVE_IFADDRS */ diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc new file mode 100644 index 0000000000..a243b69f35 --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc @@ -0,0 +1,181 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_HAVE_IFADDRS + +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" + +/* Return the listener in s with address addr or NULL. */ +static grpc_tcp_listener *find_listener_with_addr(grpc_tcp_server *s, + grpc_resolved_address *addr) { + grpc_tcp_listener *l; + gpr_mu_lock(&s->mu); + for (l = s->head; l != NULL; l = l->next) { + if (l->addr.len != addr->len) { + continue; + } + if (memcmp(l->addr.addr, addr->addr, addr->len) == 0) { + break; + } + } + gpr_mu_unlock(&s->mu); + return l; +} + +/* Bind to "::" to get a port number not used by any address. */ +static grpc_error *get_unused_port(int *port) { + grpc_resolved_address wild; + grpc_sockaddr_make_wildcard6(0, &wild); + grpc_dualstack_mode dsmode; + int fd; + grpc_error *err = + grpc_create_dualstack_socket(&wild, SOCK_STREAM, 0, &dsmode, &fd); + if (err != GRPC_ERROR_NONE) { + return err; + } + if (dsmode == GRPC_DSMODE_IPV4) { + grpc_sockaddr_make_wildcard4(0, &wild); + } + if (bind(fd, (const struct sockaddr *)wild.addr, (socklen_t)wild.len) != 0) { + err = GRPC_OS_ERROR(errno, "bind"); + close(fd); + return err; + } + if (getsockname(fd, (struct sockaddr *)wild.addr, (socklen_t *)&wild.len) != + 0) { + err = GRPC_OS_ERROR(errno, "getsockname"); + close(fd); + return err; + } + close(fd); + *port = grpc_sockaddr_get_port(&wild); + return *port <= 0 ? GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad port") + : GRPC_ERROR_NONE; +} + +grpc_error *grpc_tcp_server_add_all_local_addrs(grpc_tcp_server *s, + unsigned port_index, + int requested_port, + int *out_port) { + struct ifaddrs *ifa = NULL; + struct ifaddrs *ifa_it; + unsigned fd_index = 0; + grpc_tcp_listener *sp = NULL; + grpc_error *err = GRPC_ERROR_NONE; + if (requested_port == 0) { + /* Note: There could be a race where some local addrs can listen on the + selected port and some can't. The sane way to handle this would be to + retry by recreating the whole grpc_tcp_server. Backing out individual + listeners and orphaning the FDs looks like too much trouble. */ + if ((err = get_unused_port(&requested_port)) != GRPC_ERROR_NONE) { + return err; + } else if (requested_port <= 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Bad get_unused_port()"); + } + gpr_log(GPR_DEBUG, "Picked unused port %d", requested_port); + } + if (getifaddrs(&ifa) != 0 || ifa == NULL) { + return GRPC_OS_ERROR(errno, "getifaddrs"); + } + for (ifa_it = ifa; ifa_it != NULL; ifa_it = ifa_it->ifa_next) { + grpc_resolved_address addr; + char *addr_str = NULL; + grpc_dualstack_mode dsmode; + grpc_tcp_listener *new_sp = NULL; + const char *ifa_name = (ifa_it->ifa_name ? ifa_it->ifa_name : ""); + if (ifa_it->ifa_addr == NULL) { + continue; + } else if (ifa_it->ifa_addr->sa_family == AF_INET) { + addr.len = sizeof(struct sockaddr_in); + } else if (ifa_it->ifa_addr->sa_family == AF_INET6) { + addr.len = sizeof(struct sockaddr_in6); + } else { + continue; + } + memcpy(addr.addr, ifa_it->ifa_addr, addr.len); + if (!grpc_sockaddr_set_port(&addr, requested_port)) { + /* Should never happen, because we check sa_family above. */ + err = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to set port"); + break; + } + if (grpc_sockaddr_to_string(&addr_str, &addr, 0) < 0) { + addr_str = gpr_strdup(""); + } + gpr_log(GPR_DEBUG, + "Adding local addr from interface %s flags 0x%x to server: %s", + ifa_name, ifa_it->ifa_flags, addr_str); + /* We could have multiple interfaces with the same address (e.g., bonding), + so look for duplicates. */ + if (find_listener_with_addr(s, &addr) != NULL) { + gpr_log(GPR_DEBUG, "Skipping duplicate addr %s on interface %s", addr_str, + ifa_name); + gpr_free(addr_str); + continue; + } + if ((err = grpc_tcp_server_add_addr(s, &addr, port_index, fd_index, &dsmode, + &new_sp)) != GRPC_ERROR_NONE) { + char *err_str = NULL; + grpc_error *root_err; + if (gpr_asprintf(&err_str, "Failed to add listener: %s", addr_str) < 0) { + err_str = gpr_strdup("Failed to add listener"); + } + root_err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_str); + gpr_free(err_str); + gpr_free(addr_str); + err = grpc_error_add_child(root_err, err); + break; + } else { + GPR_ASSERT(requested_port == new_sp->port); + ++fd_index; + if (sp != NULL) { + new_sp->is_sibling = 1; + sp->sibling = new_sp; + } + sp = new_sp; + } + gpr_free(addr_str); + } + freeifaddrs(ifa); + if (err != GRPC_ERROR_NONE) { + return err; + } else if (sp == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No local addresses"); + } else { + *out_port = sp->port; + return GRPC_ERROR_NONE; + } +} + +bool grpc_tcp_server_have_ifaddrs(void) { return true; } + +#endif /* GRPC_HAVE_IFADDRS */ diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c deleted file mode 100644 index 34eab20d6a..0000000000 --- a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#if defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) - -#include "src/core/lib/iomgr/tcp_server_utils_posix.h" - -grpc_error *grpc_tcp_server_add_all_local_addrs(grpc_tcp_server *s, - unsigned port_index, - int requested_port, - int *out_port) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("no ifaddrs available"); -} - -bool grpc_tcp_server_have_ifaddrs(void) { return false; } - -#endif /* defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) */ diff --git a/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc new file mode 100644 index 0000000000..34eab20d6a --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#if defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) + +#include "src/core/lib/iomgr/tcp_server_utils_posix.h" + +grpc_error *grpc_tcp_server_add_all_local_addrs(grpc_tcp_server *s, + unsigned port_index, + int requested_port, + int *out_port) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("no ifaddrs available"); +} + +bool grpc_tcp_server_have_ifaddrs(void) { return false; } + +#endif /* defined(GRPC_POSIX_SOCKET) && !defined(GRPC_HAVE_IFADDRS) */ diff --git a/src/core/lib/iomgr/tcp_server_uv.c b/src/core/lib/iomgr/tcp_server_uv.c deleted file mode 100644 index 3b9332321f..0000000000 --- a/src/core/lib/iomgr/tcp_server_uv.c +++ /dev/null @@ -1,454 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include -#include - -#include -#include - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/tcp_server.h" -#include "src/core/lib/iomgr/tcp_uv.h" - -/* one listening port */ -typedef struct grpc_tcp_listener grpc_tcp_listener; -struct grpc_tcp_listener { - uv_tcp_t *handle; - grpc_tcp_server *server; - unsigned port_index; - int port; - /* linked list */ - struct grpc_tcp_listener *next; - - bool closed; - - bool has_pending_connection; -}; - -struct grpc_tcp_server { - gpr_refcount refs; - - /* Called whenever accept() succeeds on a server port. */ - grpc_tcp_server_cb on_accept_cb; - void *on_accept_cb_arg; - - int open_ports; - - /* linked list of server ports */ - grpc_tcp_listener *head; - grpc_tcp_listener *tail; - - /* List of closures passed to shutdown_starting_add(). */ - grpc_closure_list shutdown_starting; - - /* shutdown callback */ - grpc_closure *shutdown_complete; - - bool shutdown; - - grpc_resource_quota *resource_quota; -}; - -grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, - grpc_closure *shutdown_complete, - const grpc_channel_args *args, - grpc_tcp_server **server) { - grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); - s->resource_quota = grpc_resource_quota_create(NULL); - for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) { - if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { - if (args->args[i].type == GRPC_ARG_POINTER) { - grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota); - s->resource_quota = - grpc_resource_quota_ref_internal(args->args[i].value.pointer.p); - } else { - grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota); - gpr_free(s); - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); - } - } - } - gpr_ref_init(&s->refs, 1); - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->open_ports = 0; - s->head = NULL; - s->tail = NULL; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - s->shutdown = false; - *server = s; - return GRPC_ERROR_NONE; -} - -grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { - GRPC_UV_ASSERT_SAME_THREAD(); - gpr_ref(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, - grpc_closure *shutdown_starting) { - grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, - GRPC_ERROR_NONE); -} - -static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - GPR_ASSERT(s->shutdown); - if (s->shutdown_complete != NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); - } - - while (s->head) { - grpc_tcp_listener *sp = s->head; - s->head = sp->next; - sp->next = NULL; - gpr_free(sp->handle); - gpr_free(sp); - } - grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota); - gpr_free(s); -} - -static void handle_close_callback(uv_handle_t *handle) { - grpc_tcp_listener *sp = (grpc_tcp_listener *)handle->data; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - sp->server->open_ports--; - if (sp->server->open_ports == 0 && sp->server->shutdown) { - finish_shutdown(&exec_ctx, sp->server); - } - grpc_exec_ctx_finish(&exec_ctx); -} - -static void close_listener(grpc_tcp_listener *sp) { - if (!sp->closed) { - sp->closed = true; - uv_close((uv_handle_t *)sp->handle, handle_close_callback); - } -} - -static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - int immediately_done = 0; - grpc_tcp_listener *sp; - - GPR_ASSERT(!s->shutdown); - s->shutdown = true; - - if (s->open_ports == 0) { - immediately_done = 1; - } - for (sp = s->head; sp; sp = sp->next) { - close_listener(sp); - } - - if (immediately_done) { - finish_shutdown(exec_ctx, s); - } -} - -void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - GRPC_UV_ASSERT_SAME_THREAD(); - if (gpr_unref(&s->refs)) { - /* Complete shutdown_starting work before destroying. */ - grpc_exec_ctx local_exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_CLOSURE_LIST_SCHED(&local_exec_ctx, &s->shutdown_starting); - if (exec_ctx == NULL) { - grpc_exec_ctx_flush(&local_exec_ctx); - tcp_server_destroy(&local_exec_ctx, s); - grpc_exec_ctx_finish(&local_exec_ctx); - } else { - grpc_exec_ctx_finish(&local_exec_ctx); - tcp_server_destroy(exec_ctx, s); - } - } -} - -static void finish_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *sp) { - grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor)); - uv_tcp_t *client; - grpc_endpoint *ep = NULL; - grpc_resolved_address peer_name; - char *peer_name_string; - int err; - uv_tcp_t *server = sp->handle; - - client = gpr_malloc(sizeof(uv_tcp_t)); - uv_tcp_init(uv_default_loop(), client); - // UV documentation says this is guaranteed to succeed - uv_accept((uv_stream_t *)server, (uv_stream_t *)client); - peer_name_string = NULL; - memset(&peer_name, 0, sizeof(grpc_resolved_address)); - peer_name.len = sizeof(struct sockaddr_storage); - err = uv_tcp_getpeername(client, (struct sockaddr *)&peer_name.addr, - (int *)&peer_name.len); - if (err == 0) { - peer_name_string = grpc_sockaddr_to_uri(&peer_name); - } else { - gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(err)); - } - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - if (peer_name_string) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s", - sp->server, peer_name_string); - } else { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection", sp->server); - } - } - ep = grpc_tcp_create(client, sp->server->resource_quota, peer_name_string); - acceptor->from_server = sp->server; - acceptor->port_index = sp->port_index; - acceptor->fd_index = 0; - sp->server->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep, NULL, - acceptor); - gpr_free(peer_name_string); -} - -static void on_connect(uv_stream_t *server, int status) { - grpc_tcp_listener *sp = (grpc_tcp_listener *)server->data; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - if (status < 0) { - switch (status) { - case UV_EINTR: - case UV_EAGAIN: - return; - default: - close_listener(sp); - return; - } - } - - GPR_ASSERT(!sp->has_pending_connection); - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p incoming connection", sp->server); - } - - // Create acceptor. - if (sp->server->on_accept_cb) { - finish_accept(&exec_ctx, sp); - } else { - sp->has_pending_connection = true; - } - grpc_exec_ctx_finish(&exec_ctx); -} - -static grpc_error *add_socket_to_server(grpc_tcp_server *s, uv_tcp_t *handle, - const grpc_resolved_address *addr, - unsigned port_index, - grpc_tcp_listener **listener) { - grpc_tcp_listener *sp = NULL; - int port = -1; - int status; - grpc_error *error; - grpc_resolved_address sockname_temp; - - // The last argument to uv_tcp_bind is flags - status = uv_tcp_bind(handle, (struct sockaddr *)addr->addr, 0); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to bind to port"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - status = uv_listen((uv_stream_t *)handle, SOMAXCONN, on_connect); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to listen to port"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - sockname_temp.len = (int)sizeof(struct sockaddr_storage); - status = uv_tcp_getsockname(handle, (struct sockaddr *)&sockname_temp.addr, - (int *)&sockname_temp.len); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getsockname failed"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - return error; - } - - port = grpc_sockaddr_get_port(&sockname_temp); - - GPR_ASSERT(port >= 0); - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = gpr_zalloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->handle = handle; - sp->port = port; - sp->port_index = port_index; - sp->closed = false; - handle->data = sp; - s->open_ports++; - GPR_ASSERT(sp->handle); - *listener = sp; - - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, - const grpc_resolved_address *addr, - int *port) { - // This function is mostly copied from tcp_server_windows.c - grpc_tcp_listener *sp = NULL; - uv_tcp_t *handle; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address wildcard; - grpc_resolved_address *allocated_addr = NULL; - grpc_resolved_address sockname_temp; - unsigned port_index = 0; - int status; - grpc_error *error = GRPC_ERROR_NONE; - int family; - - GRPC_UV_ASSERT_SAME_THREAD(); - - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - - /* Check if this is a wildcard port, and if so, try to keep the port the same - as some previously created listener. */ - if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { - sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == uv_tcp_getsockname(sp->handle, - (struct sockaddr *)&sockname_temp.addr, - (int *)&sockname_temp.len)) { - *port = grpc_sockaddr_get_port(&sockname_temp); - if (*port > 0) { - allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); - memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); - grpc_sockaddr_set_port(allocated_addr, *port); - addr = allocated_addr; - break; - } - } - } - } - - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ - if (grpc_sockaddr_is_wildcard(addr, port)) { - grpc_sockaddr_make_wildcard6(*port, &wildcard); - - addr = &wildcard; - } - - handle = gpr_malloc(sizeof(uv_tcp_t)); - - family = grpc_sockaddr_get_family(addr); - status = uv_tcp_init_ex(uv_default_loop(), handle, (unsigned int)family); -#if defined(GPR_LINUX) && defined(SO_REUSEPORT) - if (family == AF_INET || family == AF_INET6) { - int fd; - uv_fileno((uv_handle_t *)handle, &fd); - int enable = 1; - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)); - } -#endif /* GPR_LINUX && SO_REUSEPORT */ - - if (status == 0) { - error = add_socket_to_server(s, handle, addr, port_index, &sp); - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to initialize UV tcp handle"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - } - - gpr_free(allocated_addr); - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - char *port_string; - grpc_sockaddr_to_string(&port_string, addr, 0); - const char *str = grpc_error_string(error); - if (port_string) { - gpr_log(GPR_DEBUG, "SERVER %p add_port %s error=%s", s, port_string, str); - gpr_free(port_string); - } else { - gpr_log(GPR_DEBUG, "SERVER %p add_port error=%s", s, str); - } - } - - if (error != GRPC_ERROR_NONE) { - grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to add port to server", &error, 1); - GRPC_ERROR_UNREF(error); - error = error_out; - *port = -1; - } else { - GPR_ASSERT(sp != NULL); - *port = sp->port; - } - return error; -} - -void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, - grpc_pollset **pollsets, size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, void *cb_arg) { - grpc_tcp_listener *sp; - (void)pollsets; - (void)pollset_count; - GRPC_UV_ASSERT_SAME_THREAD(); - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "SERVER_START %p", server); - } - GPR_ASSERT(on_accept_cb); - GPR_ASSERT(!server->on_accept_cb); - server->on_accept_cb = on_accept_cb; - server->on_accept_cb_arg = cb_arg; - for (sp = server->head; sp; sp = sp->next) { - if (sp->has_pending_connection) { - finish_accept(exec_ctx, sp); - sp->has_pending_connection = false; - } - } -} - -void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, - grpc_tcp_server *s) {} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_server_uv.cc b/src/core/lib/iomgr/tcp_server_uv.cc new file mode 100644 index 0000000000..3b9332321f --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_uv.cc @@ -0,0 +1,454 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include +#include + +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/tcp_uv.h" + +/* one listening port */ +typedef struct grpc_tcp_listener grpc_tcp_listener; +struct grpc_tcp_listener { + uv_tcp_t *handle; + grpc_tcp_server *server; + unsigned port_index; + int port; + /* linked list */ + struct grpc_tcp_listener *next; + + bool closed; + + bool has_pending_connection; +}; + +struct grpc_tcp_server { + gpr_refcount refs; + + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void *on_accept_cb_arg; + + int open_ports; + + /* linked list of server ports */ + grpc_tcp_listener *head; + grpc_tcp_listener *tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure *shutdown_complete; + + bool shutdown; + + grpc_resource_quota *resource_quota; +}; + +grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, + grpc_closure *shutdown_complete, + const grpc_channel_args *args, + grpc_tcp_server **server) { + grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); + s->resource_quota = grpc_resource_quota_create(NULL); + for (size_t i = 0; i < (args == NULL ? 0 : args->num_args); i++) { + if (0 == strcmp(GRPC_ARG_RESOURCE_QUOTA, args->args[i].key)) { + if (args->args[i].type == GRPC_ARG_POINTER) { + grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota); + s->resource_quota = + grpc_resource_quota_ref_internal(args->args[i].value.pointer.p); + } else { + grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota); + gpr_free(s); + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + GRPC_ARG_RESOURCE_QUOTA " must be a pointer to a buffer pool"); + } + } + } + gpr_ref_init(&s->refs, 1); + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; + s->open_ports = 0; + s->head = NULL; + s->tail = NULL; + s->shutdown_starting.head = NULL; + s->shutdown_starting.tail = NULL; + s->shutdown_complete = shutdown_complete; + s->shutdown = false; + *server = s; + return GRPC_ERROR_NONE; +} + +grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { + GRPC_UV_ASSERT_SAME_THREAD(); + gpr_ref(&s->refs); + return s; +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, + grpc_closure *shutdown_starting) { + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); +} + +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + GPR_ASSERT(s->shutdown); + if (s->shutdown_complete != NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); + } + + while (s->head) { + grpc_tcp_listener *sp = s->head; + s->head = sp->next; + sp->next = NULL; + gpr_free(sp->handle); + gpr_free(sp); + } + grpc_resource_quota_unref_internal(exec_ctx, s->resource_quota); + gpr_free(s); +} + +static void handle_close_callback(uv_handle_t *handle) { + grpc_tcp_listener *sp = (grpc_tcp_listener *)handle->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + sp->server->open_ports--; + if (sp->server->open_ports == 0 && sp->server->shutdown) { + finish_shutdown(&exec_ctx, sp->server); + } + grpc_exec_ctx_finish(&exec_ctx); +} + +static void close_listener(grpc_tcp_listener *sp) { + if (!sp->closed) { + sp->closed = true; + uv_close((uv_handle_t *)sp->handle, handle_close_callback); + } +} + +static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + int immediately_done = 0; + grpc_tcp_listener *sp; + + GPR_ASSERT(!s->shutdown); + s->shutdown = true; + + if (s->open_ports == 0) { + immediately_done = 1; + } + for (sp = s->head; sp; sp = sp->next) { + close_listener(sp); + } + + if (immediately_done) { + finish_shutdown(exec_ctx, s); + } +} + +void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + GRPC_UV_ASSERT_SAME_THREAD(); + if (gpr_unref(&s->refs)) { + /* Complete shutdown_starting work before destroying. */ + grpc_exec_ctx local_exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_CLOSURE_LIST_SCHED(&local_exec_ctx, &s->shutdown_starting); + if (exec_ctx == NULL) { + grpc_exec_ctx_flush(&local_exec_ctx); + tcp_server_destroy(&local_exec_ctx, s); + grpc_exec_ctx_finish(&local_exec_ctx); + } else { + grpc_exec_ctx_finish(&local_exec_ctx); + tcp_server_destroy(exec_ctx, s); + } + } +} + +static void finish_accept(grpc_exec_ctx *exec_ctx, grpc_tcp_listener *sp) { + grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor)); + uv_tcp_t *client; + grpc_endpoint *ep = NULL; + grpc_resolved_address peer_name; + char *peer_name_string; + int err; + uv_tcp_t *server = sp->handle; + + client = gpr_malloc(sizeof(uv_tcp_t)); + uv_tcp_init(uv_default_loop(), client); + // UV documentation says this is guaranteed to succeed + uv_accept((uv_stream_t *)server, (uv_stream_t *)client); + peer_name_string = NULL; + memset(&peer_name, 0, sizeof(grpc_resolved_address)); + peer_name.len = sizeof(struct sockaddr_storage); + err = uv_tcp_getpeername(client, (struct sockaddr *)&peer_name.addr, + (int *)&peer_name.len); + if (err == 0) { + peer_name_string = grpc_sockaddr_to_uri(&peer_name); + } else { + gpr_log(GPR_INFO, "uv_tcp_getpeername error: %s", uv_strerror(err)); + } + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + if (peer_name_string) { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection: %s", + sp->server, peer_name_string); + } else { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p accepted connection", sp->server); + } + } + ep = grpc_tcp_create(client, sp->server->resource_quota, peer_name_string); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = 0; + sp->server->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep, NULL, + acceptor); + gpr_free(peer_name_string); +} + +static void on_connect(uv_stream_t *server, int status) { + grpc_tcp_listener *sp = (grpc_tcp_listener *)server->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + if (status < 0) { + switch (status) { + case UV_EINTR: + case UV_EAGAIN: + return; + default: + close_listener(sp); + return; + } + } + + GPR_ASSERT(!sp->has_pending_connection); + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "SERVER_CONNECT: %p incoming connection", sp->server); + } + + // Create acceptor. + if (sp->server->on_accept_cb) { + finish_accept(&exec_ctx, sp); + } else { + sp->has_pending_connection = true; + } + grpc_exec_ctx_finish(&exec_ctx); +} + +static grpc_error *add_socket_to_server(grpc_tcp_server *s, uv_tcp_t *handle, + const grpc_resolved_address *addr, + unsigned port_index, + grpc_tcp_listener **listener) { + grpc_tcp_listener *sp = NULL; + int port = -1; + int status; + grpc_error *error; + grpc_resolved_address sockname_temp; + + // The last argument to uv_tcp_bind is flags + status = uv_tcp_bind(handle, (struct sockaddr *)addr->addr, 0); + if (status != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to bind to port"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + return error; + } + + status = uv_listen((uv_stream_t *)handle, SOMAXCONN, on_connect); + if (status != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to listen to port"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + return error; + } + + sockname_temp.len = (int)sizeof(struct sockaddr_storage); + status = uv_tcp_getsockname(handle, (struct sockaddr *)&sockname_temp.addr, + (int *)&sockname_temp.len); + if (status != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("getsockname failed"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + return error; + } + + port = grpc_sockaddr_get_port(&sockname_temp); + + GPR_ASSERT(port >= 0); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = gpr_zalloc(sizeof(grpc_tcp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->handle = handle; + sp->port = port; + sp->port_index = port_index; + sp->closed = false; + handle->data = sp; + s->open_ports++; + GPR_ASSERT(sp->handle); + *listener = sp; + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *port) { + // This function is mostly copied from tcp_server_windows.c + grpc_tcp_listener *sp = NULL; + uv_tcp_t *handle; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + int status; + grpc_error *error = GRPC_ERROR_NONE; + int family; + + GRPC_UV_ASSERT_SAME_THREAD(); + + if (s->tail != NULL) { + port_index = s->tail->port_index + 1; + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (sp = s->head; sp; sp = sp->next) { + sockname_temp.len = sizeof(struct sockaddr_storage); + if (0 == uv_tcp_getsockname(sp->handle, + (struct sockaddr *)&sockname_temp.addr, + (int *)&sockname_temp.len)) { + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, port)) { + grpc_sockaddr_make_wildcard6(*port, &wildcard); + + addr = &wildcard; + } + + handle = gpr_malloc(sizeof(uv_tcp_t)); + + family = grpc_sockaddr_get_family(addr); + status = uv_tcp_init_ex(uv_default_loop(), handle, (unsigned int)family); +#if defined(GPR_LINUX) && defined(SO_REUSEPORT) + if (family == AF_INET || family == AF_INET6) { + int fd; + uv_fileno((uv_handle_t *)handle, &fd); + int enable = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(enable)); + } +#endif /* GPR_LINUX && SO_REUSEPORT */ + + if (status == 0) { + error = add_socket_to_server(s, handle, addr, port_index, &sp); + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to initialize UV tcp handle"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + } + + gpr_free(allocated_addr); + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + char *port_string; + grpc_sockaddr_to_string(&port_string, addr, 0); + const char *str = grpc_error_string(error); + if (port_string) { + gpr_log(GPR_DEBUG, "SERVER %p add_port %s error=%s", s, port_string, str); + gpr_free(port_string); + } else { + gpr_log(GPR_DEBUG, "SERVER %p add_port error=%s", s, str); + } + } + + if (error != GRPC_ERROR_NONE) { + grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != NULL); + *port = sp->port; + } + return error; +} + +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *server, + grpc_pollset **pollsets, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, void *cb_arg) { + grpc_tcp_listener *sp; + (void)pollsets; + (void)pollset_count; + GRPC_UV_ASSERT_SAME_THREAD(); + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "SERVER_START %p", server); + } + GPR_ASSERT(on_accept_cb); + GPR_ASSERT(!server->on_accept_cb); + server->on_accept_cb = on_accept_cb; + server->on_accept_cb_arg = cb_arg; + for (sp = server->head; sp; sp = sp->next) { + if (sp->has_pending_connection) { + finish_accept(exec_ctx, sp); + sp->has_pending_connection = false; + } + } +} + +void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, + grpc_tcp_server *s) {} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_server_windows.c b/src/core/lib/iomgr/tcp_server_windows.c deleted file mode 100644 index 0162afc1ad..0000000000 --- a/src/core/lib/iomgr/tcp_server_windows.c +++ /dev/null @@ -1,543 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include "src/core/lib/iomgr/sockaddr.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/pollset_windows.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/socket_windows.h" -#include "src/core/lib/iomgr/tcp_server.h" -#include "src/core/lib/iomgr/tcp_windows.h" - -#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 - -/* one listening port */ -typedef struct grpc_tcp_listener grpc_tcp_listener; -struct grpc_tcp_listener { - /* This seemingly magic number comes from AcceptEx's documentation. each - address buffer needs to have at least 16 more bytes at their end. */ - uint8_t addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; - /* This will hold the socket for the next accept. */ - SOCKET new_socket; - /* The listener winsocket. */ - grpc_winsocket *socket; - /* The actual TCP port number. */ - int port; - unsigned port_index; - grpc_tcp_server *server; - /* The cached AcceptEx for that port. */ - LPFN_ACCEPTEX AcceptEx; - int shutting_down; - int outstanding_calls; - /* closure for socket notification of accept being ready */ - grpc_closure on_accept; - /* linked list */ - struct grpc_tcp_listener *next; -}; - -/* the overall server */ -struct grpc_tcp_server { - gpr_refcount refs; - /* Called whenever accept() succeeds on a server port. */ - grpc_tcp_server_cb on_accept_cb; - void *on_accept_cb_arg; - - gpr_mu mu; - - /* active port count: how many ports are actually still listening */ - int active_ports; - - /* linked list of server ports */ - grpc_tcp_listener *head; - grpc_tcp_listener *tail; - - /* List of closures passed to shutdown_starting_add(). */ - grpc_closure_list shutdown_starting; - - /* shutdown callback */ - grpc_closure *shutdown_complete; - - grpc_channel_args *channel_args; -}; - -/* Public function. Allocates the proper data structures to hold a - grpc_tcp_server. */ -grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, - grpc_closure *shutdown_complete, - const grpc_channel_args *args, - grpc_tcp_server **server) { - grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); - s->channel_args = grpc_channel_args_copy(args); - gpr_ref_init(&s->refs, 1); - gpr_mu_init(&s->mu); - s->active_ports = 0; - s->on_accept_cb = NULL; - s->on_accept_cb_arg = NULL; - s->head = NULL; - s->tail = NULL; - s->shutdown_starting.head = NULL; - s->shutdown_starting.tail = NULL; - s->shutdown_complete = shutdown_complete; - *server = s; - return GRPC_ERROR_NONE; -} - -static void destroy_server(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_tcp_server *s = arg; - - /* Now that the accepts have been aborted, we can destroy the sockets. - The IOCP won't get notified on these, so we can flag them as already - closed by the system. */ - while (s->head) { - grpc_tcp_listener *sp = s->head; - s->head = sp->next; - sp->next = NULL; - grpc_winsocket_destroy(sp->socket); - gpr_free(sp); - } - grpc_channel_args_destroy(exec_ctx, s->channel_args); - gpr_free(s); -} - -static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx, - grpc_tcp_server *s) { - if (s->shutdown_complete != NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); - } - - GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(destroy_server, s, - grpc_schedule_on_exec_ctx), - GRPC_ERROR_NONE); -} - -grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { - gpr_ref_non_zero(&s->refs); - return s; -} - -void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, - grpc_closure *shutdown_starting) { - gpr_mu_lock(&s->mu); - grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, - GRPC_ERROR_NONE); - gpr_mu_unlock(&s->mu); -} - -static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - grpc_tcp_listener *sp; - gpr_mu_lock(&s->mu); - - /* First, shutdown all fd's. This will queue abortion calls for all - of the pending accepts due to the normal operation mechanism. */ - if (s->active_ports == 0) { - finish_shutdown_locked(exec_ctx, s); - } else { - for (sp = s->head; sp; sp = sp->next) { - sp->shutting_down = 1; - grpc_winsocket_shutdown(sp->socket); - } - } - gpr_mu_unlock(&s->mu); -} - -void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { - if (gpr_unref(&s->refs)) { - grpc_tcp_server_shutdown_listeners(exec_ctx, s); - gpr_mu_lock(&s->mu); - GRPC_CLOSURE_LIST_SCHED(exec_ctx, &s->shutdown_starting); - gpr_mu_unlock(&s->mu); - tcp_server_destroy(exec_ctx, s); - } -} - -/* Prepare (bind) a recently-created socket for listening. */ -static grpc_error *prepare_socket(SOCKET sock, - const grpc_resolved_address *addr, - int *port) { - grpc_resolved_address sockname_temp; - grpc_error *error = GRPC_ERROR_NONE; - - error = grpc_tcp_prepare_socket(sock); - if (error != GRPC_ERROR_NONE) { - goto failure; - } - - if (bind(sock, (const struct sockaddr *)addr->addr, (int)addr->len) == - SOCKET_ERROR) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); - goto failure; - } - - if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "listen"); - goto failure; - } - - int sockname_temp_len = sizeof(struct sockaddr_storage); - if (getsockname(sock, (struct sockaddr *)sockname_temp.addr, - &sockname_temp_len) == SOCKET_ERROR) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); - goto failure; - } - sockname_temp.len = (size_t)sockname_temp_len; - - *port = grpc_sockaddr_get_port(&sockname_temp); - return GRPC_ERROR_NONE; - -failure: - GPR_ASSERT(error != GRPC_ERROR_NONE); - char *tgtaddr = grpc_sockaddr_to_uri(addr); - grpc_error_set_int( - grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to prepare server socket", &error, 1), - GRPC_ERROR_STR_TARGET_ADDRESS, - grpc_slice_from_copied_string(tgtaddr)), - GRPC_ERROR_INT_FD, (intptr_t)sock); - gpr_free(tgtaddr); - GRPC_ERROR_UNREF(error); - if (sock != INVALID_SOCKET) closesocket(sock); - return error; -} - -static void decrement_active_ports_and_notify_locked(grpc_exec_ctx *exec_ctx, - grpc_tcp_listener *sp) { - sp->shutting_down = 0; - GPR_ASSERT(sp->server->active_ports > 0); - if (0 == --sp->server->active_ports) { - finish_shutdown_locked(exec_ctx, sp->server); - } -} - -/* In order to do an async accept, we need to create a socket first which - will be the one assigned to the new incoming connection. */ -static grpc_error *start_accept_locked(grpc_exec_ctx *exec_ctx, - grpc_tcp_listener *port) { - SOCKET sock = INVALID_SOCKET; - BOOL success; - DWORD addrlen = sizeof(struct sockaddr_in6) + 16; - DWORD bytes_received = 0; - grpc_error *error = GRPC_ERROR_NONE; - - if (port->shutting_down) { - return GRPC_ERROR_NONE; - } - - sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, - WSA_FLAG_OVERLAPPED); - if (sock == INVALID_SOCKET) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); - goto failure; - } - - error = grpc_tcp_prepare_socket(sock); - if (error != GRPC_ERROR_NONE) goto failure; - - /* Start the "accept" asynchronously. */ - success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0, - addrlen, addrlen, &bytes_received, - &port->socket->read_info.overlapped); - - /* It is possible to get an accept immediately without delay. However, we - will still get an IOCP notification for it. So let's just ignore it. */ - if (!success) { - int last_error = WSAGetLastError(); - if (last_error != ERROR_IO_PENDING) { - error = GRPC_WSA_ERROR(last_error, "AcceptEx"); - goto failure; - } - } - - /* We're ready to do the accept. Calling grpc_socket_notify_on_read may - immediately process an accept that happened in the meantime. */ - port->new_socket = sock; - grpc_socket_notify_on_read(exec_ctx, port->socket, &port->on_accept); - port->outstanding_calls++; - return error; - -failure: - GPR_ASSERT(error != GRPC_ERROR_NONE); - if (sock != INVALID_SOCKET) closesocket(sock); - return error; -} - -/* Event manager callback when reads are ready. */ -static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_tcp_listener *sp = arg; - SOCKET sock = sp->new_socket; - grpc_winsocket_callback_info *info = &sp->socket->read_info; - grpc_endpoint *ep = NULL; - grpc_resolved_address peer_name; - char *peer_name_string; - char *fd_name; - DWORD transfered_bytes; - DWORD flags; - BOOL wsa_success; - int err; - - gpr_mu_lock(&sp->server->mu); - - peer_name.len = sizeof(struct sockaddr_storage); - - /* The general mechanism for shutting down is to queue abortion calls. While - this is necessary in the read/write case, it's useless for the accept - case. We only need to adjust the pending callback count */ - if (error != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(error); - gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg); - - gpr_mu_unlock(&sp->server->mu); - return; - } - - /* The IOCP notified us of a completed operation. Let's grab the results, - and act accordingly. */ - transfered_bytes = 0; - wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, - &transfered_bytes, FALSE, &flags); - if (!wsa_success) { - if (!sp->shutting_down) { - char *utf8_message = gpr_format_message(WSAGetLastError()); - gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); - gpr_free(utf8_message); - } - closesocket(sock); - } else { - if (!sp->shutting_down) { - peer_name_string = NULL; - err = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, - (char *)&sp->socket->socket, sizeof(sp->socket->socket)); - if (err) { - char *utf8_message = gpr_format_message(WSAGetLastError()); - gpr_log(GPR_ERROR, "setsockopt error: %s", utf8_message); - gpr_free(utf8_message); - } - int peer_name_len = (int)peer_name.len; - err = - getpeername(sock, (struct sockaddr *)peer_name.addr, &peer_name_len); - peer_name.len = (size_t)peer_name_len; - if (!err) { - peer_name_string = grpc_sockaddr_to_uri(&peer_name); - } else { - char *utf8_message = gpr_format_message(WSAGetLastError()); - gpr_log(GPR_ERROR, "getpeername error: %s", utf8_message); - gpr_free(utf8_message); - } - gpr_asprintf(&fd_name, "tcp_server:%s", peer_name_string); - ep = grpc_tcp_create(exec_ctx, grpc_winsocket_create(sock, fd_name), - sp->server->channel_args, peer_name_string); - gpr_free(fd_name); - gpr_free(peer_name_string); - } else { - closesocket(sock); - } - } - - /* The only time we should call our callback, is where we successfully - managed to accept a connection, and created an endpoint. */ - if (ep) { - // Create acceptor. - grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor)); - acceptor->from_server = sp->server; - acceptor->port_index = sp->port_index; - acceptor->fd_index = 0; - sp->server->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep, NULL, - acceptor); - } - /* As we were notified from the IOCP of one and exactly one accept, - the former socked we created has now either been destroy or assigned - to the new connection. We need to create a new one for the next - connection. */ - GPR_ASSERT( - GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(exec_ctx, sp))); - if (0 == --sp->outstanding_calls) { - decrement_active_ports_and_notify_locked(exec_ctx, sp); - } - gpr_mu_unlock(&sp->server->mu); -} - -static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, - const grpc_resolved_address *addr, - unsigned port_index, - grpc_tcp_listener **listener) { - grpc_tcp_listener *sp = NULL; - int port = -1; - int status; - GUID guid = WSAID_ACCEPTEX; - DWORD ioctl_num_bytes; - LPFN_ACCEPTEX AcceptEx; - grpc_error *error = GRPC_ERROR_NONE; - - /* We need to grab the AcceptEx pointer for that port, as it may be - interface-dependent. We'll cache it to avoid doing that again. */ - status = - WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), - &AcceptEx, sizeof(AcceptEx), &ioctl_num_bytes, NULL, NULL); - - if (status != 0) { - char *utf8_message = gpr_format_message(WSAGetLastError()); - gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); - gpr_free(utf8_message); - closesocket(sock); - return NULL; - } - - error = prepare_socket(sock, addr, &port); - if (error != GRPC_ERROR_NONE) { - return error; - } - - GPR_ASSERT(port >= 0); - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = gpr_malloc(sizeof(grpc_tcp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->socket = grpc_winsocket_create(sock, "listener"); - sp->shutting_down = 0; - sp->outstanding_calls = 0; - sp->AcceptEx = AcceptEx; - sp->new_socket = INVALID_SOCKET; - sp->port = port; - sp->port_index = port_index; - GRPC_CLOSURE_INIT(&sp->on_accept, on_accept, sp, grpc_schedule_on_exec_ctx); - GPR_ASSERT(sp->socket); - gpr_mu_unlock(&s->mu); - *listener = sp; - - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, - const grpc_resolved_address *addr, - int *port) { - grpc_tcp_listener *sp = NULL; - SOCKET sock; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address wildcard; - grpc_resolved_address *allocated_addr = NULL; - grpc_resolved_address sockname_temp; - unsigned port_index = 0; - grpc_error *error = GRPC_ERROR_NONE; - - if (s->tail != NULL) { - port_index = s->tail->port_index + 1; - } - - /* Check if this is a wildcard port, and if so, try to keep the port the same - as some previously created listener. */ - if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { - int sockname_temp_len = sizeof(struct sockaddr_storage); - if (0 == getsockname(sp->socket->socket, - (struct sockaddr *)sockname_temp.addr, - &sockname_temp_len)) { - sockname_temp.len = (size_t)sockname_temp_len; - *port = grpc_sockaddr_get_port(&sockname_temp); - if (*port > 0) { - allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); - memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); - grpc_sockaddr_set_port(allocated_addr, *port); - addr = allocated_addr; - break; - } - } - } - } - - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ - if (grpc_sockaddr_is_wildcard(addr, port)) { - grpc_sockaddr_make_wildcard6(*port, &wildcard); - - addr = &wildcard; - } - - sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, - WSA_FLAG_OVERLAPPED); - if (sock == INVALID_SOCKET) { - error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); - goto done; - } - - error = add_socket_to_server(s, sock, addr, port_index, &sp); - -done: - gpr_free(allocated_addr); - - if (error != GRPC_ERROR_NONE) { - grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Failed to add port to server", &error, 1); - GRPC_ERROR_UNREF(error); - error = error_out; - *port = -1; - } else { - GPR_ASSERT(sp != NULL); - *port = sp->port; - } - return error; -} - -void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, - grpc_pollset **pollset, size_t pollset_count, - grpc_tcp_server_cb on_accept_cb, - void *on_accept_cb_arg) { - grpc_tcp_listener *sp; - GPR_ASSERT(on_accept_cb); - gpr_mu_lock(&s->mu); - GPR_ASSERT(!s->on_accept_cb); - GPR_ASSERT(s->active_ports == 0); - s->on_accept_cb = on_accept_cb; - s->on_accept_cb_arg = on_accept_cb_arg; - for (sp = s->head; sp; sp = sp->next) { - GPR_ASSERT( - GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(exec_ctx, sp))); - s->active_ports++; - } - gpr_mu_unlock(&s->mu); -} - -void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, - grpc_tcp_server *s) {} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_server_windows.cc b/src/core/lib/iomgr/tcp_server_windows.cc new file mode 100644 index 0000000000..0162afc1ad --- /dev/null +++ b/src/core/lib/iomgr/tcp_server_windows.cc @@ -0,0 +1,543 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/pollset_windows.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_server.h" +#include "src/core/lib/iomgr/tcp_windows.h" + +#define MIN_SAFE_ACCEPT_QUEUE_SIZE 100 + +/* one listening port */ +typedef struct grpc_tcp_listener grpc_tcp_listener; +struct grpc_tcp_listener { + /* This seemingly magic number comes from AcceptEx's documentation. each + address buffer needs to have at least 16 more bytes at their end. */ + uint8_t addresses[(sizeof(struct sockaddr_in6) + 16) * 2]; + /* This will hold the socket for the next accept. */ + SOCKET new_socket; + /* The listener winsocket. */ + grpc_winsocket *socket; + /* The actual TCP port number. */ + int port; + unsigned port_index; + grpc_tcp_server *server; + /* The cached AcceptEx for that port. */ + LPFN_ACCEPTEX AcceptEx; + int shutting_down; + int outstanding_calls; + /* closure for socket notification of accept being ready */ + grpc_closure on_accept; + /* linked list */ + struct grpc_tcp_listener *next; +}; + +/* the overall server */ +struct grpc_tcp_server { + gpr_refcount refs; + /* Called whenever accept() succeeds on a server port. */ + grpc_tcp_server_cb on_accept_cb; + void *on_accept_cb_arg; + + gpr_mu mu; + + /* active port count: how many ports are actually still listening */ + int active_ports; + + /* linked list of server ports */ + grpc_tcp_listener *head; + grpc_tcp_listener *tail; + + /* List of closures passed to shutdown_starting_add(). */ + grpc_closure_list shutdown_starting; + + /* shutdown callback */ + grpc_closure *shutdown_complete; + + grpc_channel_args *channel_args; +}; + +/* Public function. Allocates the proper data structures to hold a + grpc_tcp_server. */ +grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, + grpc_closure *shutdown_complete, + const grpc_channel_args *args, + grpc_tcp_server **server) { + grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); + s->channel_args = grpc_channel_args_copy(args); + gpr_ref_init(&s->refs, 1); + gpr_mu_init(&s->mu); + s->active_ports = 0; + s->on_accept_cb = NULL; + s->on_accept_cb_arg = NULL; + s->head = NULL; + s->tail = NULL; + s->shutdown_starting.head = NULL; + s->shutdown_starting.tail = NULL; + s->shutdown_complete = shutdown_complete; + *server = s; + return GRPC_ERROR_NONE; +} + +static void destroy_server(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_tcp_server *s = arg; + + /* Now that the accepts have been aborted, we can destroy the sockets. + The IOCP won't get notified on these, so we can flag them as already + closed by the system. */ + while (s->head) { + grpc_tcp_listener *sp = s->head; + s->head = sp->next; + sp->next = NULL; + grpc_winsocket_destroy(sp->socket); + gpr_free(sp); + } + grpc_channel_args_destroy(exec_ctx, s->channel_args); + gpr_free(s); +} + +static void finish_shutdown_locked(grpc_exec_ctx *exec_ctx, + grpc_tcp_server *s) { + if (s->shutdown_complete != NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); + } + + GRPC_CLOSURE_SCHED(exec_ctx, GRPC_CLOSURE_CREATE(destroy_server, s, + grpc_schedule_on_exec_ctx), + GRPC_ERROR_NONE); +} + +grpc_tcp_server *grpc_tcp_server_ref(grpc_tcp_server *s) { + gpr_ref_non_zero(&s->refs); + return s; +} + +void grpc_tcp_server_shutdown_starting_add(grpc_tcp_server *s, + grpc_closure *shutdown_starting) { + gpr_mu_lock(&s->mu); + grpc_closure_list_append(&s->shutdown_starting, shutdown_starting, + GRPC_ERROR_NONE); + gpr_mu_unlock(&s->mu); +} + +static void tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + grpc_tcp_listener *sp; + gpr_mu_lock(&s->mu); + + /* First, shutdown all fd's. This will queue abortion calls for all + of the pending accepts due to the normal operation mechanism. */ + if (s->active_ports == 0) { + finish_shutdown_locked(exec_ctx, s); + } else { + for (sp = s->head; sp; sp = sp->next) { + sp->shutting_down = 1; + grpc_winsocket_shutdown(sp->socket); + } + } + gpr_mu_unlock(&s->mu); +} + +void grpc_tcp_server_unref(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) { + if (gpr_unref(&s->refs)) { + grpc_tcp_server_shutdown_listeners(exec_ctx, s); + gpr_mu_lock(&s->mu); + GRPC_CLOSURE_LIST_SCHED(exec_ctx, &s->shutdown_starting); + gpr_mu_unlock(&s->mu); + tcp_server_destroy(exec_ctx, s); + } +} + +/* Prepare (bind) a recently-created socket for listening. */ +static grpc_error *prepare_socket(SOCKET sock, + const grpc_resolved_address *addr, + int *port) { + grpc_resolved_address sockname_temp; + grpc_error *error = GRPC_ERROR_NONE; + + error = grpc_tcp_prepare_socket(sock); + if (error != GRPC_ERROR_NONE) { + goto failure; + } + + if (bind(sock, (const struct sockaddr *)addr->addr, (int)addr->len) == + SOCKET_ERROR) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "bind"); + goto failure; + } + + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "listen"); + goto failure; + } + + int sockname_temp_len = sizeof(struct sockaddr_storage); + if (getsockname(sock, (struct sockaddr *)sockname_temp.addr, + &sockname_temp_len) == SOCKET_ERROR) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); + goto failure; + } + sockname_temp.len = (size_t)sockname_temp_len; + + *port = grpc_sockaddr_get_port(&sockname_temp); + return GRPC_ERROR_NONE; + +failure: + GPR_ASSERT(error != GRPC_ERROR_NONE); + char *tgtaddr = grpc_sockaddr_to_uri(addr); + grpc_error_set_int( + grpc_error_set_str(GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to prepare server socket", &error, 1), + GRPC_ERROR_STR_TARGET_ADDRESS, + grpc_slice_from_copied_string(tgtaddr)), + GRPC_ERROR_INT_FD, (intptr_t)sock); + gpr_free(tgtaddr); + GRPC_ERROR_UNREF(error); + if (sock != INVALID_SOCKET) closesocket(sock); + return error; +} + +static void decrement_active_ports_and_notify_locked(grpc_exec_ctx *exec_ctx, + grpc_tcp_listener *sp) { + sp->shutting_down = 0; + GPR_ASSERT(sp->server->active_ports > 0); + if (0 == --sp->server->active_ports) { + finish_shutdown_locked(exec_ctx, sp->server); + } +} + +/* In order to do an async accept, we need to create a socket first which + will be the one assigned to the new incoming connection. */ +static grpc_error *start_accept_locked(grpc_exec_ctx *exec_ctx, + grpc_tcp_listener *port) { + SOCKET sock = INVALID_SOCKET; + BOOL success; + DWORD addrlen = sizeof(struct sockaddr_in6) + 16; + DWORD bytes_received = 0; + grpc_error *error = GRPC_ERROR_NONE; + + if (port->shutting_down) { + return GRPC_ERROR_NONE; + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); + goto failure; + } + + error = grpc_tcp_prepare_socket(sock); + if (error != GRPC_ERROR_NONE) goto failure; + + /* Start the "accept" asynchronously. */ + success = port->AcceptEx(port->socket->socket, sock, port->addresses, 0, + addrlen, addrlen, &bytes_received, + &port->socket->read_info.overlapped); + + /* It is possible to get an accept immediately without delay. However, we + will still get an IOCP notification for it. So let's just ignore it. */ + if (!success) { + int last_error = WSAGetLastError(); + if (last_error != ERROR_IO_PENDING) { + error = GRPC_WSA_ERROR(last_error, "AcceptEx"); + goto failure; + } + } + + /* We're ready to do the accept. Calling grpc_socket_notify_on_read may + immediately process an accept that happened in the meantime. */ + port->new_socket = sock; + grpc_socket_notify_on_read(exec_ctx, port->socket, &port->on_accept); + port->outstanding_calls++; + return error; + +failure: + GPR_ASSERT(error != GRPC_ERROR_NONE); + if (sock != INVALID_SOCKET) closesocket(sock); + return error; +} + +/* Event manager callback when reads are ready. */ +static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_tcp_listener *sp = arg; + SOCKET sock = sp->new_socket; + grpc_winsocket_callback_info *info = &sp->socket->read_info; + grpc_endpoint *ep = NULL; + grpc_resolved_address peer_name; + char *peer_name_string; + char *fd_name; + DWORD transfered_bytes; + DWORD flags; + BOOL wsa_success; + int err; + + gpr_mu_lock(&sp->server->mu); + + peer_name.len = sizeof(struct sockaddr_storage); + + /* The general mechanism for shutting down is to queue abortion calls. While + this is necessary in the read/write case, it's useless for the accept + case. We only need to adjust the pending callback count */ + if (error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(error); + gpr_log(GPR_INFO, "Skipping on_accept due to error: %s", msg); + + gpr_mu_unlock(&sp->server->mu); + return; + } + + /* The IOCP notified us of a completed operation. Let's grab the results, + and act accordingly. */ + transfered_bytes = 0; + wsa_success = WSAGetOverlappedResult(sock, &info->overlapped, + &transfered_bytes, FALSE, &flags); + if (!wsa_success) { + if (!sp->shutting_down) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "on_accept error: %s", utf8_message); + gpr_free(utf8_message); + } + closesocket(sock); + } else { + if (!sp->shutting_down) { + peer_name_string = NULL; + err = setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char *)&sp->socket->socket, sizeof(sp->socket->socket)); + if (err) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "setsockopt error: %s", utf8_message); + gpr_free(utf8_message); + } + int peer_name_len = (int)peer_name.len; + err = + getpeername(sock, (struct sockaddr *)peer_name.addr, &peer_name_len); + peer_name.len = (size_t)peer_name_len; + if (!err) { + peer_name_string = grpc_sockaddr_to_uri(&peer_name); + } else { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "getpeername error: %s", utf8_message); + gpr_free(utf8_message); + } + gpr_asprintf(&fd_name, "tcp_server:%s", peer_name_string); + ep = grpc_tcp_create(exec_ctx, grpc_winsocket_create(sock, fd_name), + sp->server->channel_args, peer_name_string); + gpr_free(fd_name); + gpr_free(peer_name_string); + } else { + closesocket(sock); + } + } + + /* The only time we should call our callback, is where we successfully + managed to accept a connection, and created an endpoint. */ + if (ep) { + // Create acceptor. + grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor)); + acceptor->from_server = sp->server; + acceptor->port_index = sp->port_index; + acceptor->fd_index = 0; + sp->server->on_accept_cb(exec_ctx, sp->server->on_accept_cb_arg, ep, NULL, + acceptor); + } + /* As we were notified from the IOCP of one and exactly one accept, + the former socked we created has now either been destroy or assigned + to the new connection. We need to create a new one for the next + connection. */ + GPR_ASSERT( + GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(exec_ctx, sp))); + if (0 == --sp->outstanding_calls) { + decrement_active_ports_and_notify_locked(exec_ctx, sp); + } + gpr_mu_unlock(&sp->server->mu); +} + +static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, + const grpc_resolved_address *addr, + unsigned port_index, + grpc_tcp_listener **listener) { + grpc_tcp_listener *sp = NULL; + int port = -1; + int status; + GUID guid = WSAID_ACCEPTEX; + DWORD ioctl_num_bytes; + LPFN_ACCEPTEX AcceptEx; + grpc_error *error = GRPC_ERROR_NONE; + + /* We need to grab the AcceptEx pointer for that port, as it may be + interface-dependent. We'll cache it to avoid doing that again. */ + status = + WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid, sizeof(guid), + &AcceptEx, sizeof(AcceptEx), &ioctl_num_bytes, NULL, NULL); + + if (status != 0) { + char *utf8_message = gpr_format_message(WSAGetLastError()); + gpr_log(GPR_ERROR, "on_connect error: %s", utf8_message); + gpr_free(utf8_message); + closesocket(sock); + return NULL; + } + + error = prepare_socket(sock, addr, &port); + if (error != GRPC_ERROR_NONE) { + return error; + } + + GPR_ASSERT(port >= 0); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); + sp = gpr_malloc(sizeof(grpc_tcp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->socket = grpc_winsocket_create(sock, "listener"); + sp->shutting_down = 0; + sp->outstanding_calls = 0; + sp->AcceptEx = AcceptEx; + sp->new_socket = INVALID_SOCKET; + sp->port = port; + sp->port_index = port_index; + GRPC_CLOSURE_INIT(&sp->on_accept, on_accept, sp, grpc_schedule_on_exec_ctx); + GPR_ASSERT(sp->socket); + gpr_mu_unlock(&s->mu); + *listener = sp; + + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, + const grpc_resolved_address *addr, + int *port) { + grpc_tcp_listener *sp = NULL; + SOCKET sock; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wildcard; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; + unsigned port_index = 0; + grpc_error *error = GRPC_ERROR_NONE; + + if (s->tail != NULL) { + port_index = s->tail->port_index + 1; + } + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (sp = s->head; sp; sp = sp->next) { + int sockname_temp_len = sizeof(struct sockaddr_storage); + if (0 == getsockname(sp->socket->socket, + (struct sockaddr *)sockname_temp.addr, + &sockname_temp_len)) { + sockname_temp.len = (size_t)sockname_temp_len; + *port = grpc_sockaddr_get_port(&sockname_temp); + if (*port > 0) { + allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, *port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, port)) { + grpc_sockaddr_make_wildcard6(*port, &wildcard); + + addr = &wildcard; + } + + sock = WSASocket(AF_INET6, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (sock == INVALID_SOCKET) { + error = GRPC_WSA_ERROR(WSAGetLastError(), "WSASocket"); + goto done; + } + + error = add_socket_to_server(s, sock, addr, port_index, &sp); + +done: + gpr_free(allocated_addr); + + if (error != GRPC_ERROR_NONE) { + grpc_error *error_out = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Failed to add port to server", &error, 1); + GRPC_ERROR_UNREF(error); + error = error_out; + *port = -1; + } else { + GPR_ASSERT(sp != NULL); + *port = sp->port; + } + return error; +} + +void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s, + grpc_pollset **pollset, size_t pollset_count, + grpc_tcp_server_cb on_accept_cb, + void *on_accept_cb_arg) { + grpc_tcp_listener *sp; + GPR_ASSERT(on_accept_cb); + gpr_mu_lock(&s->mu); + GPR_ASSERT(!s->on_accept_cb); + GPR_ASSERT(s->active_ports == 0); + s->on_accept_cb = on_accept_cb; + s->on_accept_cb_arg = on_accept_cb_arg; + for (sp = s->head; sp; sp = sp->next) { + GPR_ASSERT( + GRPC_LOG_IF_ERROR("start_accept", start_accept_locked(exec_ctx, sp))); + s->active_ports++; + } + gpr_mu_unlock(&s->mu); +} + +void grpc_tcp_server_shutdown_listeners(grpc_exec_ctx *exec_ctx, + grpc_tcp_server *s) {} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_uv.c b/src/core/lib/iomgr/tcp_uv.c deleted file mode 100644 index a05c19b4ac..0000000000 --- a/src/core/lib/iomgr/tcp_uv.c +++ /dev/null @@ -1,381 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_UV - -#include -#include - -#include - -#include -#include -#include - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/network_status_tracker.h" -#include "src/core/lib/iomgr/resource_quota.h" -#include "src/core/lib/iomgr/tcp_uv.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp"); - -typedef struct { - grpc_endpoint base; - gpr_refcount refcount; - - uv_write_t write_req; - uv_shutdown_t shutdown_req; - - uv_tcp_t *handle; - - grpc_closure *read_cb; - grpc_closure *write_cb; - - grpc_slice read_slice; - grpc_slice_buffer *read_slices; - grpc_slice_buffer *write_slices; - uv_buf_t *write_buffers; - - grpc_resource_user *resource_user; - - bool shutting_down; - - char *peer_string; - grpc_pollset *pollset; -} grpc_tcp; - -static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - grpc_slice_unref_internal(exec_ctx, tcp->read_slice); - grpc_resource_user_unref(exec_ctx, tcp->resource_user); - gpr_free(tcp->handle); - gpr_free(tcp->peer_string); - gpr_free(tcp); -} - -#ifndef NDEBUG -#define TCP_UNREF(exec_ctx, tcp, reason) \ - tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val - 1); - } - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val + 1); - } - gpr_ref(&tcp->refcount); -} -#else -#define TCP_UNREF(exec_ctx, tcp, reason) tcp_unref((exec_ctx), (tcp)) -#define TCP_REF(tcp, reason) tcp_ref((tcp)) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } -#endif - -static void uv_close_callback(uv_handle_t *handle) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_tcp *tcp = handle->data; - TCP_UNREF(&exec_ctx, tcp, "destroy"); - grpc_exec_ctx_finish(&exec_ctx); -} - -static grpc_slice alloc_read_slice(grpc_exec_ctx *exec_ctx, - grpc_resource_user *resource_user) { - return grpc_resource_user_slice_malloc(exec_ctx, resource_user, - GRPC_TCP_DEFAULT_READ_SLICE_SIZE); -} - -static void alloc_uv_buf(uv_handle_t *handle, size_t suggested_size, - uv_buf_t *buf) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_tcp *tcp = handle->data; - (void)suggested_size; - buf->base = (char *)GRPC_SLICE_START_PTR(tcp->read_slice); - buf->len = GRPC_SLICE_LENGTH(tcp->read_slice); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void read_callback(uv_stream_t *stream, ssize_t nread, - const uv_buf_t *buf) { - grpc_slice sub; - grpc_error *error; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_tcp *tcp = stream->data; - grpc_closure *cb = tcp->read_cb; - if (nread == 0) { - // Nothing happened. Wait for the next callback - return; - } - TCP_UNREF(&exec_ctx, tcp, "read"); - tcp->read_cb = NULL; - // TODO(murgatroid99): figure out what the return value here means - uv_read_stop(stream); - if (nread == UV_EOF) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); - } else if (nread > 0) { - // Successful read - sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, (size_t)nread); - grpc_slice_buffer_add(tcp->read_slices, sub); - tcp->read_slice = alloc_read_slice(&exec_ctx, tcp->resource_user); - error = GRPC_ERROR_NONE; - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - size_t i; - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "read: error=%s", str); - - for (i = 0; i < tcp->read_slices->count; i++) { - char *dump = grpc_dump_slice(tcp->read_slices->slices[i], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, - dump); - gpr_free(dump); - } - } - } else { - // nread < 0: Error - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed"); - } - GRPC_CLOSURE_SCHED(&exec_ctx, cb, error); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void uv_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_slice_buffer *read_slices, grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - int status; - grpc_error *error = GRPC_ERROR_NONE; - GRPC_UV_ASSERT_SAME_THREAD(); - GPR_ASSERT(tcp->read_cb == NULL); - tcp->read_cb = cb; - tcp->read_slices = read_slices; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices); - TCP_REF(tcp, "read"); - // TODO(murgatroid99): figure out what the return value here means - status = - uv_read_start((uv_stream_t *)tcp->handle, alloc_uv_buf, read_callback); - if (status != 0) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed at start"); - error = - grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, - grpc_slice_from_static_string(uv_strerror(status))); - GRPC_CLOSURE_SCHED(exec_ctx, cb, error); - } - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str); - } -} - -static void write_callback(uv_write_t *req, int status) { - grpc_tcp *tcp = req->data; - grpc_error *error; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_closure *cb = tcp->write_cb; - tcp->write_cb = NULL; - TCP_UNREF(&exec_ctx, tcp, "write"); - if (status == 0) { - error = GRPC_ERROR_NONE; - } else { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Write failed"); - } - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(error); - gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str); - } - gpr_free(tcp->write_buffers); - grpc_resource_user_free(&exec_ctx, tcp->resource_user, - sizeof(uv_buf_t) * tcp->write_slices->count); - GRPC_CLOSURE_SCHED(&exec_ctx, cb, error); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void uv_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_slice_buffer *write_slices, - grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - uv_buf_t *buffers; - unsigned int buffer_count; - unsigned int i; - grpc_slice *slice; - uv_write_t *write_req; - GRPC_UV_ASSERT_SAME_THREAD(); - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - size_t j; - - for (j = 0; j < write_slices->count; j++) { - char *data = grpc_dump_slice(write_slices->slices[j], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); - gpr_free(data); - } - } - - if (tcp->shutting_down) { - GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "TCP socket is shutting down")); - return; - } - - GPR_ASSERT(tcp->write_cb == NULL); - tcp->write_slices = write_slices; - GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); - if (tcp->write_slices->count == 0) { - // No slices means we don't have to do anything, - // and libuv doesn't like empty writes - GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_ERROR_NONE); - return; - } - - tcp->write_cb = cb; - buffer_count = (unsigned int)tcp->write_slices->count; - buffers = gpr_malloc(sizeof(uv_buf_t) * buffer_count); - grpc_resource_user_alloc(exec_ctx, tcp->resource_user, - sizeof(uv_buf_t) * buffer_count, NULL); - for (i = 0; i < buffer_count; i++) { - slice = &tcp->write_slices->slices[i]; - buffers[i].base = (char *)GRPC_SLICE_START_PTR(*slice); - buffers[i].len = GRPC_SLICE_LENGTH(*slice); - } - tcp->write_buffers = buffers; - write_req = &tcp->write_req; - write_req->data = tcp; - TCP_REF(tcp, "write"); - // TODO(murgatroid99): figure out what the return value here means - uv_write(write_req, (uv_stream_t *)tcp->handle, buffers, buffer_count, - write_callback); -} - -static void uv_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset *pollset) { - // No-op. We're ignoring pollsets currently - (void)exec_ctx; - (void)ep; - (void)pollset; - grpc_tcp *tcp = (grpc_tcp *)ep; - tcp->pollset = pollset; -} - -static void uv_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset_set *pollset) { - // No-op. We're ignoring pollsets currently - (void)exec_ctx; - (void)ep; - (void)pollset; -} - -static void shutdown_callback(uv_shutdown_t *req, int status) {} - -static void uv_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_error *why) { - grpc_tcp *tcp = (grpc_tcp *)ep; - if (!tcp->shutting_down) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - const char *str = grpc_error_string(why); - gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->handle, str); - } - tcp->shutting_down = true; - uv_shutdown_t *req = &tcp->shutdown_req; - uv_shutdown(req, (uv_stream_t *)tcp->handle, shutdown_callback); - grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); - } - GRPC_ERROR_UNREF(why); -} - -static void uv_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { - grpc_network_status_unregister_endpoint(ep); - grpc_tcp *tcp = (grpc_tcp *)ep; - uv_close((uv_handle_t *)tcp->handle, uv_close_callback); -} - -static char *uv_get_peer(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return gpr_strdup(tcp->peer_string); -} - -static grpc_resource_user *uv_get_resource_user(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return tcp->resource_user; -} - -static int uv_get_fd(grpc_endpoint *ep) { return -1; } - -static grpc_endpoint_vtable vtable = { - uv_endpoint_read, uv_endpoint_write, uv_add_to_pollset, - uv_add_to_pollset_set, uv_endpoint_shutdown, uv_destroy, - uv_get_resource_user, uv_get_peer, uv_get_fd}; - -grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, - grpc_resource_quota *resource_quota, - char *peer_string) { - grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); - } - - /* Disable Nagle's Algorithm */ - uv_tcp_nodelay(handle, 1); - - memset(tcp, 0, sizeof(grpc_tcp)); - tcp->base.vtable = &vtable; - tcp->handle = handle; - handle->data = tcp; - gpr_ref_init(&tcp->refcount, 1); - tcp->peer_string = gpr_strdup(peer_string); - tcp->shutting_down = false; - tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); - tcp->read_slice = alloc_read_slice(&exec_ctx, tcp->resource_user); - /* Tell network status tracking code about the new endpoint */ - grpc_network_status_register_endpoint(&tcp->base); - -#ifndef GRPC_UV_TCP_HOLD_LOOP - uv_unref((uv_handle_t *)handle); -#endif - - grpc_exec_ctx_finish(&exec_ctx); - return &tcp->base; -} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_uv.cc b/src/core/lib/iomgr/tcp_uv.cc new file mode 100644 index 0000000000..a05c19b4ac --- /dev/null +++ b/src/core/lib/iomgr/tcp_uv.cc @@ -0,0 +1,381 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_UV + +#include +#include + +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/tcp_uv.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp"); + +typedef struct { + grpc_endpoint base; + gpr_refcount refcount; + + uv_write_t write_req; + uv_shutdown_t shutdown_req; + + uv_tcp_t *handle; + + grpc_closure *read_cb; + grpc_closure *write_cb; + + grpc_slice read_slice; + grpc_slice_buffer *read_slices; + grpc_slice_buffer *write_slices; + uv_buf_t *write_buffers; + + grpc_resource_user *resource_user; + + bool shutting_down; + + char *peer_string; + grpc_pollset *pollset; +} grpc_tcp; + +static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + grpc_slice_unref_internal(exec_ctx, tcp->read_slice); + grpc_resource_user_unref(exec_ctx, tcp->resource_user); + gpr_free(tcp->handle); + gpr_free(tcp->peer_string); + gpr_free(tcp); +} + +#ifndef NDEBUG +#define TCP_UNREF(exec_ctx, tcp, reason) \ + tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, + const char *reason, const char *file, int line) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val + 1); + } + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(exec_ctx, tcp, reason) tcp_unref((exec_ctx), (tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +static void uv_close_callback(uv_handle_t *handle) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_tcp *tcp = handle->data; + TCP_UNREF(&exec_ctx, tcp, "destroy"); + grpc_exec_ctx_finish(&exec_ctx); +} + +static grpc_slice alloc_read_slice(grpc_exec_ctx *exec_ctx, + grpc_resource_user *resource_user) { + return grpc_resource_user_slice_malloc(exec_ctx, resource_user, + GRPC_TCP_DEFAULT_READ_SLICE_SIZE); +} + +static void alloc_uv_buf(uv_handle_t *handle, size_t suggested_size, + uv_buf_t *buf) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_tcp *tcp = handle->data; + (void)suggested_size; + buf->base = (char *)GRPC_SLICE_START_PTR(tcp->read_slice); + buf->len = GRPC_SLICE_LENGTH(tcp->read_slice); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void read_callback(uv_stream_t *stream, ssize_t nread, + const uv_buf_t *buf) { + grpc_slice sub; + grpc_error *error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_tcp *tcp = stream->data; + grpc_closure *cb = tcp->read_cb; + if (nread == 0) { + // Nothing happened. Wait for the next callback + return; + } + TCP_UNREF(&exec_ctx, tcp, "read"); + tcp->read_cb = NULL; + // TODO(murgatroid99): figure out what the return value here means + uv_read_stop(stream); + if (nread == UV_EOF) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("EOF"); + } else if (nread > 0) { + // Successful read + sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, (size_t)nread); + grpc_slice_buffer_add(tcp->read_slices, sub); + tcp->read_slice = alloc_read_slice(&exec_ctx, tcp->resource_user); + error = GRPC_ERROR_NONE; + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + size_t i; + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "read: error=%s", str); + + for (i = 0; i < tcp->read_slices->count; i++) { + char *dump = grpc_dump_slice(tcp->read_slices->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p (peer=%s): %s", tcp, tcp->peer_string, + dump); + gpr_free(dump); + } + } + } else { + // nread < 0: Error + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed"); + } + GRPC_CLOSURE_SCHED(&exec_ctx, cb, error); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void uv_endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *read_slices, grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + int status; + grpc_error *error = GRPC_ERROR_NONE; + GRPC_UV_ASSERT_SAME_THREAD(); + GPR_ASSERT(tcp->read_cb == NULL); + tcp->read_cb = cb; + tcp->read_slices = read_slices; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices); + TCP_REF(tcp, "read"); + // TODO(murgatroid99): figure out what the return value here means + status = + uv_read_start((uv_stream_t *)tcp->handle, alloc_uv_buf, read_callback); + if (status != 0) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Read failed at start"); + error = + grpc_error_set_str(error, GRPC_ERROR_STR_OS_ERROR, + grpc_slice_from_static_string(uv_strerror(status))); + GRPC_CLOSURE_SCHED(exec_ctx, cb, error); + } + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Initiating read on %p: error=%s", tcp, str); + } +} + +static void write_callback(uv_write_t *req, int status) { + grpc_tcp *tcp = req->data; + grpc_error *error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_closure *cb = tcp->write_cb; + tcp->write_cb = NULL; + TCP_UNREF(&exec_ctx, tcp, "write"); + if (status == 0) { + error = GRPC_ERROR_NONE; + } else { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("TCP Write failed"); + } + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(error); + gpr_log(GPR_DEBUG, "write complete on %p: error=%s", tcp, str); + } + gpr_free(tcp->write_buffers); + grpc_resource_user_free(&exec_ctx, tcp->resource_user, + sizeof(uv_buf_t) * tcp->write_slices->count); + GRPC_CLOSURE_SCHED(&exec_ctx, cb, error); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void uv_endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *write_slices, + grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + uv_buf_t *buffers; + unsigned int buffer_count; + unsigned int i; + grpc_slice *slice; + uv_write_t *write_req; + GRPC_UV_ASSERT_SAME_THREAD(); + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + size_t j; + + for (j = 0; j < write_slices->count; j++) { + char *data = grpc_dump_slice(write_slices->slices[j], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p (peer=%s): %s", tcp, tcp->peer_string, data); + gpr_free(data); + } + } + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TCP socket is shutting down")); + return; + } + + GPR_ASSERT(tcp->write_cb == NULL); + tcp->write_slices = write_slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + if (tcp->write_slices->count == 0) { + // No slices means we don't have to do anything, + // and libuv doesn't like empty writes + GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_ERROR_NONE); + return; + } + + tcp->write_cb = cb; + buffer_count = (unsigned int)tcp->write_slices->count; + buffers = gpr_malloc(sizeof(uv_buf_t) * buffer_count); + grpc_resource_user_alloc(exec_ctx, tcp->resource_user, + sizeof(uv_buf_t) * buffer_count, NULL); + for (i = 0; i < buffer_count; i++) { + slice = &tcp->write_slices->slices[i]; + buffers[i].base = (char *)GRPC_SLICE_START_PTR(*slice); + buffers[i].len = GRPC_SLICE_LENGTH(*slice); + } + tcp->write_buffers = buffers; + write_req = &tcp->write_req; + write_req->data = tcp; + TCP_REF(tcp, "write"); + // TODO(murgatroid99): figure out what the return value here means + uv_write(write_req, (uv_stream_t *)tcp->handle, buffers, buffer_count, + write_callback); +} + +static void uv_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *pollset) { + // No-op. We're ignoring pollsets currently + (void)exec_ctx; + (void)ep; + (void)pollset; + grpc_tcp *tcp = (grpc_tcp *)ep; + tcp->pollset = pollset; +} + +static void uv_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pollset) { + // No-op. We're ignoring pollsets currently + (void)exec_ctx; + (void)ep; + (void)pollset; +} + +static void shutdown_callback(uv_shutdown_t *req, int status) {} + +static void uv_endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_error *why) { + grpc_tcp *tcp = (grpc_tcp *)ep; + if (!tcp->shutting_down) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + const char *str = grpc_error_string(why); + gpr_log(GPR_DEBUG, "TCP %p shutdown why=%s", tcp->handle, str); + } + tcp->shutting_down = true; + uv_shutdown_t *req = &tcp->shutdown_req; + uv_shutdown(req, (uv_stream_t *)tcp->handle, shutdown_callback); + grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); + } + GRPC_ERROR_UNREF(why); +} + +static void uv_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp *tcp = (grpc_tcp *)ep; + uv_close((uv_handle_t *)tcp->handle, uv_close_callback); +} + +static char *uv_get_peer(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_resource_user *uv_get_resource_user(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return tcp->resource_user; +} + +static int uv_get_fd(grpc_endpoint *ep) { return -1; } + +static grpc_endpoint_vtable vtable = { + uv_endpoint_read, uv_endpoint_write, uv_add_to_pollset, + uv_add_to_pollset_set, uv_endpoint_shutdown, uv_destroy, + uv_get_resource_user, uv_get_peer, uv_get_fd}; + +grpc_endpoint *grpc_tcp_create(uv_tcp_t *handle, + grpc_resource_quota *resource_quota, + char *peer_string) { + grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_log(GPR_DEBUG, "Creating TCP endpoint %p", tcp); + } + + /* Disable Nagle's Algorithm */ + uv_tcp_nodelay(handle, 1); + + memset(tcp, 0, sizeof(grpc_tcp)); + tcp->base.vtable = &vtable; + tcp->handle = handle; + handle->data = tcp; + gpr_ref_init(&tcp->refcount, 1); + tcp->peer_string = gpr_strdup(peer_string); + tcp->shutting_down = false; + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + tcp->read_slice = alloc_read_slice(&exec_ctx, tcp->resource_user); + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + +#ifndef GRPC_UV_TCP_HOLD_LOOP + uv_unref((uv_handle_t *)handle); +#endif + + grpc_exec_ctx_finish(&exec_ctx); + return &tcp->base; +} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/tcp_windows.c b/src/core/lib/iomgr/tcp_windows.c deleted file mode 100644 index 2cbb97403b..0000000000 --- a/src/core/lib/iomgr/tcp_windows.c +++ /dev/null @@ -1,448 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_WINSOCK_SOCKET - -#include - -#include "src/core/lib/iomgr/network_status_tracker.h" -#include "src/core/lib/iomgr/sockaddr_windows.h" - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/iomgr/iocp_windows.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/socket_windows.h" -#include "src/core/lib/iomgr/tcp_client.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/slice/slice_internal.h" - -#if defined(__MSYS__) && defined(GPR_ARCH_64) -/* Nasty workaround for nasty bug when using the 64 bits msys compiler - in conjunction with Microsoft Windows headers. */ -#define GRPC_FIONBIO _IOW('f', 126, uint32_t) -#else -#define GRPC_FIONBIO FIONBIO -#endif - -grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp"); - -static grpc_error *set_non_block(SOCKET sock) { - int status; - uint32_t param = 1; - DWORD ret; - status = WSAIoctl(sock, GRPC_FIONBIO, ¶m, sizeof(param), NULL, 0, &ret, - NULL, NULL); - return status == 0 - ? GRPC_ERROR_NONE - : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)"); -} - -static grpc_error *set_dualstack(SOCKET sock) { - int status; - unsigned long param = 0; - status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)¶m, - sizeof(param)); - return status == 0 - ? GRPC_ERROR_NONE - : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)"); -} - -grpc_error *grpc_tcp_prepare_socket(SOCKET sock) { - grpc_error *err; - err = set_non_block(sock); - if (err != GRPC_ERROR_NONE) return err; - err = set_dualstack(sock); - if (err != GRPC_ERROR_NONE) return err; - return GRPC_ERROR_NONE; -} - -typedef struct grpc_tcp { - /* This is our C++ class derivation emulation. */ - grpc_endpoint base; - /* The one socket this endpoint is using. */ - grpc_winsocket *socket; - /* Refcounting how many operations are in progress. */ - gpr_refcount refcount; - - grpc_closure on_read; - grpc_closure on_write; - - grpc_closure *read_cb; - grpc_closure *write_cb; - grpc_slice read_slice; - grpc_slice_buffer *write_slices; - grpc_slice_buffer *read_slices; - - grpc_resource_user *resource_user; - - /* The IO Completion Port runs from another thread. We need some mechanism - to protect ourselves when requesting a shutdown. */ - gpr_mu mu; - int shutting_down; - grpc_error *shutdown_error; - - char *peer_string; -} grpc_tcp; - -static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - grpc_winsocket_destroy(tcp->socket); - gpr_mu_destroy(&tcp->mu); - gpr_free(tcp->peer_string); - grpc_resource_user_unref(exec_ctx, tcp->resource_user); - if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error); - gpr_free(tcp); -} - -#ifndef NDEBUG -#define TCP_UNREF(exec_ctx, tcp, reason) \ - tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__) -#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val - 1); - } - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_tcp_trace)) { - gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, - val + 1); - } - gpr_ref(&tcp->refcount); -} -#else -#define TCP_UNREF(exec_ctx, tcp, reason) tcp_unref((exec_ctx), (tcp)) -#define TCP_REF(tcp, reason) tcp_ref((tcp)) -static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { - if (gpr_unref(&tcp->refcount)) { - tcp_free(exec_ctx, tcp); - } -} - -static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } -#endif - -/* Asynchronous callback from the IOCP, or the background thread. */ -static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) { - grpc_tcp *tcp = tcpp; - grpc_closure *cb = tcp->read_cb; - grpc_winsocket *socket = tcp->socket; - grpc_slice sub; - grpc_winsocket_callback_info *info = &socket->read_info; - - GRPC_ERROR_REF(error); - - if (error == GRPC_ERROR_NONE) { - if (info->wsa_error != 0 && !tcp->shutting_down) { - char *utf8_message = gpr_format_message(info->wsa_error); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(utf8_message); - gpr_free(utf8_message); - grpc_slice_unref_internal(exec_ctx, tcp->read_slice); - } else { - if (info->bytes_transfered != 0 && !tcp->shutting_down) { - sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered); - grpc_slice_buffer_add(tcp->read_slices, sub); - } else { - grpc_slice_unref_internal(exec_ctx, tcp->read_slice); - error = tcp->shutting_down - ? GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "TCP stream shutting down", &tcp->shutdown_error, 1) - : GRPC_ERROR_CREATE_FROM_STATIC_STRING("End of TCP stream"); - } - } - } - - tcp->read_cb = NULL; - TCP_UNREF(exec_ctx, tcp, "read"); - GRPC_CLOSURE_SCHED(exec_ctx, cb, error); -} - -static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_slice_buffer *read_slices, grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_winsocket *handle = tcp->socket; - grpc_winsocket_callback_info *info = &handle->read_info; - int status; - DWORD bytes_read = 0; - DWORD flags = 0; - WSABUF buffer; - - if (tcp->shutting_down) { - GRPC_CLOSURE_SCHED( - exec_ctx, cb, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "TCP socket is shutting down", &tcp->shutdown_error, 1)); - return; - } - - tcp->read_cb = cb; - tcp->read_slices = read_slices; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices); - - tcp->read_slice = GRPC_SLICE_MALLOC(8192); - - buffer.len = (ULONG)GRPC_SLICE_LENGTH( - tcp->read_slice); // we know slice size fits in 32bit. - buffer.buf = (char *)GRPC_SLICE_START_PTR(tcp->read_slice); - - TCP_REF(tcp, "read"); - - /* First let's try a synchronous, non-blocking read. */ - status = - WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL); - info->wsa_error = status == 0 ? 0 : WSAGetLastError(); - - /* Did we get data immediately ? Yay. */ - if (info->wsa_error != WSAEWOULDBLOCK) { - info->bytes_transfered = bytes_read; - GRPC_CLOSURE_SCHED(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE); - return; - } - - /* Otherwise, let's retry, by queuing a read. */ - memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); - status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, - &info->overlapped, NULL); - - if (status != 0) { - int wsa_error = WSAGetLastError(); - if (wsa_error != WSA_IO_PENDING) { - info->wsa_error = wsa_error; - GRPC_CLOSURE_SCHED(exec_ctx, &tcp->on_read, - GRPC_WSA_ERROR(info->wsa_error, "WSARecv")); - return; - } - } - - grpc_socket_notify_on_read(exec_ctx, tcp->socket, &tcp->on_read); -} - -/* Asynchronous callback from the IOCP, or the background thread. */ -static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) { - grpc_tcp *tcp = (grpc_tcp *)tcpp; - grpc_winsocket *handle = tcp->socket; - grpc_winsocket_callback_info *info = &handle->write_info; - grpc_closure *cb; - - GRPC_ERROR_REF(error); - - gpr_mu_lock(&tcp->mu); - cb = tcp->write_cb; - tcp->write_cb = NULL; - gpr_mu_unlock(&tcp->mu); - - if (error == GRPC_ERROR_NONE) { - if (info->wsa_error != 0) { - error = GRPC_WSA_ERROR(info->wsa_error, "WSASend"); - } else { - GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length); - } - } - - TCP_UNREF(exec_ctx, tcp, "write"); - GRPC_CLOSURE_SCHED(exec_ctx, cb, error); -} - -/* Initiates a write. */ -static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_slice_buffer *slices, grpc_closure *cb) { - grpc_tcp *tcp = (grpc_tcp *)ep; - grpc_winsocket *socket = tcp->socket; - grpc_winsocket_callback_info *info = &socket->write_info; - unsigned i; - DWORD bytes_sent; - int status; - WSABUF local_buffers[16]; - WSABUF *allocated = NULL; - WSABUF *buffers = local_buffers; - size_t len; - - if (tcp->shutting_down) { - GRPC_CLOSURE_SCHED( - exec_ctx, cb, - GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "TCP socket is shutting down", &tcp->shutdown_error, 1)); - return; - } - - tcp->write_cb = cb; - tcp->write_slices = slices; - GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); - if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) { - buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count); - allocated = buffers; - } - - for (i = 0; i < tcp->write_slices->count; i++) { - len = GRPC_SLICE_LENGTH(tcp->write_slices->slices[i]); - GPR_ASSERT(len <= ULONG_MAX); - buffers[i].len = (ULONG)len; - buffers[i].buf = (char *)GRPC_SLICE_START_PTR(tcp->write_slices->slices[i]); - } - - /* First, let's try a synchronous, non-blocking write. */ - status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count, - &bytes_sent, 0, NULL, NULL); - info->wsa_error = status == 0 ? 0 : WSAGetLastError(); - - /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy - connection that has its send queue filled up. But if we don't, then we can - avoid doing an async write operation at all. */ - if (info->wsa_error != WSAEWOULDBLOCK) { - grpc_error *error = status == 0 - ? GRPC_ERROR_NONE - : GRPC_WSA_ERROR(info->wsa_error, "WSASend"); - GRPC_CLOSURE_SCHED(exec_ctx, cb, error); - if (allocated) gpr_free(allocated); - return; - } - - TCP_REF(tcp, "write"); - - /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same - operation, this time asynchronously. */ - memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); - status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count, - &bytes_sent, 0, &socket->write_info.overlapped, NULL); - if (allocated) gpr_free(allocated); - - if (status != 0) { - int wsa_error = WSAGetLastError(); - if (wsa_error != WSA_IO_PENDING) { - TCP_UNREF(exec_ctx, tcp, "write"); - GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend")); - return; - } - } - - /* As all is now setup, we can now ask for the IOCP notification. It may - trigger the callback immediately however, but no matter. */ - grpc_socket_notify_on_write(exec_ctx, socket, &tcp->on_write); -} - -static void win_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset *ps) { - grpc_tcp *tcp; - (void)ps; - tcp = (grpc_tcp *)ep; - grpc_iocp_add_socket(tcp->socket); -} - -static void win_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_pollset_set *pss) { - grpc_tcp *tcp; - (void)pss; - tcp = (grpc_tcp *)ep; - grpc_iocp_add_socket(tcp->socket); -} - -/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks - for the potential read and write operations. It is up to the caller to - guarantee this isn't called in parallel to a read or write request, so - we're not going to protect against these. However the IO Completion Port - callback will happen from another thread, so we need to protect against - concurrent access of the data structure in that regard. */ -static void win_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, - grpc_error *why) { - grpc_tcp *tcp = (grpc_tcp *)ep; - gpr_mu_lock(&tcp->mu); - /* At that point, what may happen is that we're already inside the IOCP - callback. See the comments in on_read and on_write. */ - if (!tcp->shutting_down) { - tcp->shutting_down = 1; - tcp->shutdown_error = why; - } else { - GRPC_ERROR_UNREF(why); - } - grpc_winsocket_shutdown(tcp->socket); - gpr_mu_unlock(&tcp->mu); - grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); -} - -static void win_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { - grpc_network_status_unregister_endpoint(ep); - grpc_tcp *tcp = (grpc_tcp *)ep; - TCP_UNREF(exec_ctx, tcp, "destroy"); -} - -static char *win_get_peer(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return gpr_strdup(tcp->peer_string); -} - -static grpc_resource_user *win_get_resource_user(grpc_endpoint *ep) { - grpc_tcp *tcp = (grpc_tcp *)ep; - return tcp->resource_user; -} - -static int win_get_fd(grpc_endpoint *ep) { return -1; } - -static grpc_endpoint_vtable vtable = { - win_read, win_write, win_add_to_pollset, win_add_to_pollset_set, - win_shutdown, win_destroy, win_get_resource_user, win_get_peer, - win_get_fd}; - -grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, - grpc_channel_args *channel_args, - char *peer_string) { - grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); - if (channel_args != NULL) { - for (size_t i = 0; i < channel_args->num_args; i++) { - if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - resource_quota = grpc_resource_quota_ref_internal( - channel_args->args[i].value.pointer.p); - } - } - } - grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); - memset(tcp, 0, sizeof(grpc_tcp)); - tcp->base.vtable = &vtable; - tcp->socket = socket; - gpr_mu_init(&tcp->mu); - gpr_ref_init(&tcp->refcount, 1); - GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx); - tcp->peer_string = gpr_strdup(peer_string); - tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); - /* Tell network status tracking code about the new endpoint */ - grpc_network_status_register_endpoint(&tcp->base); - - return &tcp->base; -} - -#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc new file mode 100644 index 0000000000..2cbb97403b --- /dev/null +++ b/src/core/lib/iomgr/tcp_windows.cc @@ -0,0 +1,448 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_WINSOCK_SOCKET + +#include + +#include "src/core/lib/iomgr/network_status_tracker.h" +#include "src/core/lib/iomgr/sockaddr_windows.h" + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/iocp_windows.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_windows.h" +#include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/slice/slice_internal.h" + +#if defined(__MSYS__) && defined(GPR_ARCH_64) +/* Nasty workaround for nasty bug when using the 64 bits msys compiler + in conjunction with Microsoft Windows headers. */ +#define GRPC_FIONBIO _IOW('f', 126, uint32_t) +#else +#define GRPC_FIONBIO FIONBIO +#endif + +grpc_tracer_flag grpc_tcp_trace = GRPC_TRACER_INITIALIZER(false, "tcp"); + +static grpc_error *set_non_block(SOCKET sock) { + int status; + uint32_t param = 1; + DWORD ret; + status = WSAIoctl(sock, GRPC_FIONBIO, ¶m, sizeof(param), NULL, 0, &ret, + NULL, NULL); + return status == 0 + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(WSAGetLastError(), "WSAIoctl(GRPC_FIONBIO)"); +} + +static grpc_error *set_dualstack(SOCKET sock) { + int status; + unsigned long param = 0; + status = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)¶m, + sizeof(param)); + return status == 0 + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(WSAGetLastError(), "setsockopt(IPV6_V6ONLY)"); +} + +grpc_error *grpc_tcp_prepare_socket(SOCKET sock) { + grpc_error *err; + err = set_non_block(sock); + if (err != GRPC_ERROR_NONE) return err; + err = set_dualstack(sock); + if (err != GRPC_ERROR_NONE) return err; + return GRPC_ERROR_NONE; +} + +typedef struct grpc_tcp { + /* This is our C++ class derivation emulation. */ + grpc_endpoint base; + /* The one socket this endpoint is using. */ + grpc_winsocket *socket; + /* Refcounting how many operations are in progress. */ + gpr_refcount refcount; + + grpc_closure on_read; + grpc_closure on_write; + + grpc_closure *read_cb; + grpc_closure *write_cb; + grpc_slice read_slice; + grpc_slice_buffer *write_slices; + grpc_slice_buffer *read_slices; + + grpc_resource_user *resource_user; + + /* The IO Completion Port runs from another thread. We need some mechanism + to protect ourselves when requesting a shutdown. */ + gpr_mu mu; + int shutting_down; + grpc_error *shutdown_error; + + char *peer_string; +} grpc_tcp; + +static void tcp_free(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + grpc_winsocket_destroy(tcp->socket); + gpr_mu_destroy(&tcp->mu); + gpr_free(tcp->peer_string); + grpc_resource_user_unref(exec_ctx, tcp->resource_user); + if (tcp->shutting_down) GRPC_ERROR_UNREF(tcp->shutdown_error); + gpr_free(tcp); +} + +#ifndef NDEBUG +#define TCP_UNREF(exec_ctx, tcp, reason) \ + tcp_unref((exec_ctx), (tcp), (reason), __FILE__, __LINE__) +#define TCP_REF(tcp, reason) tcp_ref((tcp), (reason), __FILE__, __LINE__) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp, + const char *reason, const char *file, int line) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val - 1); + } + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp, const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_tcp_trace)) { + gpr_atm val = gpr_atm_no_barrier_load(&tcp->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "TCP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, tcp, reason, val, + val + 1); + } + gpr_ref(&tcp->refcount); +} +#else +#define TCP_UNREF(exec_ctx, tcp, reason) tcp_unref((exec_ctx), (tcp)) +#define TCP_REF(tcp, reason) tcp_ref((tcp)) +static void tcp_unref(grpc_exec_ctx *exec_ctx, grpc_tcp *tcp) { + if (gpr_unref(&tcp->refcount)) { + tcp_free(exec_ctx, tcp); + } +} + +static void tcp_ref(grpc_tcp *tcp) { gpr_ref(&tcp->refcount); } +#endif + +/* Asynchronous callback from the IOCP, or the background thread. */ +static void on_read(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) { + grpc_tcp *tcp = tcpp; + grpc_closure *cb = tcp->read_cb; + grpc_winsocket *socket = tcp->socket; + grpc_slice sub; + grpc_winsocket_callback_info *info = &socket->read_info; + + GRPC_ERROR_REF(error); + + if (error == GRPC_ERROR_NONE) { + if (info->wsa_error != 0 && !tcp->shutting_down) { + char *utf8_message = gpr_format_message(info->wsa_error); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(utf8_message); + gpr_free(utf8_message); + grpc_slice_unref_internal(exec_ctx, tcp->read_slice); + } else { + if (info->bytes_transfered != 0 && !tcp->shutting_down) { + sub = grpc_slice_sub_no_ref(tcp->read_slice, 0, info->bytes_transfered); + grpc_slice_buffer_add(tcp->read_slices, sub); + } else { + grpc_slice_unref_internal(exec_ctx, tcp->read_slice); + error = tcp->shutting_down + ? GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "TCP stream shutting down", &tcp->shutdown_error, 1) + : GRPC_ERROR_CREATE_FROM_STATIC_STRING("End of TCP stream"); + } + } + } + + tcp->read_cb = NULL; + TCP_UNREF(exec_ctx, tcp, "read"); + GRPC_CLOSURE_SCHED(exec_ctx, cb, error); +} + +static void win_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *read_slices, grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_winsocket *handle = tcp->socket; + grpc_winsocket_callback_info *info = &handle->read_info; + int status; + DWORD bytes_read = 0; + DWORD flags = 0; + WSABUF buffer; + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED( + exec_ctx, cb, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "TCP socket is shutting down", &tcp->shutdown_error, 1)); + return; + } + + tcp->read_cb = cb; + tcp->read_slices = read_slices; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, read_slices); + + tcp->read_slice = GRPC_SLICE_MALLOC(8192); + + buffer.len = (ULONG)GRPC_SLICE_LENGTH( + tcp->read_slice); // we know slice size fits in 32bit. + buffer.buf = (char *)GRPC_SLICE_START_PTR(tcp->read_slice); + + TCP_REF(tcp, "read"); + + /* First let's try a synchronous, non-blocking read. */ + status = + WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, NULL, NULL); + info->wsa_error = status == 0 ? 0 : WSAGetLastError(); + + /* Did we get data immediately ? Yay. */ + if (info->wsa_error != WSAEWOULDBLOCK) { + info->bytes_transfered = bytes_read; + GRPC_CLOSURE_SCHED(exec_ctx, &tcp->on_read, GRPC_ERROR_NONE); + return; + } + + /* Otherwise, let's retry, by queuing a read. */ + memset(&tcp->socket->read_info.overlapped, 0, sizeof(OVERLAPPED)); + status = WSARecv(tcp->socket->socket, &buffer, 1, &bytes_read, &flags, + &info->overlapped, NULL); + + if (status != 0) { + int wsa_error = WSAGetLastError(); + if (wsa_error != WSA_IO_PENDING) { + info->wsa_error = wsa_error; + GRPC_CLOSURE_SCHED(exec_ctx, &tcp->on_read, + GRPC_WSA_ERROR(info->wsa_error, "WSARecv")); + return; + } + } + + grpc_socket_notify_on_read(exec_ctx, tcp->socket, &tcp->on_read); +} + +/* Asynchronous callback from the IOCP, or the background thread. */ +static void on_write(grpc_exec_ctx *exec_ctx, void *tcpp, grpc_error *error) { + grpc_tcp *tcp = (grpc_tcp *)tcpp; + grpc_winsocket *handle = tcp->socket; + grpc_winsocket_callback_info *info = &handle->write_info; + grpc_closure *cb; + + GRPC_ERROR_REF(error); + + gpr_mu_lock(&tcp->mu); + cb = tcp->write_cb; + tcp->write_cb = NULL; + gpr_mu_unlock(&tcp->mu); + + if (error == GRPC_ERROR_NONE) { + if (info->wsa_error != 0) { + error = GRPC_WSA_ERROR(info->wsa_error, "WSASend"); + } else { + GPR_ASSERT(info->bytes_transfered == tcp->write_slices->length); + } + } + + TCP_UNREF(exec_ctx, tcp, "write"); + GRPC_CLOSURE_SCHED(exec_ctx, cb, error); +} + +/* Initiates a write. */ +static void win_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_slice_buffer *slices, grpc_closure *cb) { + grpc_tcp *tcp = (grpc_tcp *)ep; + grpc_winsocket *socket = tcp->socket; + grpc_winsocket_callback_info *info = &socket->write_info; + unsigned i; + DWORD bytes_sent; + int status; + WSABUF local_buffers[16]; + WSABUF *allocated = NULL; + WSABUF *buffers = local_buffers; + size_t len; + + if (tcp->shutting_down) { + GRPC_CLOSURE_SCHED( + exec_ctx, cb, + GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "TCP socket is shutting down", &tcp->shutdown_error, 1)); + return; + } + + tcp->write_cb = cb; + tcp->write_slices = slices; + GPR_ASSERT(tcp->write_slices->count <= UINT_MAX); + if (tcp->write_slices->count > GPR_ARRAY_SIZE(local_buffers)) { + buffers = (WSABUF *)gpr_malloc(sizeof(WSABUF) * tcp->write_slices->count); + allocated = buffers; + } + + for (i = 0; i < tcp->write_slices->count; i++) { + len = GRPC_SLICE_LENGTH(tcp->write_slices->slices[i]); + GPR_ASSERT(len <= ULONG_MAX); + buffers[i].len = (ULONG)len; + buffers[i].buf = (char *)GRPC_SLICE_START_PTR(tcp->write_slices->slices[i]); + } + + /* First, let's try a synchronous, non-blocking write. */ + status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count, + &bytes_sent, 0, NULL, NULL); + info->wsa_error = status == 0 ? 0 : WSAGetLastError(); + + /* We would kind of expect to get a WSAEWOULDBLOCK here, especially on a busy + connection that has its send queue filled up. But if we don't, then we can + avoid doing an async write operation at all. */ + if (info->wsa_error != WSAEWOULDBLOCK) { + grpc_error *error = status == 0 + ? GRPC_ERROR_NONE + : GRPC_WSA_ERROR(info->wsa_error, "WSASend"); + GRPC_CLOSURE_SCHED(exec_ctx, cb, error); + if (allocated) gpr_free(allocated); + return; + } + + TCP_REF(tcp, "write"); + + /* If we got a WSAEWOULDBLOCK earlier, then we need to re-do the same + operation, this time asynchronously. */ + memset(&socket->write_info.overlapped, 0, sizeof(OVERLAPPED)); + status = WSASend(socket->socket, buffers, (DWORD)tcp->write_slices->count, + &bytes_sent, 0, &socket->write_info.overlapped, NULL); + if (allocated) gpr_free(allocated); + + if (status != 0) { + int wsa_error = WSAGetLastError(); + if (wsa_error != WSA_IO_PENDING) { + TCP_UNREF(exec_ctx, tcp, "write"); + GRPC_CLOSURE_SCHED(exec_ctx, cb, GRPC_WSA_ERROR(wsa_error, "WSASend")); + return; + } + } + + /* As all is now setup, we can now ask for the IOCP notification. It may + trigger the callback immediately however, but no matter. */ + grpc_socket_notify_on_write(exec_ctx, socket, &tcp->on_write); +} + +static void win_add_to_pollset(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset *ps) { + grpc_tcp *tcp; + (void)ps; + tcp = (grpc_tcp *)ep; + grpc_iocp_add_socket(tcp->socket); +} + +static void win_add_to_pollset_set(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_pollset_set *pss) { + grpc_tcp *tcp; + (void)pss; + tcp = (grpc_tcp *)ep; + grpc_iocp_add_socket(tcp->socket); +} + +/* Initiates a shutdown of the TCP endpoint. This will queue abort callbacks + for the potential read and write operations. It is up to the caller to + guarantee this isn't called in parallel to a read or write request, so + we're not going to protect against these. However the IO Completion Port + callback will happen from another thread, so we need to protect against + concurrent access of the data structure in that regard. */ +static void win_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep, + grpc_error *why) { + grpc_tcp *tcp = (grpc_tcp *)ep; + gpr_mu_lock(&tcp->mu); + /* At that point, what may happen is that we're already inside the IOCP + callback. See the comments in on_read and on_write. */ + if (!tcp->shutting_down) { + tcp->shutting_down = 1; + tcp->shutdown_error = why; + } else { + GRPC_ERROR_UNREF(why); + } + grpc_winsocket_shutdown(tcp->socket); + gpr_mu_unlock(&tcp->mu); + grpc_resource_user_shutdown(exec_ctx, tcp->resource_user); +} + +static void win_destroy(grpc_exec_ctx *exec_ctx, grpc_endpoint *ep) { + grpc_network_status_unregister_endpoint(ep); + grpc_tcp *tcp = (grpc_tcp *)ep; + TCP_UNREF(exec_ctx, tcp, "destroy"); +} + +static char *win_get_peer(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return gpr_strdup(tcp->peer_string); +} + +static grpc_resource_user *win_get_resource_user(grpc_endpoint *ep) { + grpc_tcp *tcp = (grpc_tcp *)ep; + return tcp->resource_user; +} + +static int win_get_fd(grpc_endpoint *ep) { return -1; } + +static grpc_endpoint_vtable vtable = { + win_read, win_write, win_add_to_pollset, win_add_to_pollset_set, + win_shutdown, win_destroy, win_get_resource_user, win_get_peer, + win_get_fd}; + +grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, + grpc_channel_args *channel_args, + char *peer_string) { + grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); + if (channel_args != NULL) { + for (size_t i = 0; i < channel_args->num_args; i++) { + if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + resource_quota = grpc_resource_quota_ref_internal( + channel_args->args[i].value.pointer.p); + } + } + } + grpc_tcp *tcp = (grpc_tcp *)gpr_malloc(sizeof(grpc_tcp)); + memset(tcp, 0, sizeof(grpc_tcp)); + tcp->base.vtable = &vtable; + tcp->socket = socket; + gpr_mu_init(&tcp->mu); + gpr_ref_init(&tcp->refcount, 1); + GRPC_CLOSURE_INIT(&tcp->on_read, on_read, tcp, grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&tcp->on_write, on_write, tcp, grpc_schedule_on_exec_ctx); + tcp->peer_string = gpr_strdup(peer_string); + tcp->resource_user = grpc_resource_user_create(resource_quota, peer_string); + /* Tell network status tracking code about the new endpoint */ + grpc_network_status_register_endpoint(&tcp->base); + + return &tcp->base; +} + +#endif /* GRPC_WINSOCK_SOCKET */ diff --git a/src/core/lib/iomgr/time_averaged_stats.c b/src/core/lib/iomgr/time_averaged_stats.c deleted file mode 100644 index 3bddec04dd..0000000000 --- a/src/core/lib/iomgr/time_averaged_stats.c +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/time_averaged_stats.h" - -void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats, - double init_avg, double regress_weight, - double persistence_factor) { - stats->init_avg = init_avg; - stats->regress_weight = regress_weight; - stats->persistence_factor = persistence_factor; - stats->batch_total_value = 0; - stats->batch_num_samples = 0; - stats->aggregate_total_weight = 0; - stats->aggregate_weighted_avg = init_avg; -} - -void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats, - double value) { - stats->batch_total_value += value; - ++stats->batch_num_samples; -} - -double grpc_time_averaged_stats_update_average( - grpc_time_averaged_stats* stats) { - /* Start with the current batch: */ - double weighted_sum = stats->batch_total_value; - double total_weight = stats->batch_num_samples; - if (stats->regress_weight > 0) { - /* Add in the regression towards init_avg_: */ - weighted_sum += stats->regress_weight * stats->init_avg; - total_weight += stats->regress_weight; - } - if (stats->persistence_factor > 0) { - /* Add in the persistence: */ - const double prev_sample_weight = - stats->persistence_factor * stats->aggregate_total_weight; - weighted_sum += prev_sample_weight * stats->aggregate_weighted_avg; - total_weight += prev_sample_weight; - } - stats->aggregate_weighted_avg = - (total_weight > 0) ? (weighted_sum / total_weight) : stats->init_avg; - stats->aggregate_total_weight = total_weight; - stats->batch_num_samples = 0; - stats->batch_total_value = 0; - return stats->aggregate_weighted_avg; -} diff --git a/src/core/lib/iomgr/time_averaged_stats.cc b/src/core/lib/iomgr/time_averaged_stats.cc new file mode 100644 index 0000000000..3bddec04dd --- /dev/null +++ b/src/core/lib/iomgr/time_averaged_stats.cc @@ -0,0 +1,62 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/time_averaged_stats.h" + +void grpc_time_averaged_stats_init(grpc_time_averaged_stats* stats, + double init_avg, double regress_weight, + double persistence_factor) { + stats->init_avg = init_avg; + stats->regress_weight = regress_weight; + stats->persistence_factor = persistence_factor; + stats->batch_total_value = 0; + stats->batch_num_samples = 0; + stats->aggregate_total_weight = 0; + stats->aggregate_weighted_avg = init_avg; +} + +void grpc_time_averaged_stats_add_sample(grpc_time_averaged_stats* stats, + double value) { + stats->batch_total_value += value; + ++stats->batch_num_samples; +} + +double grpc_time_averaged_stats_update_average( + grpc_time_averaged_stats* stats) { + /* Start with the current batch: */ + double weighted_sum = stats->batch_total_value; + double total_weight = stats->batch_num_samples; + if (stats->regress_weight > 0) { + /* Add in the regression towards init_avg_: */ + weighted_sum += stats->regress_weight * stats->init_avg; + total_weight += stats->regress_weight; + } + if (stats->persistence_factor > 0) { + /* Add in the persistence: */ + const double prev_sample_weight = + stats->persistence_factor * stats->aggregate_total_weight; + weighted_sum += prev_sample_weight * stats->aggregate_weighted_avg; + total_weight += prev_sample_weight; + } + stats->aggregate_weighted_avg = + (total_weight > 0) ? (weighted_sum / total_weight) : stats->init_avg; + stats->aggregate_total_weight = total_weight; + stats->batch_num_samples = 0; + stats->batch_total_value = 0; + return stats->aggregate_weighted_avg; +} diff --git a/src/core/lib/iomgr/timer_generic.c b/src/core/lib/iomgr/timer_generic.c deleted file mode 100644 index 2472cf26be..0000000000 --- a/src/core/lib/iomgr/timer_generic.c +++ /dev/null @@ -1,705 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_TIMER_USE_GENERIC - -#include "src/core/lib/iomgr/timer.h" - -#include -#include -#include -#include -#include -#include -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/time_averaged_stats.h" -#include "src/core/lib/iomgr/timer_heap.h" -#include "src/core/lib/support/spinlock.h" - -#define INVALID_HEAP_INDEX 0xffffffffu - -#define LOG2_NUM_SHARDS 5 -#define NUM_SHARDS (1 << LOG2_NUM_SHARDS) -#define ADD_DEADLINE_SCALE 0.33 -#define MIN_QUEUE_WINDOW_DURATION 0.01 -#define MAX_QUEUE_WINDOW_DURATION 1 - -grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer"); -grpc_tracer_flag grpc_timer_check_trace = - GRPC_TRACER_INITIALIZER(false, "timer_check"); - -/* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with - * deadlines earlier than 'queue_deadline" cap are maintained in the heap and - * others are maintained in the list (unordered). This helps to keep the number - * of elements in the heap low. - * - * The 'queue_deadline_cap' gets recomputed periodically based on the timer - * stats maintained in 'stats' and the relevant timers are then moved from the - * 'list' to 'heap' - */ -typedef struct { - gpr_mu mu; - grpc_time_averaged_stats stats; - /* All and only timers with deadlines <= this will be in the heap. */ - gpr_atm queue_deadline_cap; - /* The deadline of the next timer due in this shard */ - gpr_atm min_deadline; - /* Index of this timer_shard in the g_shard_queue */ - uint32_t shard_queue_index; - /* This holds all timers with deadlines < queue_deadline_cap. Timers in this - list have the top bit of their deadline set to 0. */ - grpc_timer_heap heap; - /* This holds timers whose deadline is >= queue_deadline_cap. */ - grpc_timer list; -} timer_shard; - -/* Array of timer shards. Whenever a timer (grpc_timer *) is added, its address - * is hashed to select the timer shard to add the timer to */ -static timer_shard g_shards[NUM_SHARDS]; - -/* Maintains a sorted list of timer shards (sorted by their min_deadline, i.e - * the deadline of the next timer in each shard). - * Access to this is protected by g_shared_mutables.mu */ -static timer_shard *g_shard_queue[NUM_SHARDS]; - -#ifndef NDEBUG - -/* == Hash table for duplicate timer detection == */ - -#define NUM_HASH_BUCKETS 1009 /* Prime number close to 1000 */ - -static gpr_mu g_hash_mu[NUM_HASH_BUCKETS]; /* One mutex per bucket */ -static grpc_timer *g_timer_ht[NUM_HASH_BUCKETS] = {NULL}; - -static void init_timer_ht() { - for (int i = 0; i < NUM_HASH_BUCKETS; i++) { - gpr_mu_init(&g_hash_mu[i]); - } -} - -static bool is_in_ht(grpc_timer *t) { - size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); - - gpr_mu_lock(&g_hash_mu[i]); - grpc_timer *p = g_timer_ht[i]; - while (p != NULL && p != t) { - p = p->hash_table_next; - } - gpr_mu_unlock(&g_hash_mu[i]); - - return (p == t); -} - -static void add_to_ht(grpc_timer *t) { - GPR_ASSERT(!t->hash_table_next); - size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); - - gpr_mu_lock(&g_hash_mu[i]); - grpc_timer *p = g_timer_ht[i]; - while (p != NULL && p != t) { - p = p->hash_table_next; - } - - if (p == t) { - grpc_closure *c = t->closure; - gpr_log(GPR_ERROR, - "** Duplicate timer (%p) being added. Closure: (%p), created at: " - "(%s:%d), scheduled at: (%s:%d) **", - t, c, c->file_created, c->line_created, c->file_initiated, - c->line_initiated); - abort(); - } - - /* Timer not present in the bucket. Insert at head of the list */ - t->hash_table_next = g_timer_ht[i]; - g_timer_ht[i] = t; - gpr_mu_unlock(&g_hash_mu[i]); -} - -static void remove_from_ht(grpc_timer *t) { - size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); - bool removed = false; - - gpr_mu_lock(&g_hash_mu[i]); - if (g_timer_ht[i] == t) { - g_timer_ht[i] = g_timer_ht[i]->hash_table_next; - removed = true; - } else if (g_timer_ht[i] != NULL) { - grpc_timer *p = g_timer_ht[i]; - while (p->hash_table_next != NULL && p->hash_table_next != t) { - p = p->hash_table_next; - } - - if (p->hash_table_next == t) { - p->hash_table_next = t->hash_table_next; - removed = true; - } - } - gpr_mu_unlock(&g_hash_mu[i]); - - if (!removed) { - grpc_closure *c = t->closure; - gpr_log(GPR_ERROR, - "** Removing timer (%p) that is not added to hash table. Closure " - "(%p), created at: (%s:%d), scheduled at: (%s:%d) **", - t, c, c->file_created, c->line_created, c->file_initiated, - c->line_initiated); - abort(); - } - - t->hash_table_next = NULL; -} - -/* If a timer is added to a timer shard (either heap or a list), it cannot - * be pending. A timer is added to hash table only-if it is added to the - * timer shard. - * Therefore, if timer->pending is false, it cannot be in hash table */ -static void validate_non_pending_timer(grpc_timer *t) { - if (!t->pending && is_in_ht(t)) { - grpc_closure *c = t->closure; - gpr_log(GPR_ERROR, - "** gpr_timer_cancel() called on a non-pending timer (%p) which " - "is in the hash table. Closure: (%p), created at: (%s:%d), " - "scheduled at: (%s:%d) **", - t, c, c->file_created, c->line_created, c->file_initiated, - c->line_initiated); - abort(); - } -} - -#define INIT_TIMER_HASH_TABLE() init_timer_ht() -#define ADD_TO_HASH_TABLE(t) add_to_ht((t)) -#define REMOVE_FROM_HASH_TABLE(t) remove_from_ht((t)) -#define VALIDATE_NON_PENDING_TIMER(t) validate_non_pending_timer((t)) - -#else - -#define INIT_TIMER_HASH_TABLE() -#define ADD_TO_HASH_TABLE(t) -#define REMOVE_FROM_HASH_TABLE(t) -#define VALIDATE_NON_PENDING_TIMER(t) - -#endif - -/* Thread local variable that stores the deadline of the next timer the thread - * has last-seen. This is an optimization to prevent the thread from checking - * shared_mutables.min_timer (which requires acquiring shared_mutables.mu lock, - * an expensive operation) */ -GPR_TLS_DECL(g_last_seen_min_timer); - -struct shared_mutables { - /* The deadline of the next timer due across all timer shards */ - gpr_atm min_timer; - /* Allow only one run_some_expired_timers at once */ - gpr_spinlock checker_mu; - bool initialized; - /* Protects g_shard_queue (and the shared_mutables struct itself) */ - gpr_mu mu; -} GPR_ALIGN_STRUCT(GPR_CACHELINE_SIZE); - -static struct shared_mutables g_shared_mutables; - -static gpr_clock_type g_clock_type; -static gpr_timespec g_start_time; - -static gpr_atm saturating_add(gpr_atm a, gpr_atm b) { - if (a > GPR_ATM_MAX - b) { - return GPR_ATM_MAX; - } - return a + b; -} - -static grpc_timer_check_result run_some_expired_timers(grpc_exec_ctx *exec_ctx, - gpr_atm now, - gpr_atm *next, - grpc_error *error); - -static gpr_timespec dbl_to_ts(double d) { - gpr_timespec ts; - ts.tv_sec = (int64_t)d; - ts.tv_nsec = (int32_t)(1e9 * (d - (double)ts.tv_sec)); - ts.clock_type = GPR_TIMESPAN; - return ts; -} - -static gpr_atm timespec_to_atm_round_up(gpr_timespec ts) { - ts = gpr_time_sub(ts, g_start_time); - double x = GPR_MS_PER_SEC * (double)ts.tv_sec + - (double)ts.tv_nsec / GPR_NS_PER_MS + - (double)(GPR_NS_PER_SEC - 1) / (double)GPR_NS_PER_SEC; - if (x < 0) return 0; - if (x > GPR_ATM_MAX) return GPR_ATM_MAX; - return (gpr_atm)x; -} - -static gpr_atm timespec_to_atm_round_down(gpr_timespec ts) { - ts = gpr_time_sub(ts, g_start_time); - double x = - GPR_MS_PER_SEC * (double)ts.tv_sec + (double)ts.tv_nsec / GPR_NS_PER_MS; - if (x < 0) return 0; - if (x > GPR_ATM_MAX) return GPR_ATM_MAX; - return (gpr_atm)x; -} - -static gpr_timespec atm_to_timespec(gpr_atm x) { - return gpr_time_add(g_start_time, dbl_to_ts((double)x / 1000.0)); -} - -static gpr_atm compute_min_deadline(timer_shard *shard) { - return grpc_timer_heap_is_empty(&shard->heap) - ? saturating_add(shard->queue_deadline_cap, 1) - : grpc_timer_heap_top(&shard->heap)->deadline; -} - -void grpc_timer_list_init(gpr_timespec now) { - uint32_t i; - - g_shared_mutables.initialized = true; - g_shared_mutables.checker_mu = GPR_SPINLOCK_INITIALIZER; - gpr_mu_init(&g_shared_mutables.mu); - g_clock_type = now.clock_type; - g_start_time = now; - g_shared_mutables.min_timer = timespec_to_atm_round_down(now); - gpr_tls_init(&g_last_seen_min_timer); - gpr_tls_set(&g_last_seen_min_timer, 0); - grpc_register_tracer(&grpc_timer_trace); - grpc_register_tracer(&grpc_timer_check_trace); - - for (i = 0; i < NUM_SHARDS; i++) { - timer_shard *shard = &g_shards[i]; - gpr_mu_init(&shard->mu); - grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1, - 0.5); - shard->queue_deadline_cap = g_shared_mutables.min_timer; - shard->shard_queue_index = i; - grpc_timer_heap_init(&shard->heap); - shard->list.next = shard->list.prev = &shard->list; - shard->min_deadline = compute_min_deadline(shard); - g_shard_queue[i] = shard; - } - - INIT_TIMER_HASH_TABLE(); -} - -void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) { - int i; - run_some_expired_timers( - exec_ctx, GPR_ATM_MAX, NULL, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timer list shutdown")); - for (i = 0; i < NUM_SHARDS; i++) { - timer_shard *shard = &g_shards[i]; - gpr_mu_destroy(&shard->mu); - grpc_timer_heap_destroy(&shard->heap); - } - gpr_mu_destroy(&g_shared_mutables.mu); - gpr_tls_destroy(&g_last_seen_min_timer); - g_shared_mutables.initialized = false; -} - -static double ts_to_dbl(gpr_timespec ts) { - return (double)ts.tv_sec + 1e-9 * ts.tv_nsec; -} - -/* returns true if the first element in the list */ -static void list_join(grpc_timer *head, grpc_timer *timer) { - timer->next = head; - timer->prev = head->prev; - timer->next->prev = timer->prev->next = timer; -} - -static void list_remove(grpc_timer *timer) { - timer->next->prev = timer->prev; - timer->prev->next = timer->next; -} - -static void swap_adjacent_shards_in_queue(uint32_t first_shard_queue_index) { - timer_shard *temp; - temp = g_shard_queue[first_shard_queue_index]; - g_shard_queue[first_shard_queue_index] = - g_shard_queue[first_shard_queue_index + 1]; - g_shard_queue[first_shard_queue_index + 1] = temp; - g_shard_queue[first_shard_queue_index]->shard_queue_index = - first_shard_queue_index; - g_shard_queue[first_shard_queue_index + 1]->shard_queue_index = - first_shard_queue_index + 1; -} - -static void note_deadline_change(timer_shard *shard) { - while (shard->shard_queue_index > 0 && - shard->min_deadline < - g_shard_queue[shard->shard_queue_index - 1]->min_deadline) { - swap_adjacent_shards_in_queue(shard->shard_queue_index - 1); - } - while (shard->shard_queue_index < NUM_SHARDS - 1 && - shard->min_deadline > - g_shard_queue[shard->shard_queue_index + 1]->min_deadline) { - swap_adjacent_shards_in_queue(shard->shard_queue_index); - } -} - -void grpc_timer_init_unset(grpc_timer *timer) { timer->pending = false; } - -void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, - gpr_timespec deadline, grpc_closure *closure, - gpr_timespec now) { - int is_first_timer = 0; - timer_shard *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)]; - GPR_ASSERT(deadline.clock_type == g_clock_type); - GPR_ASSERT(now.clock_type == g_clock_type); - timer->closure = closure; - gpr_atm deadline_atm = timer->deadline = timespec_to_atm_round_up(deadline); - -#ifndef NDEBUG - timer->hash_table_next = NULL; -#endif - - if (GRPC_TRACER_ON(grpc_timer_trace)) { - gpr_log(GPR_DEBUG, "TIMER %p: SET %" PRId64 ".%09d [%" PRIdPTR - "] now %" PRId64 ".%09d [%" PRIdPTR "] call %p[%p]", - timer, deadline.tv_sec, deadline.tv_nsec, deadline_atm, now.tv_sec, - now.tv_nsec, timespec_to_atm_round_down(now), closure, closure->cb); - } - - if (!g_shared_mutables.initialized) { - timer->pending = false; - GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Attempt to create timer before initialization")); - return; - } - - gpr_mu_lock(&shard->mu); - timer->pending = true; - if (gpr_time_cmp(deadline, now) <= 0) { - timer->pending = false; - GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_NONE); - gpr_mu_unlock(&shard->mu); - /* early out */ - return; - } - - grpc_time_averaged_stats_add_sample(&shard->stats, - ts_to_dbl(gpr_time_sub(deadline, now))); - - ADD_TO_HASH_TABLE(timer); - - if (deadline_atm < shard->queue_deadline_cap) { - is_first_timer = grpc_timer_heap_add(&shard->heap, timer); - } else { - timer->heap_index = INVALID_HEAP_INDEX; - list_join(&shard->list, timer); - } - if (GRPC_TRACER_ON(grpc_timer_trace)) { - gpr_log(GPR_DEBUG, " .. add to shard %d with queue_deadline_cap=%" PRIdPTR - " => is_first_timer=%s", - (int)(shard - g_shards), shard->queue_deadline_cap, - is_first_timer ? "true" : "false"); - } - gpr_mu_unlock(&shard->mu); - - /* Deadline may have decreased, we need to adjust the master queue. Note - that there is a potential racy unlocked region here. There could be a - reordering of multiple grpc_timer_init calls, at this point, but the < test - below should ensure that we err on the side of caution. There could - also be a race with grpc_timer_check, which might beat us to the lock. In - that case, it is possible that the timer that we added will have already - run by the time we hold the lock, but that too is a safe error. - Finally, it's possible that the grpc_timer_check that intervened failed to - trigger the new timer because the min_deadline hadn't yet been reduced. - In that case, the timer will simply have to wait for the next - grpc_timer_check. */ - if (is_first_timer) { - gpr_mu_lock(&g_shared_mutables.mu); - if (GRPC_TRACER_ON(grpc_timer_trace)) { - gpr_log(GPR_DEBUG, " .. old shard min_deadline=%" PRIdPTR, - shard->min_deadline); - } - if (deadline_atm < shard->min_deadline) { - gpr_atm old_min_deadline = g_shard_queue[0]->min_deadline; - shard->min_deadline = deadline_atm; - note_deadline_change(shard); - if (shard->shard_queue_index == 0 && deadline_atm < old_min_deadline) { - gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, deadline_atm); - grpc_kick_poller(); - } - } - gpr_mu_unlock(&g_shared_mutables.mu); - } -} - -void grpc_timer_consume_kick(void) { - /* force re-evaluation of last seeen min */ - gpr_tls_set(&g_last_seen_min_timer, 0); -} - -void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { - if (!g_shared_mutables.initialized) { - /* must have already been cancelled, also the shard mutex is invalid */ - return; - } - - timer_shard *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)]; - gpr_mu_lock(&shard->mu); - if (GRPC_TRACER_ON(grpc_timer_trace)) { - gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer, - timer->pending ? "true" : "false"); - } - - if (timer->pending) { - REMOVE_FROM_HASH_TABLE(timer); - - GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED); - timer->pending = false; - if (timer->heap_index == INVALID_HEAP_INDEX) { - list_remove(timer); - } else { - grpc_timer_heap_remove(&shard->heap, timer); - } - } else { - VALIDATE_NON_PENDING_TIMER(timer); - } - gpr_mu_unlock(&shard->mu); -} - -/* Rebalances the timer shard by computing a new 'queue_deadline_cap' and moving - all relevant timers in shard->list (i.e timers with deadlines earlier than - 'queue_deadline_cap') into into shard->heap. - Returns 'true' if shard->heap has atleast ONE element - REQUIRES: shard->mu locked */ -static int refill_heap(timer_shard *shard, gpr_atm now) { - /* Compute the new queue window width and bound by the limits: */ - double computed_deadline_delta = - grpc_time_averaged_stats_update_average(&shard->stats) * - ADD_DEADLINE_SCALE; - double deadline_delta = - GPR_CLAMP(computed_deadline_delta, MIN_QUEUE_WINDOW_DURATION, - MAX_QUEUE_WINDOW_DURATION); - grpc_timer *timer, *next; - - /* Compute the new cap and put all timers under it into the queue: */ - shard->queue_deadline_cap = - saturating_add(GPR_MAX(now, shard->queue_deadline_cap), - (gpr_atm)(deadline_delta * 1000.0)); - - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, " .. shard[%d]->queue_deadline_cap --> %" PRIdPTR, - (int)(shard - g_shards), shard->queue_deadline_cap); - } - for (timer = shard->list.next; timer != &shard->list; timer = next) { - next = timer->next; - - if (timer->deadline < shard->queue_deadline_cap) { - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, " .. add timer with deadline %" PRIdPTR " to heap", - timer->deadline); - } - list_remove(timer); - grpc_timer_heap_add(&shard->heap, timer); - } - } - return !grpc_timer_heap_is_empty(&shard->heap); -} - -/* This pops the next non-cancelled timer with deadline <= now from the - queue, or returns NULL if there isn't one. - REQUIRES: shard->mu locked */ -static grpc_timer *pop_one(timer_shard *shard, gpr_atm now) { - grpc_timer *timer; - for (;;) { - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, " .. shard[%d]: heap_empty=%s", - (int)(shard - g_shards), - grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false"); - } - if (grpc_timer_heap_is_empty(&shard->heap)) { - if (now < shard->queue_deadline_cap) return NULL; - if (!refill_heap(shard, now)) return NULL; - } - timer = grpc_timer_heap_top(&shard->heap); - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, - " .. check top timer deadline=%" PRIdPTR " now=%" PRIdPTR, - timer->deadline, now); - } - if (timer->deadline > now) return NULL; - if (GRPC_TRACER_ON(grpc_timer_trace)) { - gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late", timer, - now - timer->deadline); - } - timer->pending = false; - grpc_timer_heap_pop(&shard->heap); - return timer; - } -} - -/* REQUIRES: shard->mu unlocked */ -static size_t pop_timers(grpc_exec_ctx *exec_ctx, timer_shard *shard, - gpr_atm now, gpr_atm *new_min_deadline, - grpc_error *error) { - size_t n = 0; - grpc_timer *timer; - gpr_mu_lock(&shard->mu); - while ((timer = pop_one(shard, now))) { - REMOVE_FROM_HASH_TABLE(timer); - GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_REF(error)); - n++; - } - *new_min_deadline = compute_min_deadline(shard); - gpr_mu_unlock(&shard->mu); - return n; -} - -static grpc_timer_check_result run_some_expired_timers(grpc_exec_ctx *exec_ctx, - gpr_atm now, - gpr_atm *next, - grpc_error *error) { - grpc_timer_check_result result = GRPC_TIMERS_NOT_CHECKED; - - gpr_atm min_timer = gpr_atm_no_barrier_load(&g_shared_mutables.min_timer); - gpr_tls_set(&g_last_seen_min_timer, min_timer); - if (now < min_timer) { - if (next != NULL) *next = GPR_MIN(*next, min_timer); - return GRPC_TIMERS_CHECKED_AND_EMPTY; - } - - if (gpr_spinlock_trylock(&g_shared_mutables.checker_mu)) { - gpr_mu_lock(&g_shared_mutables.mu); - result = GRPC_TIMERS_CHECKED_AND_EMPTY; - - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, " .. shard[%d]->min_deadline = %" PRIdPTR, - (int)(g_shard_queue[0] - g_shards), - g_shard_queue[0]->min_deadline); - } - - while (g_shard_queue[0]->min_deadline < now || - (now != GPR_ATM_MAX && g_shard_queue[0]->min_deadline == now)) { - gpr_atm new_min_deadline; - - /* For efficiency, we pop as many available timers as we can from the - shard. This may violate perfect timer deadline ordering, but that - shouldn't be a big deal because we don't make ordering guarantees. */ - if (pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, - error) > 0) { - result = GRPC_TIMERS_FIRED; - } - - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, - " .. result --> %d" - ", shard[%d]->min_deadline %" PRIdPTR " --> %" PRIdPTR - ", now=%" PRIdPTR, - result, (int)(g_shard_queue[0] - g_shards), - g_shard_queue[0]->min_deadline, new_min_deadline, now); - } - - /* An grpc_timer_init() on the shard could intervene here, adding a new - timer that is earlier than new_min_deadline. However, - grpc_timer_init() will block on the master_lock before it can call - set_min_deadline, so this one will complete first and then the Addtimer - will reduce the min_deadline (perhaps unnecessarily). */ - g_shard_queue[0]->min_deadline = new_min_deadline; - note_deadline_change(g_shard_queue[0]); - } - - if (next) { - *next = GPR_MIN(*next, g_shard_queue[0]->min_deadline); - } - - gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, - g_shard_queue[0]->min_deadline); - gpr_mu_unlock(&g_shared_mutables.mu); - gpr_spinlock_unlock(&g_shared_mutables.checker_mu); - } - - GRPC_ERROR_UNREF(error); - - return result; -} - -grpc_timer_check_result grpc_timer_check(grpc_exec_ctx *exec_ctx, - gpr_timespec now, gpr_timespec *next) { - // prelude - GPR_ASSERT(now.clock_type == g_clock_type); - gpr_atm now_atm = timespec_to_atm_round_down(now); - - /* fetch from a thread-local first: this avoids contention on a globally - mutable cacheline in the common case */ - gpr_atm min_timer = gpr_tls_get(&g_last_seen_min_timer); - if (now_atm < min_timer) { - if (next != NULL) { - *next = - atm_to_timespec(GPR_MIN(timespec_to_atm_round_up(*next), min_timer)); - } - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, - "TIMER CHECK SKIP: now_atm=%" PRIdPTR " min_timer=%" PRIdPTR, - now_atm, min_timer); - } - return GRPC_TIMERS_CHECKED_AND_EMPTY; - } - - grpc_error *shutdown_error = - gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0 - ? GRPC_ERROR_NONE - : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system"); - - // tracing - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - char *next_str; - if (next == NULL) { - next_str = gpr_strdup("NULL"); - } else { - gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec, - next->tv_nsec, timespec_to_atm_round_down(*next)); - } - gpr_log(GPR_DEBUG, "TIMER CHECK BEGIN: now=%" PRId64 ".%09d [%" PRIdPTR - "] next=%s tls_min=%" PRIdPTR " glob_min=%" PRIdPTR, - now.tv_sec, now.tv_nsec, now_atm, next_str, - gpr_tls_get(&g_last_seen_min_timer), - gpr_atm_no_barrier_load(&g_shared_mutables.min_timer)); - gpr_free(next_str); - } - // actual code - grpc_timer_check_result r; - gpr_atm next_atm; - if (next == NULL) { - r = run_some_expired_timers(exec_ctx, now_atm, NULL, shutdown_error); - } else { - next_atm = timespec_to_atm_round_down(*next); - r = run_some_expired_timers(exec_ctx, now_atm, &next_atm, shutdown_error); - *next = atm_to_timespec(next_atm); - } - // tracing - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - char *next_str; - if (next == NULL) { - next_str = gpr_strdup("NULL"); - } else { - gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec, - next->tv_nsec, next_atm); - } - gpr_log(GPR_DEBUG, "TIMER CHECK END: r=%d; next=%s", r, next_str); - gpr_free(next_str); - } - return r; -} - -#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc new file mode 100644 index 0000000000..2472cf26be --- /dev/null +++ b/src/core/lib/iomgr/timer_generic.cc @@ -0,0 +1,705 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_TIMER_USE_GENERIC + +#include "src/core/lib/iomgr/timer.h" + +#include +#include +#include +#include +#include +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/time_averaged_stats.h" +#include "src/core/lib/iomgr/timer_heap.h" +#include "src/core/lib/support/spinlock.h" + +#define INVALID_HEAP_INDEX 0xffffffffu + +#define LOG2_NUM_SHARDS 5 +#define NUM_SHARDS (1 << LOG2_NUM_SHARDS) +#define ADD_DEADLINE_SCALE 0.33 +#define MIN_QUEUE_WINDOW_DURATION 0.01 +#define MAX_QUEUE_WINDOW_DURATION 1 + +grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer"); +grpc_tracer_flag grpc_timer_check_trace = + GRPC_TRACER_INITIALIZER(false, "timer_check"); + +/* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with + * deadlines earlier than 'queue_deadline" cap are maintained in the heap and + * others are maintained in the list (unordered). This helps to keep the number + * of elements in the heap low. + * + * The 'queue_deadline_cap' gets recomputed periodically based on the timer + * stats maintained in 'stats' and the relevant timers are then moved from the + * 'list' to 'heap' + */ +typedef struct { + gpr_mu mu; + grpc_time_averaged_stats stats; + /* All and only timers with deadlines <= this will be in the heap. */ + gpr_atm queue_deadline_cap; + /* The deadline of the next timer due in this shard */ + gpr_atm min_deadline; + /* Index of this timer_shard in the g_shard_queue */ + uint32_t shard_queue_index; + /* This holds all timers with deadlines < queue_deadline_cap. Timers in this + list have the top bit of their deadline set to 0. */ + grpc_timer_heap heap; + /* This holds timers whose deadline is >= queue_deadline_cap. */ + grpc_timer list; +} timer_shard; + +/* Array of timer shards. Whenever a timer (grpc_timer *) is added, its address + * is hashed to select the timer shard to add the timer to */ +static timer_shard g_shards[NUM_SHARDS]; + +/* Maintains a sorted list of timer shards (sorted by their min_deadline, i.e + * the deadline of the next timer in each shard). + * Access to this is protected by g_shared_mutables.mu */ +static timer_shard *g_shard_queue[NUM_SHARDS]; + +#ifndef NDEBUG + +/* == Hash table for duplicate timer detection == */ + +#define NUM_HASH_BUCKETS 1009 /* Prime number close to 1000 */ + +static gpr_mu g_hash_mu[NUM_HASH_BUCKETS]; /* One mutex per bucket */ +static grpc_timer *g_timer_ht[NUM_HASH_BUCKETS] = {NULL}; + +static void init_timer_ht() { + for (int i = 0; i < NUM_HASH_BUCKETS; i++) { + gpr_mu_init(&g_hash_mu[i]); + } +} + +static bool is_in_ht(grpc_timer *t) { + size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); + + gpr_mu_lock(&g_hash_mu[i]); + grpc_timer *p = g_timer_ht[i]; + while (p != NULL && p != t) { + p = p->hash_table_next; + } + gpr_mu_unlock(&g_hash_mu[i]); + + return (p == t); +} + +static void add_to_ht(grpc_timer *t) { + GPR_ASSERT(!t->hash_table_next); + size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); + + gpr_mu_lock(&g_hash_mu[i]); + grpc_timer *p = g_timer_ht[i]; + while (p != NULL && p != t) { + p = p->hash_table_next; + } + + if (p == t) { + grpc_closure *c = t->closure; + gpr_log(GPR_ERROR, + "** Duplicate timer (%p) being added. Closure: (%p), created at: " + "(%s:%d), scheduled at: (%s:%d) **", + t, c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated); + abort(); + } + + /* Timer not present in the bucket. Insert at head of the list */ + t->hash_table_next = g_timer_ht[i]; + g_timer_ht[i] = t; + gpr_mu_unlock(&g_hash_mu[i]); +} + +static void remove_from_ht(grpc_timer *t) { + size_t i = GPR_HASH_POINTER(t, NUM_HASH_BUCKETS); + bool removed = false; + + gpr_mu_lock(&g_hash_mu[i]); + if (g_timer_ht[i] == t) { + g_timer_ht[i] = g_timer_ht[i]->hash_table_next; + removed = true; + } else if (g_timer_ht[i] != NULL) { + grpc_timer *p = g_timer_ht[i]; + while (p->hash_table_next != NULL && p->hash_table_next != t) { + p = p->hash_table_next; + } + + if (p->hash_table_next == t) { + p->hash_table_next = t->hash_table_next; + removed = true; + } + } + gpr_mu_unlock(&g_hash_mu[i]); + + if (!removed) { + grpc_closure *c = t->closure; + gpr_log(GPR_ERROR, + "** Removing timer (%p) that is not added to hash table. Closure " + "(%p), created at: (%s:%d), scheduled at: (%s:%d) **", + t, c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated); + abort(); + } + + t->hash_table_next = NULL; +} + +/* If a timer is added to a timer shard (either heap or a list), it cannot + * be pending. A timer is added to hash table only-if it is added to the + * timer shard. + * Therefore, if timer->pending is false, it cannot be in hash table */ +static void validate_non_pending_timer(grpc_timer *t) { + if (!t->pending && is_in_ht(t)) { + grpc_closure *c = t->closure; + gpr_log(GPR_ERROR, + "** gpr_timer_cancel() called on a non-pending timer (%p) which " + "is in the hash table. Closure: (%p), created at: (%s:%d), " + "scheduled at: (%s:%d) **", + t, c, c->file_created, c->line_created, c->file_initiated, + c->line_initiated); + abort(); + } +} + +#define INIT_TIMER_HASH_TABLE() init_timer_ht() +#define ADD_TO_HASH_TABLE(t) add_to_ht((t)) +#define REMOVE_FROM_HASH_TABLE(t) remove_from_ht((t)) +#define VALIDATE_NON_PENDING_TIMER(t) validate_non_pending_timer((t)) + +#else + +#define INIT_TIMER_HASH_TABLE() +#define ADD_TO_HASH_TABLE(t) +#define REMOVE_FROM_HASH_TABLE(t) +#define VALIDATE_NON_PENDING_TIMER(t) + +#endif + +/* Thread local variable that stores the deadline of the next timer the thread + * has last-seen. This is an optimization to prevent the thread from checking + * shared_mutables.min_timer (which requires acquiring shared_mutables.mu lock, + * an expensive operation) */ +GPR_TLS_DECL(g_last_seen_min_timer); + +struct shared_mutables { + /* The deadline of the next timer due across all timer shards */ + gpr_atm min_timer; + /* Allow only one run_some_expired_timers at once */ + gpr_spinlock checker_mu; + bool initialized; + /* Protects g_shard_queue (and the shared_mutables struct itself) */ + gpr_mu mu; +} GPR_ALIGN_STRUCT(GPR_CACHELINE_SIZE); + +static struct shared_mutables g_shared_mutables; + +static gpr_clock_type g_clock_type; +static gpr_timespec g_start_time; + +static gpr_atm saturating_add(gpr_atm a, gpr_atm b) { + if (a > GPR_ATM_MAX - b) { + return GPR_ATM_MAX; + } + return a + b; +} + +static grpc_timer_check_result run_some_expired_timers(grpc_exec_ctx *exec_ctx, + gpr_atm now, + gpr_atm *next, + grpc_error *error); + +static gpr_timespec dbl_to_ts(double d) { + gpr_timespec ts; + ts.tv_sec = (int64_t)d; + ts.tv_nsec = (int32_t)(1e9 * (d - (double)ts.tv_sec)); + ts.clock_type = GPR_TIMESPAN; + return ts; +} + +static gpr_atm timespec_to_atm_round_up(gpr_timespec ts) { + ts = gpr_time_sub(ts, g_start_time); + double x = GPR_MS_PER_SEC * (double)ts.tv_sec + + (double)ts.tv_nsec / GPR_NS_PER_MS + + (double)(GPR_NS_PER_SEC - 1) / (double)GPR_NS_PER_SEC; + if (x < 0) return 0; + if (x > GPR_ATM_MAX) return GPR_ATM_MAX; + return (gpr_atm)x; +} + +static gpr_atm timespec_to_atm_round_down(gpr_timespec ts) { + ts = gpr_time_sub(ts, g_start_time); + double x = + GPR_MS_PER_SEC * (double)ts.tv_sec + (double)ts.tv_nsec / GPR_NS_PER_MS; + if (x < 0) return 0; + if (x > GPR_ATM_MAX) return GPR_ATM_MAX; + return (gpr_atm)x; +} + +static gpr_timespec atm_to_timespec(gpr_atm x) { + return gpr_time_add(g_start_time, dbl_to_ts((double)x / 1000.0)); +} + +static gpr_atm compute_min_deadline(timer_shard *shard) { + return grpc_timer_heap_is_empty(&shard->heap) + ? saturating_add(shard->queue_deadline_cap, 1) + : grpc_timer_heap_top(&shard->heap)->deadline; +} + +void grpc_timer_list_init(gpr_timespec now) { + uint32_t i; + + g_shared_mutables.initialized = true; + g_shared_mutables.checker_mu = GPR_SPINLOCK_INITIALIZER; + gpr_mu_init(&g_shared_mutables.mu); + g_clock_type = now.clock_type; + g_start_time = now; + g_shared_mutables.min_timer = timespec_to_atm_round_down(now); + gpr_tls_init(&g_last_seen_min_timer); + gpr_tls_set(&g_last_seen_min_timer, 0); + grpc_register_tracer(&grpc_timer_trace); + grpc_register_tracer(&grpc_timer_check_trace); + + for (i = 0; i < NUM_SHARDS; i++) { + timer_shard *shard = &g_shards[i]; + gpr_mu_init(&shard->mu); + grpc_time_averaged_stats_init(&shard->stats, 1.0 / ADD_DEADLINE_SCALE, 0.1, + 0.5); + shard->queue_deadline_cap = g_shared_mutables.min_timer; + shard->shard_queue_index = i; + grpc_timer_heap_init(&shard->heap); + shard->list.next = shard->list.prev = &shard->list; + shard->min_deadline = compute_min_deadline(shard); + g_shard_queue[i] = shard; + } + + INIT_TIMER_HASH_TABLE(); +} + +void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) { + int i; + run_some_expired_timers( + exec_ctx, GPR_ATM_MAX, NULL, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Timer list shutdown")); + for (i = 0; i < NUM_SHARDS; i++) { + timer_shard *shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + grpc_timer_heap_destroy(&shard->heap); + } + gpr_mu_destroy(&g_shared_mutables.mu); + gpr_tls_destroy(&g_last_seen_min_timer); + g_shared_mutables.initialized = false; +} + +static double ts_to_dbl(gpr_timespec ts) { + return (double)ts.tv_sec + 1e-9 * ts.tv_nsec; +} + +/* returns true if the first element in the list */ +static void list_join(grpc_timer *head, grpc_timer *timer) { + timer->next = head; + timer->prev = head->prev; + timer->next->prev = timer->prev->next = timer; +} + +static void list_remove(grpc_timer *timer) { + timer->next->prev = timer->prev; + timer->prev->next = timer->next; +} + +static void swap_adjacent_shards_in_queue(uint32_t first_shard_queue_index) { + timer_shard *temp; + temp = g_shard_queue[first_shard_queue_index]; + g_shard_queue[first_shard_queue_index] = + g_shard_queue[first_shard_queue_index + 1]; + g_shard_queue[first_shard_queue_index + 1] = temp; + g_shard_queue[first_shard_queue_index]->shard_queue_index = + first_shard_queue_index; + g_shard_queue[first_shard_queue_index + 1]->shard_queue_index = + first_shard_queue_index + 1; +} + +static void note_deadline_change(timer_shard *shard) { + while (shard->shard_queue_index > 0 && + shard->min_deadline < + g_shard_queue[shard->shard_queue_index - 1]->min_deadline) { + swap_adjacent_shards_in_queue(shard->shard_queue_index - 1); + } + while (shard->shard_queue_index < NUM_SHARDS - 1 && + shard->min_deadline > + g_shard_queue[shard->shard_queue_index + 1]->min_deadline) { + swap_adjacent_shards_in_queue(shard->shard_queue_index); + } +} + +void grpc_timer_init_unset(grpc_timer *timer) { timer->pending = false; } + +void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, + gpr_timespec deadline, grpc_closure *closure, + gpr_timespec now) { + int is_first_timer = 0; + timer_shard *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)]; + GPR_ASSERT(deadline.clock_type == g_clock_type); + GPR_ASSERT(now.clock_type == g_clock_type); + timer->closure = closure; + gpr_atm deadline_atm = timer->deadline = timespec_to_atm_round_up(deadline); + +#ifndef NDEBUG + timer->hash_table_next = NULL; +#endif + + if (GRPC_TRACER_ON(grpc_timer_trace)) { + gpr_log(GPR_DEBUG, "TIMER %p: SET %" PRId64 ".%09d [%" PRIdPTR + "] now %" PRId64 ".%09d [%" PRIdPTR "] call %p[%p]", + timer, deadline.tv_sec, deadline.tv_nsec, deadline_atm, now.tv_sec, + now.tv_nsec, timespec_to_atm_round_down(now), closure, closure->cb); + } + + if (!g_shared_mutables.initialized) { + timer->pending = false; + GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Attempt to create timer before initialization")); + return; + } + + gpr_mu_lock(&shard->mu); + timer->pending = true; + if (gpr_time_cmp(deadline, now) <= 0) { + timer->pending = false; + GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_NONE); + gpr_mu_unlock(&shard->mu); + /* early out */ + return; + } + + grpc_time_averaged_stats_add_sample(&shard->stats, + ts_to_dbl(gpr_time_sub(deadline, now))); + + ADD_TO_HASH_TABLE(timer); + + if (deadline_atm < shard->queue_deadline_cap) { + is_first_timer = grpc_timer_heap_add(&shard->heap, timer); + } else { + timer->heap_index = INVALID_HEAP_INDEX; + list_join(&shard->list, timer); + } + if (GRPC_TRACER_ON(grpc_timer_trace)) { + gpr_log(GPR_DEBUG, " .. add to shard %d with queue_deadline_cap=%" PRIdPTR + " => is_first_timer=%s", + (int)(shard - g_shards), shard->queue_deadline_cap, + is_first_timer ? "true" : "false"); + } + gpr_mu_unlock(&shard->mu); + + /* Deadline may have decreased, we need to adjust the master queue. Note + that there is a potential racy unlocked region here. There could be a + reordering of multiple grpc_timer_init calls, at this point, but the < test + below should ensure that we err on the side of caution. There could + also be a race with grpc_timer_check, which might beat us to the lock. In + that case, it is possible that the timer that we added will have already + run by the time we hold the lock, but that too is a safe error. + Finally, it's possible that the grpc_timer_check that intervened failed to + trigger the new timer because the min_deadline hadn't yet been reduced. + In that case, the timer will simply have to wait for the next + grpc_timer_check. */ + if (is_first_timer) { + gpr_mu_lock(&g_shared_mutables.mu); + if (GRPC_TRACER_ON(grpc_timer_trace)) { + gpr_log(GPR_DEBUG, " .. old shard min_deadline=%" PRIdPTR, + shard->min_deadline); + } + if (deadline_atm < shard->min_deadline) { + gpr_atm old_min_deadline = g_shard_queue[0]->min_deadline; + shard->min_deadline = deadline_atm; + note_deadline_change(shard); + if (shard->shard_queue_index == 0 && deadline_atm < old_min_deadline) { + gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, deadline_atm); + grpc_kick_poller(); + } + } + gpr_mu_unlock(&g_shared_mutables.mu); + } +} + +void grpc_timer_consume_kick(void) { + /* force re-evaluation of last seeen min */ + gpr_tls_set(&g_last_seen_min_timer, 0); +} + +void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { + if (!g_shared_mutables.initialized) { + /* must have already been cancelled, also the shard mutex is invalid */ + return; + } + + timer_shard *shard = &g_shards[GPR_HASH_POINTER(timer, NUM_SHARDS)]; + gpr_mu_lock(&shard->mu); + if (GRPC_TRACER_ON(grpc_timer_trace)) { + gpr_log(GPR_DEBUG, "TIMER %p: CANCEL pending=%s", timer, + timer->pending ? "true" : "false"); + } + + if (timer->pending) { + REMOVE_FROM_HASH_TABLE(timer); + + GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED); + timer->pending = false; + if (timer->heap_index == INVALID_HEAP_INDEX) { + list_remove(timer); + } else { + grpc_timer_heap_remove(&shard->heap, timer); + } + } else { + VALIDATE_NON_PENDING_TIMER(timer); + } + gpr_mu_unlock(&shard->mu); +} + +/* Rebalances the timer shard by computing a new 'queue_deadline_cap' and moving + all relevant timers in shard->list (i.e timers with deadlines earlier than + 'queue_deadline_cap') into into shard->heap. + Returns 'true' if shard->heap has atleast ONE element + REQUIRES: shard->mu locked */ +static int refill_heap(timer_shard *shard, gpr_atm now) { + /* Compute the new queue window width and bound by the limits: */ + double computed_deadline_delta = + grpc_time_averaged_stats_update_average(&shard->stats) * + ADD_DEADLINE_SCALE; + double deadline_delta = + GPR_CLAMP(computed_deadline_delta, MIN_QUEUE_WINDOW_DURATION, + MAX_QUEUE_WINDOW_DURATION); + grpc_timer *timer, *next; + + /* Compute the new cap and put all timers under it into the queue: */ + shard->queue_deadline_cap = + saturating_add(GPR_MAX(now, shard->queue_deadline_cap), + (gpr_atm)(deadline_delta * 1000.0)); + + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, " .. shard[%d]->queue_deadline_cap --> %" PRIdPTR, + (int)(shard - g_shards), shard->queue_deadline_cap); + } + for (timer = shard->list.next; timer != &shard->list; timer = next) { + next = timer->next; + + if (timer->deadline < shard->queue_deadline_cap) { + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, " .. add timer with deadline %" PRIdPTR " to heap", + timer->deadline); + } + list_remove(timer); + grpc_timer_heap_add(&shard->heap, timer); + } + } + return !grpc_timer_heap_is_empty(&shard->heap); +} + +/* This pops the next non-cancelled timer with deadline <= now from the + queue, or returns NULL if there isn't one. + REQUIRES: shard->mu locked */ +static grpc_timer *pop_one(timer_shard *shard, gpr_atm now) { + grpc_timer *timer; + for (;;) { + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, " .. shard[%d]: heap_empty=%s", + (int)(shard - g_shards), + grpc_timer_heap_is_empty(&shard->heap) ? "true" : "false"); + } + if (grpc_timer_heap_is_empty(&shard->heap)) { + if (now < shard->queue_deadline_cap) return NULL; + if (!refill_heap(shard, now)) return NULL; + } + timer = grpc_timer_heap_top(&shard->heap); + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, + " .. check top timer deadline=%" PRIdPTR " now=%" PRIdPTR, + timer->deadline, now); + } + if (timer->deadline > now) return NULL; + if (GRPC_TRACER_ON(grpc_timer_trace)) { + gpr_log(GPR_DEBUG, "TIMER %p: FIRE %" PRIdPTR "ms late", timer, + now - timer->deadline); + } + timer->pending = false; + grpc_timer_heap_pop(&shard->heap); + return timer; + } +} + +/* REQUIRES: shard->mu unlocked */ +static size_t pop_timers(grpc_exec_ctx *exec_ctx, timer_shard *shard, + gpr_atm now, gpr_atm *new_min_deadline, + grpc_error *error) { + size_t n = 0; + grpc_timer *timer; + gpr_mu_lock(&shard->mu); + while ((timer = pop_one(shard, now))) { + REMOVE_FROM_HASH_TABLE(timer); + GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_REF(error)); + n++; + } + *new_min_deadline = compute_min_deadline(shard); + gpr_mu_unlock(&shard->mu); + return n; +} + +static grpc_timer_check_result run_some_expired_timers(grpc_exec_ctx *exec_ctx, + gpr_atm now, + gpr_atm *next, + grpc_error *error) { + grpc_timer_check_result result = GRPC_TIMERS_NOT_CHECKED; + + gpr_atm min_timer = gpr_atm_no_barrier_load(&g_shared_mutables.min_timer); + gpr_tls_set(&g_last_seen_min_timer, min_timer); + if (now < min_timer) { + if (next != NULL) *next = GPR_MIN(*next, min_timer); + return GRPC_TIMERS_CHECKED_AND_EMPTY; + } + + if (gpr_spinlock_trylock(&g_shared_mutables.checker_mu)) { + gpr_mu_lock(&g_shared_mutables.mu); + result = GRPC_TIMERS_CHECKED_AND_EMPTY; + + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, " .. shard[%d]->min_deadline = %" PRIdPTR, + (int)(g_shard_queue[0] - g_shards), + g_shard_queue[0]->min_deadline); + } + + while (g_shard_queue[0]->min_deadline < now || + (now != GPR_ATM_MAX && g_shard_queue[0]->min_deadline == now)) { + gpr_atm new_min_deadline; + + /* For efficiency, we pop as many available timers as we can from the + shard. This may violate perfect timer deadline ordering, but that + shouldn't be a big deal because we don't make ordering guarantees. */ + if (pop_timers(exec_ctx, g_shard_queue[0], now, &new_min_deadline, + error) > 0) { + result = GRPC_TIMERS_FIRED; + } + + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, + " .. result --> %d" + ", shard[%d]->min_deadline %" PRIdPTR " --> %" PRIdPTR + ", now=%" PRIdPTR, + result, (int)(g_shard_queue[0] - g_shards), + g_shard_queue[0]->min_deadline, new_min_deadline, now); + } + + /* An grpc_timer_init() on the shard could intervene here, adding a new + timer that is earlier than new_min_deadline. However, + grpc_timer_init() will block on the master_lock before it can call + set_min_deadline, so this one will complete first and then the Addtimer + will reduce the min_deadline (perhaps unnecessarily). */ + g_shard_queue[0]->min_deadline = new_min_deadline; + note_deadline_change(g_shard_queue[0]); + } + + if (next) { + *next = GPR_MIN(*next, g_shard_queue[0]->min_deadline); + } + + gpr_atm_no_barrier_store(&g_shared_mutables.min_timer, + g_shard_queue[0]->min_deadline); + gpr_mu_unlock(&g_shared_mutables.mu); + gpr_spinlock_unlock(&g_shared_mutables.checker_mu); + } + + GRPC_ERROR_UNREF(error); + + return result; +} + +grpc_timer_check_result grpc_timer_check(grpc_exec_ctx *exec_ctx, + gpr_timespec now, gpr_timespec *next) { + // prelude + GPR_ASSERT(now.clock_type == g_clock_type); + gpr_atm now_atm = timespec_to_atm_round_down(now); + + /* fetch from a thread-local first: this avoids contention on a globally + mutable cacheline in the common case */ + gpr_atm min_timer = gpr_tls_get(&g_last_seen_min_timer); + if (now_atm < min_timer) { + if (next != NULL) { + *next = + atm_to_timespec(GPR_MIN(timespec_to_atm_round_up(*next), min_timer)); + } + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, + "TIMER CHECK SKIP: now_atm=%" PRIdPTR " min_timer=%" PRIdPTR, + now_atm, min_timer); + } + return GRPC_TIMERS_CHECKED_AND_EMPTY; + } + + grpc_error *shutdown_error = + gpr_time_cmp(now, gpr_inf_future(now.clock_type)) != 0 + ? GRPC_ERROR_NONE + : GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutting down timer system"); + + // tracing + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + char *next_str; + if (next == NULL) { + next_str = gpr_strdup("NULL"); + } else { + gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec, + next->tv_nsec, timespec_to_atm_round_down(*next)); + } + gpr_log(GPR_DEBUG, "TIMER CHECK BEGIN: now=%" PRId64 ".%09d [%" PRIdPTR + "] next=%s tls_min=%" PRIdPTR " glob_min=%" PRIdPTR, + now.tv_sec, now.tv_nsec, now_atm, next_str, + gpr_tls_get(&g_last_seen_min_timer), + gpr_atm_no_barrier_load(&g_shared_mutables.min_timer)); + gpr_free(next_str); + } + // actual code + grpc_timer_check_result r; + gpr_atm next_atm; + if (next == NULL) { + r = run_some_expired_timers(exec_ctx, now_atm, NULL, shutdown_error); + } else { + next_atm = timespec_to_atm_round_down(*next); + r = run_some_expired_timers(exec_ctx, now_atm, &next_atm, shutdown_error); + *next = atm_to_timespec(next_atm); + } + // tracing + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + char *next_str; + if (next == NULL) { + next_str = gpr_strdup("NULL"); + } else { + gpr_asprintf(&next_str, "%" PRId64 ".%09d [%" PRIdPTR "]", next->tv_sec, + next->tv_nsec, next_atm); + } + gpr_log(GPR_DEBUG, "TIMER CHECK END: r=%d; next=%s", r, next_str); + gpr_free(next_str); + } + return r; +} + +#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_heap.c b/src/core/lib/iomgr/timer_heap.c deleted file mode 100644 index 2648d5da5d..0000000000 --- a/src/core/lib/iomgr/timer_heap.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_TIMER_USE_GENERIC - -#include "src/core/lib/iomgr/timer_heap.h" - -#include - -#include -#include - -/* Adjusts a heap so as to move a hole at position i closer to the root, - until a suitable position is found for element t. Then, copies t into that - position. This functor is called each time immediately after modifying a - value in the underlying container, with the offset of the modified element as - its argument. */ -static void adjust_upwards(grpc_timer **first, uint32_t i, grpc_timer *t) { - while (i > 0) { - uint32_t parent = (uint32_t)(((int)i - 1) / 2); - if (first[parent]->deadline <= t->deadline) break; - first[i] = first[parent]; - first[i]->heap_index = i; - i = parent; - } - first[i] = t; - t->heap_index = i; -} - -/* Adjusts a heap so as to move a hole at position i farther away from the root, - until a suitable position is found for element t. Then, copies t into that - position. */ -static void adjust_downwards(grpc_timer **first, uint32_t i, uint32_t length, - grpc_timer *t) { - for (;;) { - uint32_t left_child = 1u + 2u * i; - if (left_child >= length) break; - uint32_t right_child = left_child + 1; - uint32_t next_i = - right_child < length && - first[left_child]->deadline > first[right_child]->deadline - ? right_child - : left_child; - if (t->deadline <= first[next_i]->deadline) break; - first[i] = first[next_i]; - first[i]->heap_index = i; - i = next_i; - } - first[i] = t; - t->heap_index = i; -} - -#define SHRINK_MIN_ELEMS 8 -#define SHRINK_FULLNESS_FACTOR 2 - -static void maybe_shrink(grpc_timer_heap *heap) { - if (heap->timer_count >= 8 && - heap->timer_count <= heap->timer_capacity / SHRINK_FULLNESS_FACTOR / 2) { - heap->timer_capacity = heap->timer_count * SHRINK_FULLNESS_FACTOR; - heap->timers = (grpc_timer **)gpr_realloc( - heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); - } -} - -static void note_changed_priority(grpc_timer_heap *heap, grpc_timer *timer) { - uint32_t i = timer->heap_index; - uint32_t parent = (uint32_t)(((int)i - 1) / 2); - if (heap->timers[parent]->deadline > timer->deadline) { - adjust_upwards(heap->timers, i, timer); - } else { - adjust_downwards(heap->timers, i, heap->timer_count, timer); - } -} - -void grpc_timer_heap_init(grpc_timer_heap *heap) { - memset(heap, 0, sizeof(*heap)); -} - -void grpc_timer_heap_destroy(grpc_timer_heap *heap) { gpr_free(heap->timers); } - -int grpc_timer_heap_add(grpc_timer_heap *heap, grpc_timer *timer) { - if (heap->timer_count == heap->timer_capacity) { - heap->timer_capacity = - GPR_MAX(heap->timer_capacity + 1, heap->timer_capacity * 3 / 2); - heap->timers = (grpc_timer **)gpr_realloc( - heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); - } - timer->heap_index = heap->timer_count; - adjust_upwards(heap->timers, heap->timer_count, timer); - heap->timer_count++; - return timer->heap_index == 0; -} - -void grpc_timer_heap_remove(grpc_timer_heap *heap, grpc_timer *timer) { - uint32_t i = timer->heap_index; - if (i == heap->timer_count - 1) { - heap->timer_count--; - maybe_shrink(heap); - return; - } - heap->timers[i] = heap->timers[heap->timer_count - 1]; - heap->timers[i]->heap_index = i; - heap->timer_count--; - maybe_shrink(heap); - note_changed_priority(heap, heap->timers[i]); -} - -int grpc_timer_heap_is_empty(grpc_timer_heap *heap) { - return heap->timer_count == 0; -} - -grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap) { - return heap->timers[0]; -} - -void grpc_timer_heap_pop(grpc_timer_heap *heap) { - grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); -} - -#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_heap.cc b/src/core/lib/iomgr/timer_heap.cc new file mode 100644 index 0000000000..2648d5da5d --- /dev/null +++ b/src/core/lib/iomgr/timer_heap.cc @@ -0,0 +1,137 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_TIMER_USE_GENERIC + +#include "src/core/lib/iomgr/timer_heap.h" + +#include + +#include +#include + +/* Adjusts a heap so as to move a hole at position i closer to the root, + until a suitable position is found for element t. Then, copies t into that + position. This functor is called each time immediately after modifying a + value in the underlying container, with the offset of the modified element as + its argument. */ +static void adjust_upwards(grpc_timer **first, uint32_t i, grpc_timer *t) { + while (i > 0) { + uint32_t parent = (uint32_t)(((int)i - 1) / 2); + if (first[parent]->deadline <= t->deadline) break; + first[i] = first[parent]; + first[i]->heap_index = i; + i = parent; + } + first[i] = t; + t->heap_index = i; +} + +/* Adjusts a heap so as to move a hole at position i farther away from the root, + until a suitable position is found for element t. Then, copies t into that + position. */ +static void adjust_downwards(grpc_timer **first, uint32_t i, uint32_t length, + grpc_timer *t) { + for (;;) { + uint32_t left_child = 1u + 2u * i; + if (left_child >= length) break; + uint32_t right_child = left_child + 1; + uint32_t next_i = + right_child < length && + first[left_child]->deadline > first[right_child]->deadline + ? right_child + : left_child; + if (t->deadline <= first[next_i]->deadline) break; + first[i] = first[next_i]; + first[i]->heap_index = i; + i = next_i; + } + first[i] = t; + t->heap_index = i; +} + +#define SHRINK_MIN_ELEMS 8 +#define SHRINK_FULLNESS_FACTOR 2 + +static void maybe_shrink(grpc_timer_heap *heap) { + if (heap->timer_count >= 8 && + heap->timer_count <= heap->timer_capacity / SHRINK_FULLNESS_FACTOR / 2) { + heap->timer_capacity = heap->timer_count * SHRINK_FULLNESS_FACTOR; + heap->timers = (grpc_timer **)gpr_realloc( + heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); + } +} + +static void note_changed_priority(grpc_timer_heap *heap, grpc_timer *timer) { + uint32_t i = timer->heap_index; + uint32_t parent = (uint32_t)(((int)i - 1) / 2); + if (heap->timers[parent]->deadline > timer->deadline) { + adjust_upwards(heap->timers, i, timer); + } else { + adjust_downwards(heap->timers, i, heap->timer_count, timer); + } +} + +void grpc_timer_heap_init(grpc_timer_heap *heap) { + memset(heap, 0, sizeof(*heap)); +} + +void grpc_timer_heap_destroy(grpc_timer_heap *heap) { gpr_free(heap->timers); } + +int grpc_timer_heap_add(grpc_timer_heap *heap, grpc_timer *timer) { + if (heap->timer_count == heap->timer_capacity) { + heap->timer_capacity = + GPR_MAX(heap->timer_capacity + 1, heap->timer_capacity * 3 / 2); + heap->timers = (grpc_timer **)gpr_realloc( + heap->timers, heap->timer_capacity * sizeof(grpc_timer *)); + } + timer->heap_index = heap->timer_count; + adjust_upwards(heap->timers, heap->timer_count, timer); + heap->timer_count++; + return timer->heap_index == 0; +} + +void grpc_timer_heap_remove(grpc_timer_heap *heap, grpc_timer *timer) { + uint32_t i = timer->heap_index; + if (i == heap->timer_count - 1) { + heap->timer_count--; + maybe_shrink(heap); + return; + } + heap->timers[i] = heap->timers[heap->timer_count - 1]; + heap->timers[i]->heap_index = i; + heap->timer_count--; + maybe_shrink(heap); + note_changed_priority(heap, heap->timers[i]); +} + +int grpc_timer_heap_is_empty(grpc_timer_heap *heap) { + return heap->timer_count == 0; +} + +grpc_timer *grpc_timer_heap_top(grpc_timer_heap *heap) { + return heap->timers[0]; +} + +void grpc_timer_heap_pop(grpc_timer_heap *heap) { + grpc_timer_heap_remove(heap, grpc_timer_heap_top(heap)); +} + +#endif /* GRPC_TIMER_USE_GENERIC */ diff --git a/src/core/lib/iomgr/timer_manager.c b/src/core/lib/iomgr/timer_manager.c deleted file mode 100644 index 04ca44563d..0000000000 --- a/src/core/lib/iomgr/timer_manager.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/timer_manager.h" - -#include -#include -#include - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/timer.h" - -typedef struct completed_thread { - gpr_thd_id t; - struct completed_thread *next; -} completed_thread; - -extern grpc_tracer_flag grpc_timer_check_trace; - -// global mutex -static gpr_mu g_mu; -// are we multi-threaded -static bool g_threaded; -// cv to wait until a thread is needed -static gpr_cv g_cv_wait; -// cv for notification when threading ends -static gpr_cv g_cv_shutdown; -// number of threads in the system -static int g_thread_count; -// number of threads sitting around waiting -static int g_waiter_count; -// linked list of threads that have completed (and need joining) -static completed_thread *g_completed_threads; -// was the manager kicked by the timer system -static bool g_kicked; -// is there a thread waiting until the next timer should fire? -static bool g_has_timed_waiter; -// the deadline of the current timed waiter thread (only relevant if -// g_has_timed_waiter is true) -static gpr_timespec g_timed_waiter_deadline; -// generation counter to track which thread is waiting for the next timer -static uint64_t g_timed_waiter_generation; - -static void timer_thread(void *completed_thread_ptr); - -static void gc_completed_threads(void) { - if (g_completed_threads != NULL) { - completed_thread *to_gc = g_completed_threads; - g_completed_threads = NULL; - gpr_mu_unlock(&g_mu); - while (to_gc != NULL) { - gpr_thd_join(to_gc->t); - completed_thread *next = to_gc->next; - gpr_free(to_gc); - to_gc = next; - } - gpr_mu_lock(&g_mu); - } -} - -static void start_timer_thread_and_unlock(void) { - GPR_ASSERT(g_threaded); - ++g_waiter_count; - ++g_thread_count; - gpr_mu_unlock(&g_mu); - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "Spawn timer thread"); - } - gpr_thd_options opt = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&opt); - completed_thread *ct = (completed_thread *)gpr_malloc(sizeof(*ct)); - // The call to gpr_thd_new() has to be under the same lock used by - // gc_completed_threads(), particularly due to ct->t, which is written here - // (internally by gpr_thd_new) and read there. Otherwise it's possible for ct - // to leak through g_completed_threads and be freed in gc_completed_threads() - // before "&ct->t" is written to, causing a use-after-free. - gpr_mu_lock(&g_mu); - gpr_thd_new(&ct->t, timer_thread, ct, &opt); - gpr_mu_unlock(&g_mu); -} - -void grpc_timer_manager_tick() { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_timespec next = gpr_inf_future(GPR_CLOCK_MONOTONIC); - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - grpc_timer_check(&exec_ctx, now, &next); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void run_some_timers(grpc_exec_ctx *exec_ctx) { - // if there's something to execute... - gpr_mu_lock(&g_mu); - // remove a waiter from the pool, and start another thread if necessary - --g_waiter_count; - if (g_waiter_count == 0 && g_threaded) { - start_timer_thread_and_unlock(); - } else { - // if there's no thread waiting with a timeout, kick an existing - // waiter so that the next deadline is not missed - if (!g_has_timed_waiter) { - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "kick untimed waiter"); - } - gpr_cv_signal(&g_cv_wait); - } - gpr_mu_unlock(&g_mu); - } - // without our lock, flush the exec_ctx - grpc_exec_ctx_flush(exec_ctx); - gpr_mu_lock(&g_mu); - // garbage collect any threads hanging out that are dead - gc_completed_threads(); - // get ready to wait again - ++g_waiter_count; - gpr_mu_unlock(&g_mu); -} - -// wait until 'next' (or forever if there is already a timed waiter in the pool) -// returns true if the thread should continue executing (false if it should -// shutdown) -static bool wait_until(gpr_timespec next) { - const gpr_timespec inf_future = gpr_inf_future(GPR_CLOCK_MONOTONIC); - gpr_mu_lock(&g_mu); - // if we're not threaded anymore, leave - if (!g_threaded) { - gpr_mu_unlock(&g_mu); - return false; - } - - // If g_kicked is true at this point, it means there was a kick from the timer - // system that the timer-manager threads here missed. We cannot trust 'next' - // here any longer (since there might be an earlier deadline). So if g_kicked - // is true at this point, we should quickly exit this and get the next - // deadline from the timer system - - if (!g_kicked) { - // if there's no timed waiter, we should become one: that waiter waits - // only until the next timer should expire. All other timers wait forever - // - // 'g_timed_waiter_generation' is a global generation counter. The idea here - // is that the thread becoming a timed-waiter increments and stores this - // global counter locally in 'my_timed_waiter_generation' before going to - // sleep. After waking up, if my_timed_waiter_generation == - // g_timed_waiter_generation, it can be sure that it was the timed_waiter - // thread (and that no other thread took over while this was asleep) - // - // Initialize my_timed_waiter_generation to some value that is NOT equal to - // g_timed_waiter_generation - uint64_t my_timed_waiter_generation = g_timed_waiter_generation - 1; - - /* If there's no timed waiter, we should become one: that waiter waits only - until the next timer should expire. All other timer threads wait forever - unless their 'next' is earlier than the current timed-waiter's deadline - (in which case the thread with earlier 'next' takes over as the new timed - waiter) */ - if (gpr_time_cmp(next, inf_future) != 0) { - if (!g_has_timed_waiter || - (gpr_time_cmp(next, g_timed_waiter_deadline) < 0)) { - my_timed_waiter_generation = ++g_timed_waiter_generation; - g_has_timed_waiter = true; - g_timed_waiter_deadline = next; - - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_timespec wait_time = - gpr_time_sub(next, gpr_now(GPR_CLOCK_MONOTONIC)); - gpr_log(GPR_DEBUG, "sleep for a %" PRId64 ".%09d seconds", - wait_time.tv_sec, wait_time.tv_nsec); - } - } else { // g_timed_waiter == true && next >= g_timed_waiter_deadline - next = inf_future; - } - } - - if (GRPC_TRACER_ON(grpc_timer_check_trace) && - gpr_time_cmp(next, inf_future) == 0) { - gpr_log(GPR_DEBUG, "sleep until kicked"); - } - - gpr_cv_wait(&g_cv_wait, &g_mu, next); - - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d", - my_timed_waiter_generation == g_timed_waiter_generation, - g_kicked); - } - // if this was the timed waiter, then we need to check timers, and flag - // that there's now no timed waiter... we'll look for a replacement if - // there's work to do after checking timers (code above) - if (my_timed_waiter_generation == g_timed_waiter_generation) { - g_has_timed_waiter = false; - g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - } - } - - // if this was a kick from the timer system, consume it (and don't stop - // this thread yet) - if (g_kicked) { - grpc_timer_consume_kick(); - g_kicked = false; - } - - gpr_mu_unlock(&g_mu); - return true; -} - -static void timer_main_loop(grpc_exec_ctx *exec_ctx) { - const gpr_timespec inf_future = gpr_inf_future(GPR_CLOCK_MONOTONIC); - for (;;) { - gpr_timespec next = inf_future; - gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); - // check timer state, updates next to the next time to run a check - switch (grpc_timer_check(exec_ctx, now, &next)) { - case GRPC_TIMERS_FIRED: - run_some_timers(exec_ctx); - break; - case GRPC_TIMERS_NOT_CHECKED: - /* This case only happens under contention, meaning more than one timer - manager thread checked timers concurrently. - - If that happens, we're guaranteed that some other thread has just - checked timers, and this will avalanche into some other thread seeing - empty timers and doing a timed sleep. - - Consequently, we can just sleep forever here and be happy at some - saved wakeup cycles. */ - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "timers not checked: expect another thread to"); - } - next = inf_future; - /* fall through */ - case GRPC_TIMERS_CHECKED_AND_EMPTY: - if (!wait_until(next)) { - return; - } - break; - } - } -} - -static void timer_thread_cleanup(completed_thread *ct) { - gpr_mu_lock(&g_mu); - // terminate the thread: drop the waiter count, thread count, and let whomever - // stopped the threading stuff know that we're done - --g_waiter_count; - --g_thread_count; - if (0 == g_thread_count) { - gpr_cv_signal(&g_cv_shutdown); - } - ct->next = g_completed_threads; - g_completed_threads = ct; - gpr_mu_unlock(&g_mu); - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "End timer thread"); - } -} - -static void timer_thread(void *completed_thread_ptr) { - // this threads exec_ctx: we try to run things through to completion here - // since it's easy to spin up new threads - grpc_exec_ctx exec_ctx = - GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL); - timer_main_loop(&exec_ctx); - grpc_exec_ctx_finish(&exec_ctx); - timer_thread_cleanup((completed_thread *)completed_thread_ptr); -} - -static void start_threads(void) { - gpr_mu_lock(&g_mu); - if (!g_threaded) { - g_threaded = true; - start_timer_thread_and_unlock(); - } else { - g_threaded = false; - gpr_mu_unlock(&g_mu); - } -} - -void grpc_timer_manager_init(void) { - gpr_mu_init(&g_mu); - gpr_cv_init(&g_cv_wait); - gpr_cv_init(&g_cv_shutdown); - g_threaded = false; - g_thread_count = 0; - g_waiter_count = 0; - g_completed_threads = NULL; - - g_has_timed_waiter = false; - g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - - start_threads(); -} - -static void stop_threads(void) { - gpr_mu_lock(&g_mu); - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "stop timer threads: threaded=%d", g_threaded); - } - if (g_threaded) { - g_threaded = false; - gpr_cv_broadcast(&g_cv_wait); - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count); - } - while (g_thread_count > 0) { - gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); - if (GRPC_TRACER_ON(grpc_timer_check_trace)) { - gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count); - } - gc_completed_threads(); - } - } - gpr_mu_unlock(&g_mu); -} - -void grpc_timer_manager_shutdown(void) { - stop_threads(); - - gpr_mu_destroy(&g_mu); - gpr_cv_destroy(&g_cv_wait); - gpr_cv_destroy(&g_cv_shutdown); -} - -void grpc_timer_manager_set_threading(bool threaded) { - if (threaded) { - start_threads(); - } else { - stop_threads(); - } -} - -void grpc_kick_poller(void) { - gpr_mu_lock(&g_mu); - g_kicked = true; - g_has_timed_waiter = false; - g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - ++g_timed_waiter_generation; - gpr_cv_signal(&g_cv_wait); - gpr_mu_unlock(&g_mu); -} diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc new file mode 100644 index 0000000000..04ca44563d --- /dev/null +++ b/src/core/lib/iomgr/timer_manager.cc @@ -0,0 +1,354 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/timer_manager.h" + +#include +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/timer.h" + +typedef struct completed_thread { + gpr_thd_id t; + struct completed_thread *next; +} completed_thread; + +extern grpc_tracer_flag grpc_timer_check_trace; + +// global mutex +static gpr_mu g_mu; +// are we multi-threaded +static bool g_threaded; +// cv to wait until a thread is needed +static gpr_cv g_cv_wait; +// cv for notification when threading ends +static gpr_cv g_cv_shutdown; +// number of threads in the system +static int g_thread_count; +// number of threads sitting around waiting +static int g_waiter_count; +// linked list of threads that have completed (and need joining) +static completed_thread *g_completed_threads; +// was the manager kicked by the timer system +static bool g_kicked; +// is there a thread waiting until the next timer should fire? +static bool g_has_timed_waiter; +// the deadline of the current timed waiter thread (only relevant if +// g_has_timed_waiter is true) +static gpr_timespec g_timed_waiter_deadline; +// generation counter to track which thread is waiting for the next timer +static uint64_t g_timed_waiter_generation; + +static void timer_thread(void *completed_thread_ptr); + +static void gc_completed_threads(void) { + if (g_completed_threads != NULL) { + completed_thread *to_gc = g_completed_threads; + g_completed_threads = NULL; + gpr_mu_unlock(&g_mu); + while (to_gc != NULL) { + gpr_thd_join(to_gc->t); + completed_thread *next = to_gc->next; + gpr_free(to_gc); + to_gc = next; + } + gpr_mu_lock(&g_mu); + } +} + +static void start_timer_thread_and_unlock(void) { + GPR_ASSERT(g_threaded); + ++g_waiter_count; + ++g_thread_count; + gpr_mu_unlock(&g_mu); + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "Spawn timer thread"); + } + gpr_thd_options opt = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&opt); + completed_thread *ct = (completed_thread *)gpr_malloc(sizeof(*ct)); + // The call to gpr_thd_new() has to be under the same lock used by + // gc_completed_threads(), particularly due to ct->t, which is written here + // (internally by gpr_thd_new) and read there. Otherwise it's possible for ct + // to leak through g_completed_threads and be freed in gc_completed_threads() + // before "&ct->t" is written to, causing a use-after-free. + gpr_mu_lock(&g_mu); + gpr_thd_new(&ct->t, timer_thread, ct, &opt); + gpr_mu_unlock(&g_mu); +} + +void grpc_timer_manager_tick() { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_timespec next = gpr_inf_future(GPR_CLOCK_MONOTONIC); + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + grpc_timer_check(&exec_ctx, now, &next); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void run_some_timers(grpc_exec_ctx *exec_ctx) { + // if there's something to execute... + gpr_mu_lock(&g_mu); + // remove a waiter from the pool, and start another thread if necessary + --g_waiter_count; + if (g_waiter_count == 0 && g_threaded) { + start_timer_thread_and_unlock(); + } else { + // if there's no thread waiting with a timeout, kick an existing + // waiter so that the next deadline is not missed + if (!g_has_timed_waiter) { + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "kick untimed waiter"); + } + gpr_cv_signal(&g_cv_wait); + } + gpr_mu_unlock(&g_mu); + } + // without our lock, flush the exec_ctx + grpc_exec_ctx_flush(exec_ctx); + gpr_mu_lock(&g_mu); + // garbage collect any threads hanging out that are dead + gc_completed_threads(); + // get ready to wait again + ++g_waiter_count; + gpr_mu_unlock(&g_mu); +} + +// wait until 'next' (or forever if there is already a timed waiter in the pool) +// returns true if the thread should continue executing (false if it should +// shutdown) +static bool wait_until(gpr_timespec next) { + const gpr_timespec inf_future = gpr_inf_future(GPR_CLOCK_MONOTONIC); + gpr_mu_lock(&g_mu); + // if we're not threaded anymore, leave + if (!g_threaded) { + gpr_mu_unlock(&g_mu); + return false; + } + + // If g_kicked is true at this point, it means there was a kick from the timer + // system that the timer-manager threads here missed. We cannot trust 'next' + // here any longer (since there might be an earlier deadline). So if g_kicked + // is true at this point, we should quickly exit this and get the next + // deadline from the timer system + + if (!g_kicked) { + // if there's no timed waiter, we should become one: that waiter waits + // only until the next timer should expire. All other timers wait forever + // + // 'g_timed_waiter_generation' is a global generation counter. The idea here + // is that the thread becoming a timed-waiter increments and stores this + // global counter locally in 'my_timed_waiter_generation' before going to + // sleep. After waking up, if my_timed_waiter_generation == + // g_timed_waiter_generation, it can be sure that it was the timed_waiter + // thread (and that no other thread took over while this was asleep) + // + // Initialize my_timed_waiter_generation to some value that is NOT equal to + // g_timed_waiter_generation + uint64_t my_timed_waiter_generation = g_timed_waiter_generation - 1; + + /* If there's no timed waiter, we should become one: that waiter waits only + until the next timer should expire. All other timer threads wait forever + unless their 'next' is earlier than the current timed-waiter's deadline + (in which case the thread with earlier 'next' takes over as the new timed + waiter) */ + if (gpr_time_cmp(next, inf_future) != 0) { + if (!g_has_timed_waiter || + (gpr_time_cmp(next, g_timed_waiter_deadline) < 0)) { + my_timed_waiter_generation = ++g_timed_waiter_generation; + g_has_timed_waiter = true; + g_timed_waiter_deadline = next; + + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_timespec wait_time = + gpr_time_sub(next, gpr_now(GPR_CLOCK_MONOTONIC)); + gpr_log(GPR_DEBUG, "sleep for a %" PRId64 ".%09d seconds", + wait_time.tv_sec, wait_time.tv_nsec); + } + } else { // g_timed_waiter == true && next >= g_timed_waiter_deadline + next = inf_future; + } + } + + if (GRPC_TRACER_ON(grpc_timer_check_trace) && + gpr_time_cmp(next, inf_future) == 0) { + gpr_log(GPR_DEBUG, "sleep until kicked"); + } + + gpr_cv_wait(&g_cv_wait, &g_mu, next); + + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "wait ended: was_timed:%d kicked:%d", + my_timed_waiter_generation == g_timed_waiter_generation, + g_kicked); + } + // if this was the timed waiter, then we need to check timers, and flag + // that there's now no timed waiter... we'll look for a replacement if + // there's work to do after checking timers (code above) + if (my_timed_waiter_generation == g_timed_waiter_generation) { + g_has_timed_waiter = false; + g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + } + } + + // if this was a kick from the timer system, consume it (and don't stop + // this thread yet) + if (g_kicked) { + grpc_timer_consume_kick(); + g_kicked = false; + } + + gpr_mu_unlock(&g_mu); + return true; +} + +static void timer_main_loop(grpc_exec_ctx *exec_ctx) { + const gpr_timespec inf_future = gpr_inf_future(GPR_CLOCK_MONOTONIC); + for (;;) { + gpr_timespec next = inf_future; + gpr_timespec now = gpr_now(GPR_CLOCK_MONOTONIC); + // check timer state, updates next to the next time to run a check + switch (grpc_timer_check(exec_ctx, now, &next)) { + case GRPC_TIMERS_FIRED: + run_some_timers(exec_ctx); + break; + case GRPC_TIMERS_NOT_CHECKED: + /* This case only happens under contention, meaning more than one timer + manager thread checked timers concurrently. + + If that happens, we're guaranteed that some other thread has just + checked timers, and this will avalanche into some other thread seeing + empty timers and doing a timed sleep. + + Consequently, we can just sleep forever here and be happy at some + saved wakeup cycles. */ + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "timers not checked: expect another thread to"); + } + next = inf_future; + /* fall through */ + case GRPC_TIMERS_CHECKED_AND_EMPTY: + if (!wait_until(next)) { + return; + } + break; + } + } +} + +static void timer_thread_cleanup(completed_thread *ct) { + gpr_mu_lock(&g_mu); + // terminate the thread: drop the waiter count, thread count, and let whomever + // stopped the threading stuff know that we're done + --g_waiter_count; + --g_thread_count; + if (0 == g_thread_count) { + gpr_cv_signal(&g_cv_shutdown); + } + ct->next = g_completed_threads; + g_completed_threads = ct; + gpr_mu_unlock(&g_mu); + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "End timer thread"); + } +} + +static void timer_thread(void *completed_thread_ptr) { + // this threads exec_ctx: we try to run things through to completion here + // since it's easy to spin up new threads + grpc_exec_ctx exec_ctx = + GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL); + timer_main_loop(&exec_ctx); + grpc_exec_ctx_finish(&exec_ctx); + timer_thread_cleanup((completed_thread *)completed_thread_ptr); +} + +static void start_threads(void) { + gpr_mu_lock(&g_mu); + if (!g_threaded) { + g_threaded = true; + start_timer_thread_and_unlock(); + } else { + g_threaded = false; + gpr_mu_unlock(&g_mu); + } +} + +void grpc_timer_manager_init(void) { + gpr_mu_init(&g_mu); + gpr_cv_init(&g_cv_wait); + gpr_cv_init(&g_cv_shutdown); + g_threaded = false; + g_thread_count = 0; + g_waiter_count = 0; + g_completed_threads = NULL; + + g_has_timed_waiter = false; + g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + + start_threads(); +} + +static void stop_threads(void) { + gpr_mu_lock(&g_mu); + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "stop timer threads: threaded=%d", g_threaded); + } + if (g_threaded) { + g_threaded = false; + gpr_cv_broadcast(&g_cv_wait); + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count); + } + while (g_thread_count > 0) { + gpr_cv_wait(&g_cv_shutdown, &g_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); + if (GRPC_TRACER_ON(grpc_timer_check_trace)) { + gpr_log(GPR_DEBUG, "num timer threads: %d", g_thread_count); + } + gc_completed_threads(); + } + } + gpr_mu_unlock(&g_mu); +} + +void grpc_timer_manager_shutdown(void) { + stop_threads(); + + gpr_mu_destroy(&g_mu); + gpr_cv_destroy(&g_cv_wait); + gpr_cv_destroy(&g_cv_shutdown); +} + +void grpc_timer_manager_set_threading(bool threaded) { + if (threaded) { + start_threads(); + } else { + stop_threads(); + } +} + +void grpc_kick_poller(void) { + gpr_mu_lock(&g_mu); + g_kicked = true; + g_has_timed_waiter = false; + g_timed_waiter_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + ++g_timed_waiter_generation; + gpr_cv_signal(&g_cv_wait); + gpr_mu_unlock(&g_mu); +} diff --git a/src/core/lib/iomgr/timer_uv.c b/src/core/lib/iomgr/timer_uv.c deleted file mode 100644 index adced41f53..0000000000 --- a/src/core/lib/iomgr/timer_uv.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#if GRPC_UV - -#include -#include - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/iomgr/iomgr_uv.h" -#include "src/core/lib/iomgr/timer.h" - -#include - -grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer"); -grpc_tracer_flag grpc_timer_check_trace = - GRPC_TRACER_INITIALIZER(false, "timer_check"); - -static void timer_close_callback(uv_handle_t *handle) { gpr_free(handle); } - -static void stop_uv_timer(uv_timer_t *handle) { - uv_timer_stop(handle); - uv_unref((uv_handle_t *)handle); - uv_close((uv_handle_t *)handle, timer_close_callback); -} - -void run_expired_timer(uv_timer_t *handle) { - grpc_timer *timer = (grpc_timer *)handle->data; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_UV_ASSERT_SAME_THREAD(); - GPR_ASSERT(timer->pending); - timer->pending = 0; - GRPC_CLOSURE_SCHED(&exec_ctx, timer->closure, GRPC_ERROR_NONE); - stop_uv_timer(handle); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, - gpr_timespec deadline, grpc_closure *closure, - gpr_timespec now) { - uint64_t timeout; - uv_timer_t *uv_timer; - GRPC_UV_ASSERT_SAME_THREAD(); - timer->closure = closure; - if (gpr_time_cmp(deadline, now) <= 0) { - timer->pending = 0; - GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_NONE); - return; - } - timer->pending = 1; - timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); - uv_timer = gpr_malloc(sizeof(uv_timer_t)); - uv_timer_init(uv_default_loop(), uv_timer); - uv_timer->data = timer; - timer->uv_timer = uv_timer; - uv_timer_start(uv_timer, run_expired_timer, timeout, 0); - /* We assume that gRPC timers are only used alongside other active gRPC - objects, and that there will therefore always be something else keeping - the uv loop alive whenever there is a timer */ - uv_unref((uv_handle_t *)uv_timer); -} - -void grpc_timer_init_unset(grpc_timer *timer) { timer->pending = 0; } - -void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { - GRPC_UV_ASSERT_SAME_THREAD(); - if (timer->pending) { - timer->pending = 0; - GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED); - stop_uv_timer((uv_timer_t *)timer->uv_timer); - } -} - -grpc_timer_check_result grpc_timer_check(grpc_exec_ctx *exec_ctx, - gpr_timespec now, gpr_timespec *next) { - return GRPC_TIMERS_NOT_CHECKED; -} - -void grpc_timer_list_init(gpr_timespec now) {} -void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {} - -void grpc_timer_consume_kick(void) {} - -#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc new file mode 100644 index 0000000000..adced41f53 --- /dev/null +++ b/src/core/lib/iomgr/timer_uv.cc @@ -0,0 +1,101 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#if GRPC_UV + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/iomgr/iomgr_uv.h" +#include "src/core/lib/iomgr/timer.h" + +#include + +grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer"); +grpc_tracer_flag grpc_timer_check_trace = + GRPC_TRACER_INITIALIZER(false, "timer_check"); + +static void timer_close_callback(uv_handle_t *handle) { gpr_free(handle); } + +static void stop_uv_timer(uv_timer_t *handle) { + uv_timer_stop(handle); + uv_unref((uv_handle_t *)handle); + uv_close((uv_handle_t *)handle, timer_close_callback); +} + +void run_expired_timer(uv_timer_t *handle) { + grpc_timer *timer = (grpc_timer *)handle->data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_UV_ASSERT_SAME_THREAD(); + GPR_ASSERT(timer->pending); + timer->pending = 0; + GRPC_CLOSURE_SCHED(&exec_ctx, timer->closure, GRPC_ERROR_NONE); + stop_uv_timer(handle); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, + gpr_timespec deadline, grpc_closure *closure, + gpr_timespec now) { + uint64_t timeout; + uv_timer_t *uv_timer; + GRPC_UV_ASSERT_SAME_THREAD(); + timer->closure = closure; + if (gpr_time_cmp(deadline, now) <= 0) { + timer->pending = 0; + GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_NONE); + return; + } + timer->pending = 1; + timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); + uv_timer = gpr_malloc(sizeof(uv_timer_t)); + uv_timer_init(uv_default_loop(), uv_timer); + uv_timer->data = timer; + timer->uv_timer = uv_timer; + uv_timer_start(uv_timer, run_expired_timer, timeout, 0); + /* We assume that gRPC timers are only used alongside other active gRPC + objects, and that there will therefore always be something else keeping + the uv loop alive whenever there is a timer */ + uv_unref((uv_handle_t *)uv_timer); +} + +void grpc_timer_init_unset(grpc_timer *timer) { timer->pending = 0; } + +void grpc_timer_cancel(grpc_exec_ctx *exec_ctx, grpc_timer *timer) { + GRPC_UV_ASSERT_SAME_THREAD(); + if (timer->pending) { + timer->pending = 0; + GRPC_CLOSURE_SCHED(exec_ctx, timer->closure, GRPC_ERROR_CANCELLED); + stop_uv_timer((uv_timer_t *)timer->uv_timer); + } +} + +grpc_timer_check_result grpc_timer_check(grpc_exec_ctx *exec_ctx, + gpr_timespec now, gpr_timespec *next) { + return GRPC_TIMERS_NOT_CHECKED; +} + +void grpc_timer_list_init(gpr_timespec now) {} +void grpc_timer_list_shutdown(grpc_exec_ctx *exec_ctx) {} + +void grpc_timer_consume_kick(void) {} + +#endif /* GRPC_UV */ diff --git a/src/core/lib/iomgr/udp_server.c b/src/core/lib/iomgr/udp_server.c deleted file mode 100644 index 00b2e68bb5..0000000000 --- a/src/core/lib/iomgr/udp_server.c +++ /dev/null @@ -1,549 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_SOCKET - -#include "src/core/lib/iomgr/udp_server.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/iomgr/ev_posix.h" -#include "src/core/lib/iomgr/resolve_address.h" -#include "src/core/lib/iomgr/sockaddr.h" -#include "src/core/lib/iomgr/sockaddr_utils.h" -#include "src/core/lib/iomgr/socket_factory_posix.h" -#include "src/core/lib/iomgr/socket_utils_posix.h" -#include "src/core/lib/iomgr/unix_sockets_posix.h" -#include "src/core/lib/support/string.h" - -/* one listening port */ -typedef struct grpc_udp_listener grpc_udp_listener; -struct grpc_udp_listener { - int fd; - grpc_fd *emfd; - grpc_udp_server *server; - grpc_resolved_address addr; - grpc_closure read_closure; - grpc_closure write_closure; - // To be called when corresponding QuicGrpcServer closes all active - // connections. - grpc_closure orphan_fd_closure; - grpc_closure destroyed_closure; - grpc_udp_server_read_cb read_cb; - grpc_udp_server_write_cb write_cb; - grpc_udp_server_orphan_cb orphan_cb; - // True if orphan_cb is trigered. - bool orphan_notified; - - struct grpc_udp_listener *next; -}; - -struct shutdown_fd_args { - grpc_fd *fd; - gpr_mu *server_mu; -}; - -/* the overall server */ -struct grpc_udp_server { - gpr_mu mu; - - /* factory to use for creating and binding sockets, or NULL */ - grpc_socket_factory *socket_factory; - - /* active port count: how many ports are actually still listening */ - size_t active_ports; - /* destroyed port count: how many ports are completely destroyed */ - size_t destroyed_ports; - - /* is this server shutting down? (boolean) */ - int shutdown; - - /* linked list of server ports */ - grpc_udp_listener *head; - grpc_udp_listener *tail; - unsigned nports; - - /* shutdown callback */ - grpc_closure *shutdown_complete; - - /* all pollsets interested in new connections */ - grpc_pollset **pollsets; - /* number of pollsets in the pollsets array */ - size_t pollset_count; - /* opaque object to pass to callbacks */ - void *user_data; -}; - -static grpc_socket_factory *get_socket_factory(const grpc_channel_args *args) { - if (args) { - const grpc_arg *arg = grpc_channel_args_find(args, GRPC_ARG_SOCKET_FACTORY); - if (arg) { - GPR_ASSERT(arg->type == GRPC_ARG_POINTER); - return (grpc_socket_factory *)arg->value.pointer.p; - } - } - return NULL; -} - -grpc_udp_server *grpc_udp_server_create(const grpc_channel_args *args) { - grpc_udp_server *s = (grpc_udp_server *)gpr_malloc(sizeof(grpc_udp_server)); - gpr_mu_init(&s->mu); - s->socket_factory = get_socket_factory(args); - if (s->socket_factory) { - grpc_socket_factory_ref(s->socket_factory); - } - s->active_ports = 0; - s->destroyed_ports = 0; - s->shutdown = 0; - s->head = NULL; - s->tail = NULL; - s->nports = 0; - - return s; -} - -static void shutdown_fd(grpc_exec_ctx *exec_ctx, void *args, - grpc_error *error) { - struct shutdown_fd_args *shutdown_args = (struct shutdown_fd_args *)args; - gpr_mu_lock(shutdown_args->server_mu); - grpc_fd_shutdown(exec_ctx, shutdown_args->fd, GRPC_ERROR_REF(error)); - gpr_mu_unlock(shutdown_args->server_mu); - gpr_free(shutdown_args); -} - -static void dummy_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - // No-op. -} - -static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { - if (s->shutdown_complete != NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); - } - - gpr_mu_destroy(&s->mu); - - while (s->head) { - grpc_udp_listener *sp = s->head; - s->head = sp->next; - gpr_free(sp); - } - - if (s->socket_factory) { - grpc_socket_factory_unref(s->socket_factory); - } - - gpr_free(s); -} - -static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, - grpc_error *error) { - grpc_udp_server *s = (grpc_udp_server *)server; - gpr_mu_lock(&s->mu); - s->destroyed_ports++; - if (s->destroyed_ports == s->nports) { - gpr_mu_unlock(&s->mu); - finish_shutdown(exec_ctx, s); - } else { - gpr_mu_unlock(&s->mu); - } -} - -/* called when all listening endpoints have been shutdown, so no further - events will be received on them - at this point it's safe to destroy - things */ -static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { - /* delete ALL the things */ - gpr_mu_lock(&s->mu); - - GPR_ASSERT(s->shutdown); - - if (s->head) { - grpc_udp_listener *sp; - for (sp = s->head; sp; sp = sp->next) { - grpc_unlink_if_unix_domain_socket(&sp->addr); - - GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, - grpc_schedule_on_exec_ctx); - if (!sp->orphan_notified) { - /* Call the orphan_cb to signal that the FD is about to be closed and - * should no longer be used. Because at this point, all listening ports - * have been shutdown already, no need to shutdown again.*/ - GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, dummy_cb, sp->emfd, - grpc_schedule_on_exec_ctx); - GPR_ASSERT(sp->orphan_cb); - sp->orphan_cb(exec_ctx, sp->emfd, &sp->orphan_fd_closure, - sp->server->user_data); - } - grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, - false /* already_closed */, "udp_listener_shutdown"); - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - finish_shutdown(exec_ctx, s); - } -} - -void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, - grpc_closure *on_done) { - grpc_udp_listener *sp; - gpr_mu_lock(&s->mu); - - GPR_ASSERT(!s->shutdown); - s->shutdown = 1; - - s->shutdown_complete = on_done; - - /* shutdown all fd's */ - if (s->active_ports) { - for (sp = s->head; sp; sp = sp->next) { - GPR_ASSERT(sp->orphan_cb); - struct shutdown_fd_args *args = - (struct shutdown_fd_args *)gpr_malloc(sizeof(*args)); - args->fd = sp->emfd; - args->server_mu = &s->mu; - GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, shutdown_fd, args, - grpc_schedule_on_exec_ctx); - sp->orphan_cb(exec_ctx, sp->emfd, &sp->orphan_fd_closure, - sp->server->user_data); - sp->orphan_notified = true; - } - gpr_mu_unlock(&s->mu); - } else { - gpr_mu_unlock(&s->mu); - deactivated_all_ports(exec_ctx, s); - } -} - -static int bind_socket(grpc_socket_factory *socket_factory, int sockfd, - const grpc_resolved_address *addr) { - return (socket_factory != NULL) - ? grpc_socket_factory_bind(socket_factory, sockfd, addr) - : bind(sockfd, (struct sockaddr *)addr->addr, - (socklen_t)addr->len); -} - -/* Prepare a recently-created socket for listening. */ -static int prepare_socket(grpc_socket_factory *socket_factory, int fd, - const grpc_resolved_address *addr) { - grpc_resolved_address sockname_temp; - struct sockaddr *addr_ptr = (struct sockaddr *)addr->addr; - /* Set send/receive socket buffers to 1 MB */ - int buffer_size_bytes = 1024 * 1024; - - if (fd < 0) { - goto error; - } - - if (grpc_set_socket_nonblocking(fd, 1) != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Unable to set nonblocking %d: %s", fd, strerror(errno)); - goto error; - } - if (grpc_set_socket_cloexec(fd, 1) != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Unable to set cloexec %d: %s", fd, strerror(errno)); - goto error; - } - - if (grpc_set_socket_ip_pktinfo_if_possible(fd) != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Unable to set ip_pktinfo."); - goto error; - } else if (addr_ptr->sa_family == AF_INET6) { - if (grpc_set_socket_ipv6_recvpktinfo_if_possible(fd) != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Unable to set ipv6_recvpktinfo."); - goto error; - } - } - - GPR_ASSERT(addr->len < ~(socklen_t)0); - if (bind_socket(socket_factory, fd, addr) < 0) { - char *addr_str; - grpc_sockaddr_to_string(&addr_str, addr, 0); - gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); - gpr_free(addr_str); - goto error; - } - - sockname_temp.len = sizeof(struct sockaddr_storage); - - if (getsockname(fd, (struct sockaddr *)sockname_temp.addr, - (socklen_t *)&sockname_temp.len) < 0) { - goto error; - } - - if (grpc_set_socket_sndbuf(fd, buffer_size_bytes) != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Failed to set send buffer size to %d bytes", - buffer_size_bytes); - goto error; - } - - if (grpc_set_socket_rcvbuf(fd, buffer_size_bytes) != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Failed to set receive buffer size to %d bytes", - buffer_size_bytes); - goto error; - } - - return grpc_sockaddr_get_port(&sockname_temp); - -error: - if (fd >= 0) { - close(fd); - } - return -1; -} - -/* event manager callback when reads are ready */ -static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_udp_listener *sp = (grpc_udp_listener *)arg; - - gpr_mu_lock(&sp->server->mu); - if (error != GRPC_ERROR_NONE) { - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(exec_ctx, sp->server); - } else { - gpr_mu_unlock(&sp->server->mu); - } - return; - } - - /* Tell the registered callback that data is available to read. */ - GPR_ASSERT(sp->read_cb); - sp->read_cb(exec_ctx, sp->emfd, sp->server->user_data); - - /* Re-arm the notification event so we get another chance to read. */ - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); - gpr_mu_unlock(&sp->server->mu); -} - -static void on_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_udp_listener *sp = (grpc_udp_listener *)arg; - - gpr_mu_lock(&(sp->server->mu)); - if (error != GRPC_ERROR_NONE) { - if (0 == --sp->server->active_ports && sp->server->shutdown) { - gpr_mu_unlock(&sp->server->mu); - deactivated_all_ports(exec_ctx, sp->server); - } else { - gpr_mu_unlock(&sp->server->mu); - } - return; - } - - /* Tell the registered callback that the socket is writeable. */ - GPR_ASSERT(sp->write_cb); - sp->write_cb(exec_ctx, sp->emfd, sp->server->user_data); - - /* Re-arm the notification event so we get another chance to write. */ - grpc_fd_notify_on_write(exec_ctx, sp->emfd, &sp->write_closure); - gpr_mu_unlock(&sp->server->mu); -} - -static int add_socket_to_server(grpc_udp_server *s, int fd, - const grpc_resolved_address *addr, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb) { - grpc_udp_listener *sp; - int port; - char *addr_str; - char *name; - - port = prepare_socket(s->socket_factory, fd, addr); - if (port >= 0) { - grpc_sockaddr_to_string(&addr_str, addr, 1); - gpr_asprintf(&name, "udp-server-listener:%s", addr_str); - gpr_free(addr_str); - gpr_mu_lock(&s->mu); - s->nports++; - sp = (grpc_udp_listener *)gpr_malloc(sizeof(grpc_udp_listener)); - sp->next = NULL; - if (s->head == NULL) { - s->head = sp; - } else { - s->tail->next = sp; - } - s->tail = sp; - sp->server = s; - sp->fd = fd; - sp->emfd = grpc_fd_create(fd, name); - memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); - sp->read_cb = read_cb; - sp->write_cb = write_cb; - sp->orphan_cb = orphan_cb; - sp->orphan_notified = false; - GPR_ASSERT(sp->emfd); - gpr_mu_unlock(&s->mu); - gpr_free(name); - } - - return port; -} - -int grpc_udp_server_add_port(grpc_udp_server *s, - const grpc_resolved_address *addr, - grpc_udp_server_read_cb read_cb, - grpc_udp_server_write_cb write_cb, - grpc_udp_server_orphan_cb orphan_cb) { - grpc_udp_listener *sp; - int allocated_port1 = -1; - int allocated_port2 = -1; - int fd; - grpc_dualstack_mode dsmode; - grpc_resolved_address addr6_v4mapped; - grpc_resolved_address wild4; - grpc_resolved_address wild6; - grpc_resolved_address addr4_copy; - grpc_resolved_address *allocated_addr = NULL; - grpc_resolved_address sockname_temp; - int port; - - /* Check if this is a wildcard port, and if so, try to keep the port the same - as some previously created listener. */ - if (grpc_sockaddr_get_port(addr) == 0) { - for (sp = s->head; sp; sp = sp->next) { - sockname_temp.len = sizeof(struct sockaddr_storage); - if (0 == getsockname(sp->fd, (struct sockaddr *)sockname_temp.addr, - (socklen_t *)&sockname_temp.len)) { - port = grpc_sockaddr_get_port(&sockname_temp); - if (port > 0) { - allocated_addr = (grpc_resolved_address *)gpr_malloc( - sizeof(grpc_resolved_address)); - memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); - grpc_sockaddr_set_port(allocated_addr, port); - addr = allocated_addr; - break; - } - } - } - } - - if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { - addr = &addr6_v4mapped; - } - - /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ - if (grpc_sockaddr_is_wildcard(addr, &port)) { - grpc_sockaddr_make_wildcards(port, &wild4, &wild6); - - /* Try listening on IPv6 first. */ - addr = &wild6; - // TODO(rjshade): Test and propagate the returned grpc_error*: - GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( - s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); - allocated_port1 = - add_socket_to_server(s, fd, addr, read_cb, write_cb, orphan_cb); - if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { - goto done; - } - - /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ - if (port == 0 && allocated_port1 > 0) { - grpc_sockaddr_set_port(&wild4, allocated_port1); - } - addr = &wild4; - } - - // TODO(rjshade): Test and propagate the returned grpc_error*: - GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( - s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); - if (fd < 0) { - gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno)); - } - if (dsmode == GRPC_DSMODE_IPV4 && - grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { - addr = &addr4_copy; - } - allocated_port2 = - add_socket_to_server(s, fd, addr, read_cb, write_cb, orphan_cb); - -done: - gpr_free(allocated_addr); - return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; -} - -int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned port_index) { - grpc_udp_listener *sp; - if (port_index >= s->nports) { - return -1; - } - - for (sp = s->head; sp && port_index != 0; sp = sp->next) { - --port_index; - } - return sp->fd; -} - -void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, - grpc_pollset **pollsets, size_t pollset_count, - void *user_data) { - size_t i; - gpr_mu_lock(&s->mu); - grpc_udp_listener *sp; - GPR_ASSERT(s->active_ports == 0); - s->pollsets = pollsets; - s->user_data = user_data; - - sp = s->head; - while (sp != NULL) { - for (i = 0; i < pollset_count; i++) { - grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); - } - GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); - - GRPC_CLOSURE_INIT(&sp->write_closure, on_write, sp, - grpc_schedule_on_exec_ctx); - grpc_fd_notify_on_write(exec_ctx, sp->emfd, &sp->write_closure); - - /* Registered for both read and write callbacks: increment active_ports - * twice to account for this, and delay free-ing of memory until both - * on_read and on_write have fired. */ - s->active_ports += 2; - - sp = sp->next; - } - - gpr_mu_unlock(&s->mu); -} - -#endif diff --git a/src/core/lib/iomgr/udp_server.cc b/src/core/lib/iomgr/udp_server.cc new file mode 100644 index 0000000000..00b2e68bb5 --- /dev/null +++ b/src/core/lib/iomgr/udp_server.cc @@ -0,0 +1,549 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_SOCKET + +#include "src/core/lib/iomgr/udp_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/iomgr/ev_posix.h" +#include "src/core/lib/iomgr/resolve_address.h" +#include "src/core/lib/iomgr/sockaddr.h" +#include "src/core/lib/iomgr/sockaddr_utils.h" +#include "src/core/lib/iomgr/socket_factory_posix.h" +#include "src/core/lib/iomgr/socket_utils_posix.h" +#include "src/core/lib/iomgr/unix_sockets_posix.h" +#include "src/core/lib/support/string.h" + +/* one listening port */ +typedef struct grpc_udp_listener grpc_udp_listener; +struct grpc_udp_listener { + int fd; + grpc_fd *emfd; + grpc_udp_server *server; + grpc_resolved_address addr; + grpc_closure read_closure; + grpc_closure write_closure; + // To be called when corresponding QuicGrpcServer closes all active + // connections. + grpc_closure orphan_fd_closure; + grpc_closure destroyed_closure; + grpc_udp_server_read_cb read_cb; + grpc_udp_server_write_cb write_cb; + grpc_udp_server_orphan_cb orphan_cb; + // True if orphan_cb is trigered. + bool orphan_notified; + + struct grpc_udp_listener *next; +}; + +struct shutdown_fd_args { + grpc_fd *fd; + gpr_mu *server_mu; +}; + +/* the overall server */ +struct grpc_udp_server { + gpr_mu mu; + + /* factory to use for creating and binding sockets, or NULL */ + grpc_socket_factory *socket_factory; + + /* active port count: how many ports are actually still listening */ + size_t active_ports; + /* destroyed port count: how many ports are completely destroyed */ + size_t destroyed_ports; + + /* is this server shutting down? (boolean) */ + int shutdown; + + /* linked list of server ports */ + grpc_udp_listener *head; + grpc_udp_listener *tail; + unsigned nports; + + /* shutdown callback */ + grpc_closure *shutdown_complete; + + /* all pollsets interested in new connections */ + grpc_pollset **pollsets; + /* number of pollsets in the pollsets array */ + size_t pollset_count; + /* opaque object to pass to callbacks */ + void *user_data; +}; + +static grpc_socket_factory *get_socket_factory(const grpc_channel_args *args) { + if (args) { + const grpc_arg *arg = grpc_channel_args_find(args, GRPC_ARG_SOCKET_FACTORY); + if (arg) { + GPR_ASSERT(arg->type == GRPC_ARG_POINTER); + return (grpc_socket_factory *)arg->value.pointer.p; + } + } + return NULL; +} + +grpc_udp_server *grpc_udp_server_create(const grpc_channel_args *args) { + grpc_udp_server *s = (grpc_udp_server *)gpr_malloc(sizeof(grpc_udp_server)); + gpr_mu_init(&s->mu); + s->socket_factory = get_socket_factory(args); + if (s->socket_factory) { + grpc_socket_factory_ref(s->socket_factory); + } + s->active_ports = 0; + s->destroyed_ports = 0; + s->shutdown = 0; + s->head = NULL; + s->tail = NULL; + s->nports = 0; + + return s; +} + +static void shutdown_fd(grpc_exec_ctx *exec_ctx, void *args, + grpc_error *error) { + struct shutdown_fd_args *shutdown_args = (struct shutdown_fd_args *)args; + gpr_mu_lock(shutdown_args->server_mu); + grpc_fd_shutdown(exec_ctx, shutdown_args->fd, GRPC_ERROR_REF(error)); + gpr_mu_unlock(shutdown_args->server_mu); + gpr_free(shutdown_args); +} + +static void dummy_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + // No-op. +} + +static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { + if (s->shutdown_complete != NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, s->shutdown_complete, GRPC_ERROR_NONE); + } + + gpr_mu_destroy(&s->mu); + + while (s->head) { + grpc_udp_listener *sp = s->head; + s->head = sp->next; + gpr_free(sp); + } + + if (s->socket_factory) { + grpc_socket_factory_unref(s->socket_factory); + } + + gpr_free(s); +} + +static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, + grpc_error *error) { + grpc_udp_server *s = (grpc_udp_server *)server; + gpr_mu_lock(&s->mu); + s->destroyed_ports++; + if (s->destroyed_ports == s->nports) { + gpr_mu_unlock(&s->mu); + finish_shutdown(exec_ctx, s); + } else { + gpr_mu_unlock(&s->mu); + } +} + +/* called when all listening endpoints have been shutdown, so no further + events will be received on them - at this point it's safe to destroy + things */ +static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_udp_server *s) { + /* delete ALL the things */ + gpr_mu_lock(&s->mu); + + GPR_ASSERT(s->shutdown); + + if (s->head) { + grpc_udp_listener *sp; + for (sp = s->head; sp; sp = sp->next) { + grpc_unlink_if_unix_domain_socket(&sp->addr); + + GRPC_CLOSURE_INIT(&sp->destroyed_closure, destroyed_port, s, + grpc_schedule_on_exec_ctx); + if (!sp->orphan_notified) { + /* Call the orphan_cb to signal that the FD is about to be closed and + * should no longer be used. Because at this point, all listening ports + * have been shutdown already, no need to shutdown again.*/ + GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, dummy_cb, sp->emfd, + grpc_schedule_on_exec_ctx); + GPR_ASSERT(sp->orphan_cb); + sp->orphan_cb(exec_ctx, sp->emfd, &sp->orphan_fd_closure, + sp->server->user_data); + } + grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL, + false /* already_closed */, "udp_listener_shutdown"); + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + finish_shutdown(exec_ctx, s); + } +} + +void grpc_udp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, + grpc_closure *on_done) { + grpc_udp_listener *sp; + gpr_mu_lock(&s->mu); + + GPR_ASSERT(!s->shutdown); + s->shutdown = 1; + + s->shutdown_complete = on_done; + + /* shutdown all fd's */ + if (s->active_ports) { + for (sp = s->head; sp; sp = sp->next) { + GPR_ASSERT(sp->orphan_cb); + struct shutdown_fd_args *args = + (struct shutdown_fd_args *)gpr_malloc(sizeof(*args)); + args->fd = sp->emfd; + args->server_mu = &s->mu; + GRPC_CLOSURE_INIT(&sp->orphan_fd_closure, shutdown_fd, args, + grpc_schedule_on_exec_ctx); + sp->orphan_cb(exec_ctx, sp->emfd, &sp->orphan_fd_closure, + sp->server->user_data); + sp->orphan_notified = true; + } + gpr_mu_unlock(&s->mu); + } else { + gpr_mu_unlock(&s->mu); + deactivated_all_ports(exec_ctx, s); + } +} + +static int bind_socket(grpc_socket_factory *socket_factory, int sockfd, + const grpc_resolved_address *addr) { + return (socket_factory != NULL) + ? grpc_socket_factory_bind(socket_factory, sockfd, addr) + : bind(sockfd, (struct sockaddr *)addr->addr, + (socklen_t)addr->len); +} + +/* Prepare a recently-created socket for listening. */ +static int prepare_socket(grpc_socket_factory *socket_factory, int fd, + const grpc_resolved_address *addr) { + grpc_resolved_address sockname_temp; + struct sockaddr *addr_ptr = (struct sockaddr *)addr->addr; + /* Set send/receive socket buffers to 1 MB */ + int buffer_size_bytes = 1024 * 1024; + + if (fd < 0) { + goto error; + } + + if (grpc_set_socket_nonblocking(fd, 1) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set nonblocking %d: %s", fd, strerror(errno)); + goto error; + } + if (grpc_set_socket_cloexec(fd, 1) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set cloexec %d: %s", fd, strerror(errno)); + goto error; + } + + if (grpc_set_socket_ip_pktinfo_if_possible(fd) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set ip_pktinfo."); + goto error; + } else if (addr_ptr->sa_family == AF_INET6) { + if (grpc_set_socket_ipv6_recvpktinfo_if_possible(fd) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Unable to set ipv6_recvpktinfo."); + goto error; + } + } + + GPR_ASSERT(addr->len < ~(socklen_t)0); + if (bind_socket(socket_factory, fd, addr) < 0) { + char *addr_str; + grpc_sockaddr_to_string(&addr_str, addr, 0); + gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno)); + gpr_free(addr_str); + goto error; + } + + sockname_temp.len = sizeof(struct sockaddr_storage); + + if (getsockname(fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len) < 0) { + goto error; + } + + if (grpc_set_socket_sndbuf(fd, buffer_size_bytes) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Failed to set send buffer size to %d bytes", + buffer_size_bytes); + goto error; + } + + if (grpc_set_socket_rcvbuf(fd, buffer_size_bytes) != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Failed to set receive buffer size to %d bytes", + buffer_size_bytes); + goto error; + } + + return grpc_sockaddr_get_port(&sockname_temp); + +error: + if (fd >= 0) { + close(fd); + } + return -1; +} + +/* event manager callback when reads are ready */ +static void on_read(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_udp_listener *sp = (grpc_udp_listener *)arg; + + gpr_mu_lock(&sp->server->mu); + if (error != GRPC_ERROR_NONE) { + if (0 == --sp->server->active_ports && sp->server->shutdown) { + gpr_mu_unlock(&sp->server->mu); + deactivated_all_ports(exec_ctx, sp->server); + } else { + gpr_mu_unlock(&sp->server->mu); + } + return; + } + + /* Tell the registered callback that data is available to read. */ + GPR_ASSERT(sp->read_cb); + sp->read_cb(exec_ctx, sp->emfd, sp->server->user_data); + + /* Re-arm the notification event so we get another chance to read. */ + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + gpr_mu_unlock(&sp->server->mu); +} + +static void on_write(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_udp_listener *sp = (grpc_udp_listener *)arg; + + gpr_mu_lock(&(sp->server->mu)); + if (error != GRPC_ERROR_NONE) { + if (0 == --sp->server->active_ports && sp->server->shutdown) { + gpr_mu_unlock(&sp->server->mu); + deactivated_all_ports(exec_ctx, sp->server); + } else { + gpr_mu_unlock(&sp->server->mu); + } + return; + } + + /* Tell the registered callback that the socket is writeable. */ + GPR_ASSERT(sp->write_cb); + sp->write_cb(exec_ctx, sp->emfd, sp->server->user_data); + + /* Re-arm the notification event so we get another chance to write. */ + grpc_fd_notify_on_write(exec_ctx, sp->emfd, &sp->write_closure); + gpr_mu_unlock(&sp->server->mu); +} + +static int add_socket_to_server(grpc_udp_server *s, int fd, + const grpc_resolved_address *addr, + grpc_udp_server_read_cb read_cb, + grpc_udp_server_write_cb write_cb, + grpc_udp_server_orphan_cb orphan_cb) { + grpc_udp_listener *sp; + int port; + char *addr_str; + char *name; + + port = prepare_socket(s->socket_factory, fd, addr); + if (port >= 0) { + grpc_sockaddr_to_string(&addr_str, addr, 1); + gpr_asprintf(&name, "udp-server-listener:%s", addr_str); + gpr_free(addr_str); + gpr_mu_lock(&s->mu); + s->nports++; + sp = (grpc_udp_listener *)gpr_malloc(sizeof(grpc_udp_listener)); + sp->next = NULL; + if (s->head == NULL) { + s->head = sp; + } else { + s->tail->next = sp; + } + s->tail = sp; + sp->server = s; + sp->fd = fd; + sp->emfd = grpc_fd_create(fd, name); + memcpy(&sp->addr, addr, sizeof(grpc_resolved_address)); + sp->read_cb = read_cb; + sp->write_cb = write_cb; + sp->orphan_cb = orphan_cb; + sp->orphan_notified = false; + GPR_ASSERT(sp->emfd); + gpr_mu_unlock(&s->mu); + gpr_free(name); + } + + return port; +} + +int grpc_udp_server_add_port(grpc_udp_server *s, + const grpc_resolved_address *addr, + grpc_udp_server_read_cb read_cb, + grpc_udp_server_write_cb write_cb, + grpc_udp_server_orphan_cb orphan_cb) { + grpc_udp_listener *sp; + int allocated_port1 = -1; + int allocated_port2 = -1; + int fd; + grpc_dualstack_mode dsmode; + grpc_resolved_address addr6_v4mapped; + grpc_resolved_address wild4; + grpc_resolved_address wild6; + grpc_resolved_address addr4_copy; + grpc_resolved_address *allocated_addr = NULL; + grpc_resolved_address sockname_temp; + int port; + + /* Check if this is a wildcard port, and if so, try to keep the port the same + as some previously created listener. */ + if (grpc_sockaddr_get_port(addr) == 0) { + for (sp = s->head; sp; sp = sp->next) { + sockname_temp.len = sizeof(struct sockaddr_storage); + if (0 == getsockname(sp->fd, (struct sockaddr *)sockname_temp.addr, + (socklen_t *)&sockname_temp.len)) { + port = grpc_sockaddr_get_port(&sockname_temp); + if (port > 0) { + allocated_addr = (grpc_resolved_address *)gpr_malloc( + sizeof(grpc_resolved_address)); + memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); + grpc_sockaddr_set_port(allocated_addr, port); + addr = allocated_addr; + break; + } + } + } + } + + if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) { + addr = &addr6_v4mapped; + } + + /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */ + if (grpc_sockaddr_is_wildcard(addr, &port)) { + grpc_sockaddr_make_wildcards(port, &wild4, &wild6); + + /* Try listening on IPv6 first. */ + addr = &wild6; + // TODO(rjshade): Test and propagate the returned grpc_error*: + GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( + s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); + allocated_port1 = + add_socket_to_server(s, fd, addr, read_cb, write_cb, orphan_cb); + if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) { + goto done; + } + + /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */ + if (port == 0 && allocated_port1 > 0) { + grpc_sockaddr_set_port(&wild4, allocated_port1); + } + addr = &wild4; + } + + // TODO(rjshade): Test and propagate the returned grpc_error*: + GRPC_ERROR_UNREF(grpc_create_dualstack_socket_using_factory( + s->socket_factory, addr, SOCK_DGRAM, IPPROTO_UDP, &dsmode, &fd)); + if (fd < 0) { + gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno)); + } + if (dsmode == GRPC_DSMODE_IPV4 && + grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) { + addr = &addr4_copy; + } + allocated_port2 = + add_socket_to_server(s, fd, addr, read_cb, write_cb, orphan_cb); + +done: + gpr_free(allocated_addr); + return allocated_port1 >= 0 ? allocated_port1 : allocated_port2; +} + +int grpc_udp_server_get_fd(grpc_udp_server *s, unsigned port_index) { + grpc_udp_listener *sp; + if (port_index >= s->nports) { + return -1; + } + + for (sp = s->head; sp && port_index != 0; sp = sp->next) { + --port_index; + } + return sp->fd; +} + +void grpc_udp_server_start(grpc_exec_ctx *exec_ctx, grpc_udp_server *s, + grpc_pollset **pollsets, size_t pollset_count, + void *user_data) { + size_t i; + gpr_mu_lock(&s->mu); + grpc_udp_listener *sp; + GPR_ASSERT(s->active_ports == 0); + s->pollsets = pollsets; + s->user_data = user_data; + + sp = s->head; + while (sp != NULL) { + for (i = 0; i < pollset_count; i++) { + grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd); + } + GRPC_CLOSURE_INIT(&sp->read_closure, on_read, sp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure); + + GRPC_CLOSURE_INIT(&sp->write_closure, on_write, sp, + grpc_schedule_on_exec_ctx); + grpc_fd_notify_on_write(exec_ctx, sp->emfd, &sp->write_closure); + + /* Registered for both read and write callbacks: increment active_ports + * twice to account for this, and delay free-ing of memory until both + * on_read and on_write have fired. */ + s->active_ports += 2; + + sp = sp->next; + } + + gpr_mu_unlock(&s->mu); +} + +#endif diff --git a/src/core/lib/iomgr/unix_sockets_posix.c b/src/core/lib/iomgr/unix_sockets_posix.c deleted file mode 100644 index 35f898f13a..0000000000 --- a/src/core/lib/iomgr/unix_sockets_posix.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_HAVE_UNIX_SOCKET - -#include "src/core/lib/iomgr/sockaddr.h" - -#include -#include -#include -#include - -#include "src/core/lib/iomgr/unix_sockets_posix.h" - -#include -#include -#include - -void grpc_create_socketpair_if_unix(int sv[2]) { - GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); -} - -grpc_error *grpc_resolve_unix_domain_address(const char *name, - grpc_resolved_addresses **addrs) { - struct sockaddr_un *un; - if (strlen(name) > GPR_ARRAY_SIZE(((struct sockaddr_un *)0)->sun_path) - 1) { - char *err_msg; - grpc_error *err; - gpr_asprintf(&err_msg, - "Path name should not have more than %" PRIuPTR " characters.", - GPR_ARRAY_SIZE(un->sun_path) - 1); - err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); - gpr_free(err_msg); - return err; - } - *addrs = - (grpc_resolved_addresses *)gpr_malloc(sizeof(grpc_resolved_addresses)); - (*addrs)->naddrs = 1; - (*addrs)->addrs = - (grpc_resolved_address *)gpr_malloc(sizeof(grpc_resolved_address)); - un = (struct sockaddr_un *)(*addrs)->addrs->addr; - un->sun_family = AF_UNIX; - strcpy(un->sun_path, name); - (*addrs)->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1; - return GRPC_ERROR_NONE; -} - -int grpc_is_unix_socket(const grpc_resolved_address *resolved_addr) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - return addr->sa_family == AF_UNIX; -} - -void grpc_unlink_if_unix_domain_socket( - const grpc_resolved_address *resolved_addr) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - if (addr->sa_family != AF_UNIX) { - return; - } - struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; - struct stat st; - - if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { - unlink(un->sun_path); - } -} - -char *grpc_sockaddr_to_uri_unix_if_possible( - const grpc_resolved_address *resolved_addr) { - const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; - if (addr->sa_family != AF_UNIX) { - return NULL; - } - - char *result; - gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un *)addr)->sun_path); - return result; -} - -#endif diff --git a/src/core/lib/iomgr/unix_sockets_posix.cc b/src/core/lib/iomgr/unix_sockets_posix.cc new file mode 100644 index 0000000000..35f898f13a --- /dev/null +++ b/src/core/lib/iomgr/unix_sockets_posix.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_HAVE_UNIX_SOCKET + +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#include +#include +#include + +void grpc_create_socketpair_if_unix(int sv[2]) { + GPR_ASSERT(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0); +} + +grpc_error *grpc_resolve_unix_domain_address(const char *name, + grpc_resolved_addresses **addrs) { + struct sockaddr_un *un; + if (strlen(name) > GPR_ARRAY_SIZE(((struct sockaddr_un *)0)->sun_path) - 1) { + char *err_msg; + grpc_error *err; + gpr_asprintf(&err_msg, + "Path name should not have more than %" PRIuPTR " characters.", + GPR_ARRAY_SIZE(un->sun_path) - 1); + err = GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_msg); + gpr_free(err_msg); + return err; + } + *addrs = + (grpc_resolved_addresses *)gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addrs)->naddrs = 1; + (*addrs)->addrs = + (grpc_resolved_address *)gpr_malloc(sizeof(grpc_resolved_address)); + un = (struct sockaddr_un *)(*addrs)->addrs->addr; + un->sun_family = AF_UNIX; + strcpy(un->sun_path, name); + (*addrs)->addrs->len = strlen(un->sun_path) + sizeof(un->sun_family) + 1; + return GRPC_ERROR_NONE; +} + +int grpc_is_unix_socket(const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + return addr->sa_family == AF_UNIX; +} + +void grpc_unlink_if_unix_domain_socket( + const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + if (addr->sa_family != AF_UNIX) { + return; + } + struct sockaddr_un *un = (struct sockaddr_un *)resolved_addr->addr; + struct stat st; + + if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) { + unlink(un->sun_path); + } +} + +char *grpc_sockaddr_to_uri_unix_if_possible( + const grpc_resolved_address *resolved_addr) { + const struct sockaddr *addr = (const struct sockaddr *)resolved_addr->addr; + if (addr->sa_family != AF_UNIX) { + return NULL; + } + + char *result; + gpr_asprintf(&result, "unix:%s", ((struct sockaddr_un *)addr)->sun_path); + return result; +} + +#endif diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.c b/src/core/lib/iomgr/unix_sockets_posix_noop.c deleted file mode 100644 index e46b1c003d..0000000000 --- a/src/core/lib/iomgr/unix_sockets_posix_noop.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/unix_sockets_posix.h" - -#ifndef GRPC_HAVE_UNIX_SOCKET - -#include - -void grpc_create_socketpair_if_unix(int sv[2]) { - // TODO: Either implement this for the non-Unix socket case or make - // sure that it is never called in any such case. Until then, leave an - // assertion to notify if this gets called inadvertently - GPR_ASSERT(0); -} - -grpc_error *grpc_resolve_unix_domain_address( - const char *name, grpc_resolved_addresses **addresses) { - *addresses = NULL; - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Unix domain sockets are not supported on Windows"); -} - -int grpc_is_unix_socket(const grpc_resolved_address *addr) { return false; } - -void grpc_unlink_if_unix_domain_socket(const grpc_resolved_address *addr) {} - -char *grpc_sockaddr_to_uri_unix_if_possible(const grpc_resolved_address *addr) { - return NULL; -} - -#endif diff --git a/src/core/lib/iomgr/unix_sockets_posix_noop.cc b/src/core/lib/iomgr/unix_sockets_posix_noop.cc new file mode 100644 index 0000000000..e46b1c003d --- /dev/null +++ b/src/core/lib/iomgr/unix_sockets_posix_noop.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/unix_sockets_posix.h" + +#ifndef GRPC_HAVE_UNIX_SOCKET + +#include + +void grpc_create_socketpair_if_unix(int sv[2]) { + // TODO: Either implement this for the non-Unix socket case or make + // sure that it is never called in any such case. Until then, leave an + // assertion to notify if this gets called inadvertently + GPR_ASSERT(0); +} + +grpc_error *grpc_resolve_unix_domain_address( + const char *name, grpc_resolved_addresses **addresses) { + *addresses = NULL; + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unix domain sockets are not supported on Windows"); +} + +int grpc_is_unix_socket(const grpc_resolved_address *addr) { return false; } + +void grpc_unlink_if_unix_domain_socket(const grpc_resolved_address *addr) {} + +char *grpc_sockaddr_to_uri_unix_if_possible(const grpc_resolved_address *addr) { + return NULL; +} + +#endif diff --git a/src/core/lib/iomgr/wakeup_fd_cv.c b/src/core/lib/iomgr/wakeup_fd_cv.c deleted file mode 100644 index 268e0175dd..0000000000 --- a/src/core/lib/iomgr/wakeup_fd_cv.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_WAKEUP_FD - -#include "src/core/lib/iomgr/wakeup_fd_cv.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#define MAX_TABLE_RESIZE 256 - -extern cv_fd_table g_cvfds; - -static grpc_error* cv_fd_init(grpc_wakeup_fd* fd_info) { - unsigned int i, newsize; - int idx; - gpr_mu_lock(&g_cvfds.mu); - if (!g_cvfds.free_fds) { - newsize = GPR_MIN(g_cvfds.size * 2, g_cvfds.size + MAX_TABLE_RESIZE); - g_cvfds.cvfds = - (fd_node*)gpr_realloc(g_cvfds.cvfds, sizeof(fd_node) * newsize); - for (i = g_cvfds.size; i < newsize; i++) { - g_cvfds.cvfds[i].is_set = 0; - g_cvfds.cvfds[i].cvs = NULL; - g_cvfds.cvfds[i].next_free = g_cvfds.free_fds; - g_cvfds.free_fds = &g_cvfds.cvfds[i]; - } - g_cvfds.size = newsize; - } - - idx = (int)(g_cvfds.free_fds - g_cvfds.cvfds); - g_cvfds.free_fds = g_cvfds.free_fds->next_free; - g_cvfds.cvfds[idx].cvs = NULL; - g_cvfds.cvfds[idx].is_set = 0; - fd_info->read_fd = GRPC_IDX_TO_FD(idx); - fd_info->write_fd = -1; - gpr_mu_unlock(&g_cvfds.mu); - return GRPC_ERROR_NONE; -} - -static grpc_error* cv_fd_wakeup(grpc_wakeup_fd* fd_info) { - cv_node* cvn; - gpr_mu_lock(&g_cvfds.mu); - g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 1; - cvn = g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs; - while (cvn) { - gpr_cv_signal(cvn->cv); - cvn = cvn->next; - } - gpr_mu_unlock(&g_cvfds.mu); - return GRPC_ERROR_NONE; -} - -static grpc_error* cv_fd_consume(grpc_wakeup_fd* fd_info) { - gpr_mu_lock(&g_cvfds.mu); - g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 0; - gpr_mu_unlock(&g_cvfds.mu); - return GRPC_ERROR_NONE; -} - -static void cv_fd_destroy(grpc_wakeup_fd* fd_info) { - if (fd_info->read_fd == 0) { - return; - } - gpr_mu_lock(&g_cvfds.mu); - // Assert that there are no active pollers - GPR_ASSERT(!g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs); - g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].next_free = g_cvfds.free_fds; - g_cvfds.free_fds = &g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)]; - gpr_mu_unlock(&g_cvfds.mu); -} - -static int cv_check_availability(void) { return 1; } - -const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable = { - cv_fd_init, cv_fd_consume, cv_fd_wakeup, cv_fd_destroy, - cv_check_availability}; - -#endif /* GRPC_POSIX_WAKUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_cv.cc b/src/core/lib/iomgr/wakeup_fd_cv.cc new file mode 100644 index 0000000000..268e0175dd --- /dev/null +++ b/src/core/lib/iomgr/wakeup_fd_cv.cc @@ -0,0 +1,104 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_WAKEUP_FD + +#include "src/core/lib/iomgr/wakeup_fd_cv.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAX_TABLE_RESIZE 256 + +extern cv_fd_table g_cvfds; + +static grpc_error* cv_fd_init(grpc_wakeup_fd* fd_info) { + unsigned int i, newsize; + int idx; + gpr_mu_lock(&g_cvfds.mu); + if (!g_cvfds.free_fds) { + newsize = GPR_MIN(g_cvfds.size * 2, g_cvfds.size + MAX_TABLE_RESIZE); + g_cvfds.cvfds = + (fd_node*)gpr_realloc(g_cvfds.cvfds, sizeof(fd_node) * newsize); + for (i = g_cvfds.size; i < newsize; i++) { + g_cvfds.cvfds[i].is_set = 0; + g_cvfds.cvfds[i].cvs = NULL; + g_cvfds.cvfds[i].next_free = g_cvfds.free_fds; + g_cvfds.free_fds = &g_cvfds.cvfds[i]; + } + g_cvfds.size = newsize; + } + + idx = (int)(g_cvfds.free_fds - g_cvfds.cvfds); + g_cvfds.free_fds = g_cvfds.free_fds->next_free; + g_cvfds.cvfds[idx].cvs = NULL; + g_cvfds.cvfds[idx].is_set = 0; + fd_info->read_fd = GRPC_IDX_TO_FD(idx); + fd_info->write_fd = -1; + gpr_mu_unlock(&g_cvfds.mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* cv_fd_wakeup(grpc_wakeup_fd* fd_info) { + cv_node* cvn; + gpr_mu_lock(&g_cvfds.mu); + g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 1; + cvn = g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs; + while (cvn) { + gpr_cv_signal(cvn->cv); + cvn = cvn->next; + } + gpr_mu_unlock(&g_cvfds.mu); + return GRPC_ERROR_NONE; +} + +static grpc_error* cv_fd_consume(grpc_wakeup_fd* fd_info) { + gpr_mu_lock(&g_cvfds.mu); + g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].is_set = 0; + gpr_mu_unlock(&g_cvfds.mu); + return GRPC_ERROR_NONE; +} + +static void cv_fd_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd == 0) { + return; + } + gpr_mu_lock(&g_cvfds.mu); + // Assert that there are no active pollers + GPR_ASSERT(!g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].cvs); + g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)].next_free = g_cvfds.free_fds; + g_cvfds.free_fds = &g_cvfds.cvfds[GRPC_FD_TO_IDX(fd_info->read_fd)]; + gpr_mu_unlock(&g_cvfds.mu); +} + +static int cv_check_availability(void) { return 1; } + +const grpc_wakeup_fd_vtable grpc_cv_wakeup_fd_vtable = { + cv_fd_init, cv_fd_consume, cv_fd_wakeup, cv_fd_destroy, + cv_check_availability}; + +#endif /* GRPC_POSIX_WAKUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.c b/src/core/lib/iomgr/wakeup_fd_eventfd.c deleted file mode 100644 index 81cb7ee280..0000000000 --- a/src/core/lib/iomgr/wakeup_fd_eventfd.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_LINUX_EVENTFD - -#include -#include -#include - -#include - -#include "src/core/lib/iomgr/wakeup_fd_posix.h" -#include "src/core/lib/profiling/timers.h" - -static grpc_error* eventfd_create(grpc_wakeup_fd* fd_info) { - int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); - if (efd < 0) { - return GRPC_OS_ERROR(errno, "eventfd"); - } - fd_info->read_fd = efd; - fd_info->write_fd = -1; - return GRPC_ERROR_NONE; -} - -static grpc_error* eventfd_consume(grpc_wakeup_fd* fd_info) { - eventfd_t value; - int err; - do { - err = eventfd_read(fd_info->read_fd, &value); - } while (err < 0 && errno == EINTR); - if (err < 0 && errno != EAGAIN) { - return GRPC_OS_ERROR(errno, "eventfd_read"); - } - return GRPC_ERROR_NONE; -} - -static grpc_error* eventfd_wakeup(grpc_wakeup_fd* fd_info) { - int err; - GPR_TIMER_BEGIN("eventfd_wakeup", 0); - do { - err = eventfd_write(fd_info->read_fd, 1); - } while (err < 0 && errno == EINTR); - if (err < 0) { - return GRPC_OS_ERROR(errno, "eventfd_write"); - } - GPR_TIMER_END("eventfd_wakeup", 0); - return GRPC_ERROR_NONE; -} - -static void eventfd_destroy(grpc_wakeup_fd* fd_info) { - if (fd_info->read_fd != 0) close(fd_info->read_fd); -} - -static int eventfd_check_availability(void) { - const int efd = eventfd(0, 0); - const int is_available = efd >= 0; - if (is_available) close(efd); - return is_available; -} - -const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { - eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, - eventfd_check_availability}; - -#endif /* GRPC_LINUX_EVENTFD */ diff --git a/src/core/lib/iomgr/wakeup_fd_eventfd.cc b/src/core/lib/iomgr/wakeup_fd_eventfd.cc new file mode 100644 index 0000000000..81cb7ee280 --- /dev/null +++ b/src/core/lib/iomgr/wakeup_fd_eventfd.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_LINUX_EVENTFD + +#include +#include +#include + +#include + +#include "src/core/lib/iomgr/wakeup_fd_posix.h" +#include "src/core/lib/profiling/timers.h" + +static grpc_error* eventfd_create(grpc_wakeup_fd* fd_info) { + int efd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (efd < 0) { + return GRPC_OS_ERROR(errno, "eventfd"); + } + fd_info->read_fd = efd; + fd_info->write_fd = -1; + return GRPC_ERROR_NONE; +} + +static grpc_error* eventfd_consume(grpc_wakeup_fd* fd_info) { + eventfd_t value; + int err; + do { + err = eventfd_read(fd_info->read_fd, &value); + } while (err < 0 && errno == EINTR); + if (err < 0 && errno != EAGAIN) { + return GRPC_OS_ERROR(errno, "eventfd_read"); + } + return GRPC_ERROR_NONE; +} + +static grpc_error* eventfd_wakeup(grpc_wakeup_fd* fd_info) { + int err; + GPR_TIMER_BEGIN("eventfd_wakeup", 0); + do { + err = eventfd_write(fd_info->read_fd, 1); + } while (err < 0 && errno == EINTR); + if (err < 0) { + return GRPC_OS_ERROR(errno, "eventfd_write"); + } + GPR_TIMER_END("eventfd_wakeup", 0); + return GRPC_ERROR_NONE; +} + +static void eventfd_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd != 0) close(fd_info->read_fd); +} + +static int eventfd_check_availability(void) { + const int efd = eventfd(0, 0); + const int is_available = efd >= 0; + if (is_available) close(efd); + return is_available; +} + +const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { + eventfd_create, eventfd_consume, eventfd_wakeup, eventfd_destroy, + eventfd_check_availability}; + +#endif /* GRPC_LINUX_EVENTFD */ diff --git a/src/core/lib/iomgr/wakeup_fd_nospecial.c b/src/core/lib/iomgr/wakeup_fd_nospecial.c deleted file mode 100644 index 4c20b8c1b7..0000000000 --- a/src/core/lib/iomgr/wakeup_fd_nospecial.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* - * This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on - * systems without anything better than pipe. - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD - -#include -#include "src/core/lib/iomgr/wakeup_fd_posix.h" - -static int check_availability_invalid(void) { return 0; } - -const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { - NULL, NULL, NULL, NULL, check_availability_invalid}; - -#endif /* GRPC_POSIX_NO_SPECIAL_WAKEUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_nospecial.cc b/src/core/lib/iomgr/wakeup_fd_nospecial.cc new file mode 100644 index 0000000000..4c20b8c1b7 --- /dev/null +++ b/src/core/lib/iomgr/wakeup_fd_nospecial.cc @@ -0,0 +1,36 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This is a dummy file to provide an invalid specialized_wakeup_fd_vtable on + * systems without anything better than pipe. + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_NO_SPECIAL_WAKEUP_FD + +#include +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +static int check_availability_invalid(void) { return 0; } + +const grpc_wakeup_fd_vtable grpc_specialized_wakeup_fd_vtable = { + NULL, NULL, NULL, NULL, check_availability_invalid}; + +#endif /* GRPC_POSIX_NO_SPECIAL_WAKEUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.c b/src/core/lib/iomgr/wakeup_fd_pipe.c deleted file mode 100644 index 05d69dc9cc..0000000000 --- a/src/core/lib/iomgr/wakeup_fd_pipe.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_WAKEUP_FD - -#include "src/core/lib/iomgr/wakeup_fd_pipe.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" - -#include -#include -#include - -#include - -#include "src/core/lib/iomgr/socket_utils_posix.h" - -static grpc_error* pipe_init(grpc_wakeup_fd* fd_info) { - int pipefd[2]; - int r = pipe(pipefd); - if (0 != r) { - gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno)); - return GRPC_OS_ERROR(errno, "pipe"); - } - grpc_error* err; - err = grpc_set_socket_nonblocking(pipefd[0], 1); - if (err != GRPC_ERROR_NONE) return err; - err = grpc_set_socket_nonblocking(pipefd[1], 1); - if (err != GRPC_ERROR_NONE) return err; - fd_info->read_fd = pipefd[0]; - fd_info->write_fd = pipefd[1]; - return GRPC_ERROR_NONE; -} - -static grpc_error* pipe_consume(grpc_wakeup_fd* fd_info) { - char buf[128]; - ssize_t r; - - for (;;) { - r = read(fd_info->read_fd, buf, sizeof(buf)); - if (r > 0) continue; - if (r == 0) return GRPC_ERROR_NONE; - switch (errno) { - case EAGAIN: - return GRPC_ERROR_NONE; - case EINTR: - continue; - default: - return GRPC_OS_ERROR(errno, "read"); - } - } -} - -static grpc_error* pipe_wakeup(grpc_wakeup_fd* fd_info) { - char c = 0; - while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR) - ; - return GRPC_ERROR_NONE; -} - -static void pipe_destroy(grpc_wakeup_fd* fd_info) { - if (fd_info->read_fd != 0) close(fd_info->read_fd); - if (fd_info->write_fd != 0) close(fd_info->write_fd); -} - -static int pipe_check_availability(void) { - grpc_wakeup_fd fd; - fd.read_fd = fd.write_fd = -1; - - if (pipe_init(&fd) == GRPC_ERROR_NONE) { - pipe_destroy(&fd); - return 1; - } else { - return 0; - } -} - -const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = { - pipe_init, pipe_consume, pipe_wakeup, pipe_destroy, - pipe_check_availability}; - -#endif /* GPR_POSIX_WAKUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_pipe.cc b/src/core/lib/iomgr/wakeup_fd_pipe.cc new file mode 100644 index 0000000000..05d69dc9cc --- /dev/null +++ b/src/core/lib/iomgr/wakeup_fd_pipe.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_WAKEUP_FD + +#include "src/core/lib/iomgr/wakeup_fd_pipe.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +#include +#include +#include + +#include + +#include "src/core/lib/iomgr/socket_utils_posix.h" + +static grpc_error* pipe_init(grpc_wakeup_fd* fd_info) { + int pipefd[2]; + int r = pipe(pipefd); + if (0 != r) { + gpr_log(GPR_ERROR, "pipe creation failed (%d): %s", errno, strerror(errno)); + return GRPC_OS_ERROR(errno, "pipe"); + } + grpc_error* err; + err = grpc_set_socket_nonblocking(pipefd[0], 1); + if (err != GRPC_ERROR_NONE) return err; + err = grpc_set_socket_nonblocking(pipefd[1], 1); + if (err != GRPC_ERROR_NONE) return err; + fd_info->read_fd = pipefd[0]; + fd_info->write_fd = pipefd[1]; + return GRPC_ERROR_NONE; +} + +static grpc_error* pipe_consume(grpc_wakeup_fd* fd_info) { + char buf[128]; + ssize_t r; + + for (;;) { + r = read(fd_info->read_fd, buf, sizeof(buf)); + if (r > 0) continue; + if (r == 0) return GRPC_ERROR_NONE; + switch (errno) { + case EAGAIN: + return GRPC_ERROR_NONE; + case EINTR: + continue; + default: + return GRPC_OS_ERROR(errno, "read"); + } + } +} + +static grpc_error* pipe_wakeup(grpc_wakeup_fd* fd_info) { + char c = 0; + while (write(fd_info->write_fd, &c, 1) != 1 && errno == EINTR) + ; + return GRPC_ERROR_NONE; +} + +static void pipe_destroy(grpc_wakeup_fd* fd_info) { + if (fd_info->read_fd != 0) close(fd_info->read_fd); + if (fd_info->write_fd != 0) close(fd_info->write_fd); +} + +static int pipe_check_availability(void) { + grpc_wakeup_fd fd; + fd.read_fd = fd.write_fd = -1; + + if (pipe_init(&fd) == GRPC_ERROR_NONE) { + pipe_destroy(&fd); + return 1; + } else { + return 0; + } +} + +const grpc_wakeup_fd_vtable grpc_pipe_wakeup_fd_vtable = { + pipe_init, pipe_consume, pipe_wakeup, pipe_destroy, + pipe_check_availability}; + +#endif /* GPR_POSIX_WAKUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_posix.c b/src/core/lib/iomgr/wakeup_fd_posix.c deleted file mode 100644 index 9af96d314b..0000000000 --- a/src/core/lib/iomgr/wakeup_fd_posix.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/iomgr/port.h" - -#ifdef GRPC_POSIX_WAKEUP_FD - -#include -#include "src/core/lib/iomgr/wakeup_fd_cv.h" -#include "src/core/lib/iomgr/wakeup_fd_pipe.h" -#include "src/core/lib/iomgr/wakeup_fd_posix.h" - -static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL; - -int grpc_allow_specialized_wakeup_fd = 1; -int grpc_allow_pipe_wakeup_fd = 1; - -int has_real_wakeup_fd = 1; -int cv_wakeup_fds_enabled = 0; - -void grpc_wakeup_fd_global_init(void) { - if (grpc_allow_specialized_wakeup_fd && - grpc_specialized_wakeup_fd_vtable.check_availability()) { - wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable; - } else if (grpc_allow_pipe_wakeup_fd && - grpc_pipe_wakeup_fd_vtable.check_availability()) { - wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; - } else { - has_real_wakeup_fd = 0; - } -} - -void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = NULL; } - -int grpc_has_wakeup_fd(void) { return has_real_wakeup_fd; } - -int grpc_cv_wakeup_fds_enabled(void) { return cv_wakeup_fds_enabled; } - -void grpc_enable_cv_wakeup_fds(int enable) { cv_wakeup_fds_enabled = enable; } - -grpc_error *grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) { - if (cv_wakeup_fds_enabled) { - return grpc_cv_wakeup_fd_vtable.init(fd_info); - } - return wakeup_fd_vtable->init(fd_info); -} - -grpc_error *grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) { - if (cv_wakeup_fds_enabled) { - return grpc_cv_wakeup_fd_vtable.consume(fd_info); - } - return wakeup_fd_vtable->consume(fd_info); -} - -grpc_error *grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) { - if (cv_wakeup_fds_enabled) { - return grpc_cv_wakeup_fd_vtable.wakeup(fd_info); - } - return wakeup_fd_vtable->wakeup(fd_info); -} - -void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info) { - if (cv_wakeup_fds_enabled) { - grpc_cv_wakeup_fd_vtable.destroy(fd_info); - } else { - wakeup_fd_vtable->destroy(fd_info); - } -} - -#endif /* GRPC_POSIX_WAKEUP_FD */ diff --git a/src/core/lib/iomgr/wakeup_fd_posix.cc b/src/core/lib/iomgr/wakeup_fd_posix.cc new file mode 100644 index 0000000000..9af96d314b --- /dev/null +++ b/src/core/lib/iomgr/wakeup_fd_posix.cc @@ -0,0 +1,85 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/iomgr/port.h" + +#ifdef GRPC_POSIX_WAKEUP_FD + +#include +#include "src/core/lib/iomgr/wakeup_fd_cv.h" +#include "src/core/lib/iomgr/wakeup_fd_pipe.h" +#include "src/core/lib/iomgr/wakeup_fd_posix.h" + +static const grpc_wakeup_fd_vtable *wakeup_fd_vtable = NULL; + +int grpc_allow_specialized_wakeup_fd = 1; +int grpc_allow_pipe_wakeup_fd = 1; + +int has_real_wakeup_fd = 1; +int cv_wakeup_fds_enabled = 0; + +void grpc_wakeup_fd_global_init(void) { + if (grpc_allow_specialized_wakeup_fd && + grpc_specialized_wakeup_fd_vtable.check_availability()) { + wakeup_fd_vtable = &grpc_specialized_wakeup_fd_vtable; + } else if (grpc_allow_pipe_wakeup_fd && + grpc_pipe_wakeup_fd_vtable.check_availability()) { + wakeup_fd_vtable = &grpc_pipe_wakeup_fd_vtable; + } else { + has_real_wakeup_fd = 0; + } +} + +void grpc_wakeup_fd_global_destroy(void) { wakeup_fd_vtable = NULL; } + +int grpc_has_wakeup_fd(void) { return has_real_wakeup_fd; } + +int grpc_cv_wakeup_fds_enabled(void) { return cv_wakeup_fds_enabled; } + +void grpc_enable_cv_wakeup_fds(int enable) { cv_wakeup_fds_enabled = enable; } + +grpc_error *grpc_wakeup_fd_init(grpc_wakeup_fd *fd_info) { + if (cv_wakeup_fds_enabled) { + return grpc_cv_wakeup_fd_vtable.init(fd_info); + } + return wakeup_fd_vtable->init(fd_info); +} + +grpc_error *grpc_wakeup_fd_consume_wakeup(grpc_wakeup_fd *fd_info) { + if (cv_wakeup_fds_enabled) { + return grpc_cv_wakeup_fd_vtable.consume(fd_info); + } + return wakeup_fd_vtable->consume(fd_info); +} + +grpc_error *grpc_wakeup_fd_wakeup(grpc_wakeup_fd *fd_info) { + if (cv_wakeup_fds_enabled) { + return grpc_cv_wakeup_fd_vtable.wakeup(fd_info); + } + return wakeup_fd_vtable->wakeup(fd_info); +} + +void grpc_wakeup_fd_destroy(grpc_wakeup_fd *fd_info) { + if (cv_wakeup_fds_enabled) { + grpc_cv_wakeup_fd_vtable.destroy(fd_info); + } else { + wakeup_fd_vtable->destroy(fd_info); + } +} + +#endif /* GRPC_POSIX_WAKEUP_FD */ diff --git a/src/core/lib/json/json.c b/src/core/lib/json/json.c deleted file mode 100644 index 4ad51f662a..0000000000 --- a/src/core/lib/json/json.c +++ /dev/null @@ -1,48 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include "src/core/lib/json/json.h" - -grpc_json* grpc_json_create(grpc_json_type type) { - grpc_json* json = (grpc_json*)gpr_zalloc(sizeof(*json)); - json->type = type; - - return json; -} - -void grpc_json_destroy(grpc_json* json) { - while (json->child) { - grpc_json_destroy(json->child); - } - - if (json->next) { - json->next->prev = json->prev; - } - - if (json->prev) { - json->prev->next = json->next; - } else if (json->parent) { - json->parent->child = json->next; - } - - gpr_free(json); -} diff --git a/src/core/lib/json/json.cc b/src/core/lib/json/json.cc new file mode 100644 index 0000000000..4ad51f662a --- /dev/null +++ b/src/core/lib/json/json.cc @@ -0,0 +1,48 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/json/json.h" + +grpc_json* grpc_json_create(grpc_json_type type) { + grpc_json* json = (grpc_json*)gpr_zalloc(sizeof(*json)); + json->type = type; + + return json; +} + +void grpc_json_destroy(grpc_json* json) { + while (json->child) { + grpc_json_destroy(json->child); + } + + if (json->next) { + json->next->prev = json->prev; + } + + if (json->prev) { + json->prev->next = json->next; + } else if (json->parent) { + json->parent->child = json->next; + } + + gpr_free(json); +} diff --git a/src/core/lib/json/json_reader.c b/src/core/lib/json/json_reader.c deleted file mode 100644 index 094a35176c..0000000000 --- a/src/core/lib/json/json_reader.c +++ /dev/null @@ -1,661 +0,0 @@ -/* - * - * Copyright 2015-2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include - -#include "src/core/lib/json/json_reader.h" - -static void json_reader_string_clear(grpc_json_reader *reader) { - reader->vtable->string_clear(reader->userdata); -} - -static void json_reader_string_add_char(grpc_json_reader *reader, uint32_t c) { - reader->vtable->string_add_char(reader->userdata, c); -} - -static void json_reader_string_add_utf32(grpc_json_reader *reader, - uint32_t utf32) { - reader->vtable->string_add_utf32(reader->userdata, utf32); -} - -static uint32_t grpc_json_reader_read_char(grpc_json_reader *reader) { - return reader->vtable->read_char(reader->userdata); -} - -static void json_reader_container_begins(grpc_json_reader *reader, - grpc_json_type type) { - reader->vtable->container_begins(reader->userdata, type); -} - -static grpc_json_type grpc_json_reader_container_ends( - grpc_json_reader *reader) { - return reader->vtable->container_ends(reader->userdata); -} - -static void json_reader_set_key(grpc_json_reader *reader) { - reader->vtable->set_key(reader->userdata); -} - -static void json_reader_set_string(grpc_json_reader *reader) { - reader->vtable->set_string(reader->userdata); -} - -static int json_reader_set_number(grpc_json_reader *reader) { - return reader->vtable->set_number(reader->userdata); -} - -static void json_reader_set_true(grpc_json_reader *reader) { - reader->vtable->set_true(reader->userdata); -} - -static void json_reader_set_false(grpc_json_reader *reader) { - reader->vtable->set_false(reader->userdata); -} - -static void json_reader_set_null(grpc_json_reader *reader) { - reader->vtable->set_null(reader->userdata); -} - -/* Call this function to initialize the reader structure. */ -void grpc_json_reader_init(grpc_json_reader *reader, - grpc_json_reader_vtable *vtable, void *userdata) { - memset(reader, 0, sizeof(*reader)); - reader->vtable = vtable; - reader->userdata = userdata; - json_reader_string_clear(reader); - reader->state = GRPC_JSON_STATE_VALUE_BEGIN; -} - -int grpc_json_reader_is_complete(grpc_json_reader *reader) { - return ((reader->depth == 0) && - ((reader->state == GRPC_JSON_STATE_END) || - (reader->state == GRPC_JSON_STATE_VALUE_END))); -} - -grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { - uint32_t c, success; - - /* This state-machine is a strict implementation of ECMA-404 */ - for (;;) { - c = grpc_json_reader_read_char(reader); - switch (c) { - /* Let's process the error cases first. */ - case GRPC_JSON_READ_CHAR_ERROR: - return GRPC_JSON_READ_ERROR; - - case GRPC_JSON_READ_CHAR_EAGAIN: - return GRPC_JSON_EAGAIN; - - case GRPC_JSON_READ_CHAR_EOF: - if (grpc_json_reader_is_complete(reader)) { - return GRPC_JSON_DONE; - } else { - return GRPC_JSON_PARSE_ERROR; - } - break; - - /* Processing whitespaces. */ - case ' ': - case '\t': - case '\n': - case '\r': - switch (reader->state) { - case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: - case GRPC_JSON_STATE_OBJECT_KEY_END: - case GRPC_JSON_STATE_VALUE_BEGIN: - case GRPC_JSON_STATE_VALUE_END: - case GRPC_JSON_STATE_END: - break; - - case GRPC_JSON_STATE_OBJECT_KEY_STRING: - case GRPC_JSON_STATE_VALUE_STRING: - if (c != ' ') return GRPC_JSON_PARSE_ERROR; - if (reader->unicode_high_surrogate != 0) - return GRPC_JSON_PARSE_ERROR; - json_reader_string_add_char(reader, c); - break; - - case GRPC_JSON_STATE_VALUE_NUMBER: - case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: - case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: - case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - success = (uint32_t)json_reader_set_number(reader); - if (!success) return GRPC_JSON_PARSE_ERROR; - json_reader_string_clear(reader); - reader->state = GRPC_JSON_STATE_VALUE_END; - break; - - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - /* Value, object or array terminations. */ - case ',': - case '}': - case ']': - switch (reader->state) { - case GRPC_JSON_STATE_OBJECT_KEY_STRING: - case GRPC_JSON_STATE_VALUE_STRING: - if (reader->unicode_high_surrogate != 0) { - return GRPC_JSON_PARSE_ERROR; - } - json_reader_string_add_char(reader, c); - break; - - case GRPC_JSON_STATE_VALUE_NUMBER: - case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: - case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: - case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - if (reader->depth == 0) { - return GRPC_JSON_PARSE_ERROR; - } else if ((c == '}') && !reader->in_object) { - return GRPC_JSON_PARSE_ERROR; - } else if ((c == ']') && !reader->in_array) { - return GRPC_JSON_PARSE_ERROR; - } - success = (uint32_t)json_reader_set_number(reader); - if (!success) return GRPC_JSON_PARSE_ERROR; - json_reader_string_clear(reader); - reader->state = GRPC_JSON_STATE_VALUE_END; - /* The missing break here is intentional. */ - /* fallthrough */ - - case GRPC_JSON_STATE_VALUE_END: - case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: - case GRPC_JSON_STATE_VALUE_BEGIN: - if (c == ',') { - if (reader->state != GRPC_JSON_STATE_VALUE_END) { - return GRPC_JSON_PARSE_ERROR; - } - if (reader->in_object) { - reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; - } else if (reader->in_array) { - reader->state = GRPC_JSON_STATE_VALUE_BEGIN; - } else { - return GRPC_JSON_PARSE_ERROR; - } - } else { - if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; - if ((c == '}') && !reader->in_object) { - return GRPC_JSON_PARSE_ERROR; - } - if ((c == '}') && - (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) && - !reader->container_just_begun) { - return GRPC_JSON_PARSE_ERROR; - } - if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR; - if ((c == ']') && - (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) && - !reader->container_just_begun) { - return GRPC_JSON_PARSE_ERROR; - } - reader->state = GRPC_JSON_STATE_VALUE_END; - switch (grpc_json_reader_container_ends(reader)) { - case GRPC_JSON_OBJECT: - reader->in_object = 1; - reader->in_array = 0; - break; - case GRPC_JSON_ARRAY: - reader->in_object = 0; - reader->in_array = 1; - break; - case GRPC_JSON_TOP_LEVEL: - GPR_ASSERT(reader->depth == 0); - reader->in_object = 0; - reader->in_array = 0; - reader->state = GRPC_JSON_STATE_END; - break; - default: - GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); - } - } - break; - - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - /* In-string escaping. */ - case '\\': - switch (reader->state) { - case GRPC_JSON_STATE_OBJECT_KEY_STRING: - reader->escaped_string_was_key = 1; - reader->state = GRPC_JSON_STATE_STRING_ESCAPE; - break; - - case GRPC_JSON_STATE_VALUE_STRING: - reader->escaped_string_was_key = 0; - reader->state = GRPC_JSON_STATE_STRING_ESCAPE; - break; - - /* This is the \\ case. */ - case GRPC_JSON_STATE_STRING_ESCAPE: - if (reader->unicode_high_surrogate != 0) - return GRPC_JSON_PARSE_ERROR; - json_reader_string_add_char(reader, '\\'); - if (reader->escaped_string_was_key) { - reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; - } else { - reader->state = GRPC_JSON_STATE_VALUE_STRING; - } - break; - - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - default: - reader->container_just_begun = 0; - switch (reader->state) { - case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: - if (c != '"') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; - break; - - case GRPC_JSON_STATE_OBJECT_KEY_STRING: - if (reader->unicode_high_surrogate != 0) { - return GRPC_JSON_PARSE_ERROR; - } - if (c == '"') { - reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; - json_reader_set_key(reader); - json_reader_string_clear(reader); - } else { - if (c < 32) return GRPC_JSON_PARSE_ERROR; - json_reader_string_add_char(reader, c); - } - break; - - case GRPC_JSON_STATE_VALUE_STRING: - if (reader->unicode_high_surrogate != 0) { - return GRPC_JSON_PARSE_ERROR; - } - if (c == '"') { - reader->state = GRPC_JSON_STATE_VALUE_END; - json_reader_set_string(reader); - json_reader_string_clear(reader); - } else { - if (c < 32) return GRPC_JSON_PARSE_ERROR; - json_reader_string_add_char(reader, c); - } - break; - - case GRPC_JSON_STATE_OBJECT_KEY_END: - if (c != ':') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_BEGIN; - break; - - case GRPC_JSON_STATE_VALUE_BEGIN: - switch (c) { - case 't': - reader->state = GRPC_JSON_STATE_VALUE_TRUE_R; - break; - - case 'f': - reader->state = GRPC_JSON_STATE_VALUE_FALSE_A; - break; - - case 'n': - reader->state = GRPC_JSON_STATE_VALUE_NULL_U; - break; - - case '"': - reader->state = GRPC_JSON_STATE_VALUE_STRING; - break; - - case '0': - json_reader_string_add_char(reader, c); - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO; - break; - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - json_reader_string_add_char(reader, c); - reader->state = GRPC_JSON_STATE_VALUE_NUMBER; - break; - - case '{': - reader->container_just_begun = 1; - json_reader_container_begins(reader, GRPC_JSON_OBJECT); - reader->depth++; - reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; - reader->in_object = 1; - reader->in_array = 0; - break; - - case '[': - reader->container_just_begun = 1; - json_reader_container_begins(reader, GRPC_JSON_ARRAY); - reader->depth++; - reader->in_object = 0; - reader->in_array = 1; - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_STRING_ESCAPE: - if (reader->escaped_string_was_key) { - reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; - } else { - reader->state = GRPC_JSON_STATE_VALUE_STRING; - } - if (reader->unicode_high_surrogate && c != 'u') { - return GRPC_JSON_PARSE_ERROR; - } - switch (c) { - case '"': - case '/': - json_reader_string_add_char(reader, c); - break; - case 'b': - json_reader_string_add_char(reader, '\b'); - break; - case 'f': - json_reader_string_add_char(reader, '\f'); - break; - case 'n': - json_reader_string_add_char(reader, '\n'); - break; - case 'r': - json_reader_string_add_char(reader, '\r'); - break; - case 't': - json_reader_string_add_char(reader, '\t'); - break; - case 'u': - reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; - reader->unicode_char = 0; - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_STRING_ESCAPE_U1: - case GRPC_JSON_STATE_STRING_ESCAPE_U2: - case GRPC_JSON_STATE_STRING_ESCAPE_U3: - case GRPC_JSON_STATE_STRING_ESCAPE_U4: - if ((c >= '0') && (c <= '9')) { - c -= '0'; - } else if ((c >= 'A') && (c <= 'F')) { - c -= 'A' - 10; - } else if ((c >= 'a') && (c <= 'f')) { - c -= 'a' - 10; - } else { - return GRPC_JSON_PARSE_ERROR; - } - reader->unicode_char = (uint16_t)(reader->unicode_char << 4); - reader->unicode_char = (uint16_t)(reader->unicode_char | c); - - switch (reader->state) { - case GRPC_JSON_STATE_STRING_ESCAPE_U1: - reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2; - break; - case GRPC_JSON_STATE_STRING_ESCAPE_U2: - reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3; - break; - case GRPC_JSON_STATE_STRING_ESCAPE_U3: - reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; - break; - case GRPC_JSON_STATE_STRING_ESCAPE_U4: - /* See grpc_json_writer_escape_string to have a description - * of what's going on here. - */ - if ((reader->unicode_char & 0xfc00) == 0xd800) { - /* high surrogate utf-16 */ - if (reader->unicode_high_surrogate != 0) - return GRPC_JSON_PARSE_ERROR; - reader->unicode_high_surrogate = reader->unicode_char; - } else if ((reader->unicode_char & 0xfc00) == 0xdc00) { - /* low surrogate utf-16 */ - uint32_t utf32; - if (reader->unicode_high_surrogate == 0) - return GRPC_JSON_PARSE_ERROR; - utf32 = 0x10000; - utf32 += (uint32_t)( - (reader->unicode_high_surrogate - 0xd800) * 0x400); - utf32 += (uint32_t)(reader->unicode_char - 0xdc00); - json_reader_string_add_utf32(reader, utf32); - reader->unicode_high_surrogate = 0; - } else { - /* anything else */ - if (reader->unicode_high_surrogate != 0) - return GRPC_JSON_PARSE_ERROR; - json_reader_string_add_utf32(reader, reader->unicode_char); - } - if (reader->escaped_string_was_key) { - reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; - } else { - reader->state = GRPC_JSON_STATE_VALUE_STRING; - } - break; - default: - GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); - } - break; - - case GRPC_JSON_STATE_VALUE_NUMBER: - json_reader_string_add_char(reader, c); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - case 'e': - case 'E': - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; - break; - case '.': - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: - json_reader_string_add_char(reader, c); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - case 'e': - case 'E': - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: - if (c != '.') return GRPC_JSON_PARSE_ERROR; - json_reader_string_add_char(reader, c); - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; - break; - - case GRPC_JSON_STATE_VALUE_NUMBER_DOT: - json_reader_string_add_char(reader, c); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_VALUE_NUMBER_E: - json_reader_string_add_char(reader, c); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM; - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - json_reader_string_add_char(reader, c); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - break; - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_VALUE_TRUE_R: - if (c != 'r') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_TRUE_U; - break; - - case GRPC_JSON_STATE_VALUE_TRUE_U: - if (c != 'u') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_TRUE_E; - break; - - case GRPC_JSON_STATE_VALUE_TRUE_E: - if (c != 'e') return GRPC_JSON_PARSE_ERROR; - json_reader_set_true(reader); - reader->state = GRPC_JSON_STATE_VALUE_END; - break; - - case GRPC_JSON_STATE_VALUE_FALSE_A: - if (c != 'a') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_FALSE_L; - break; - - case GRPC_JSON_STATE_VALUE_FALSE_L: - if (c != 'l') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_FALSE_S; - break; - - case GRPC_JSON_STATE_VALUE_FALSE_S: - if (c != 's') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_FALSE_E; - break; - - case GRPC_JSON_STATE_VALUE_FALSE_E: - if (c != 'e') return GRPC_JSON_PARSE_ERROR; - json_reader_set_false(reader); - reader->state = GRPC_JSON_STATE_VALUE_END; - break; - - case GRPC_JSON_STATE_VALUE_NULL_U: - if (c != 'u') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_NULL_L1; - break; - - case GRPC_JSON_STATE_VALUE_NULL_L1: - if (c != 'l') return GRPC_JSON_PARSE_ERROR; - reader->state = GRPC_JSON_STATE_VALUE_NULL_L2; - break; - - case GRPC_JSON_STATE_VALUE_NULL_L2: - if (c != 'l') return GRPC_JSON_PARSE_ERROR; - json_reader_set_null(reader); - reader->state = GRPC_JSON_STATE_VALUE_END; - break; - - /* All of the VALUE_END cases are handled in the specialized case - * above. */ - case GRPC_JSON_STATE_VALUE_END: - switch (c) { - case ',': - case '}': - case ']': - GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); - break; - - default: - return GRPC_JSON_PARSE_ERROR; - } - break; - - case GRPC_JSON_STATE_END: - return GRPC_JSON_PARSE_ERROR; - } - } - } - - GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); -} diff --git a/src/core/lib/json/json_reader.cc b/src/core/lib/json/json_reader.cc new file mode 100644 index 0000000000..094a35176c --- /dev/null +++ b/src/core/lib/json/json_reader.cc @@ -0,0 +1,661 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include + +#include "src/core/lib/json/json_reader.h" + +static void json_reader_string_clear(grpc_json_reader *reader) { + reader->vtable->string_clear(reader->userdata); +} + +static void json_reader_string_add_char(grpc_json_reader *reader, uint32_t c) { + reader->vtable->string_add_char(reader->userdata, c); +} + +static void json_reader_string_add_utf32(grpc_json_reader *reader, + uint32_t utf32) { + reader->vtable->string_add_utf32(reader->userdata, utf32); +} + +static uint32_t grpc_json_reader_read_char(grpc_json_reader *reader) { + return reader->vtable->read_char(reader->userdata); +} + +static void json_reader_container_begins(grpc_json_reader *reader, + grpc_json_type type) { + reader->vtable->container_begins(reader->userdata, type); +} + +static grpc_json_type grpc_json_reader_container_ends( + grpc_json_reader *reader) { + return reader->vtable->container_ends(reader->userdata); +} + +static void json_reader_set_key(grpc_json_reader *reader) { + reader->vtable->set_key(reader->userdata); +} + +static void json_reader_set_string(grpc_json_reader *reader) { + reader->vtable->set_string(reader->userdata); +} + +static int json_reader_set_number(grpc_json_reader *reader) { + return reader->vtable->set_number(reader->userdata); +} + +static void json_reader_set_true(grpc_json_reader *reader) { + reader->vtable->set_true(reader->userdata); +} + +static void json_reader_set_false(grpc_json_reader *reader) { + reader->vtable->set_false(reader->userdata); +} + +static void json_reader_set_null(grpc_json_reader *reader) { + reader->vtable->set_null(reader->userdata); +} + +/* Call this function to initialize the reader structure. */ +void grpc_json_reader_init(grpc_json_reader *reader, + grpc_json_reader_vtable *vtable, void *userdata) { + memset(reader, 0, sizeof(*reader)); + reader->vtable = vtable; + reader->userdata = userdata; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; +} + +int grpc_json_reader_is_complete(grpc_json_reader *reader) { + return ((reader->depth == 0) && + ((reader->state == GRPC_JSON_STATE_END) || + (reader->state == GRPC_JSON_STATE_VALUE_END))); +} + +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { + uint32_t c, success; + + /* This state-machine is a strict implementation of ECMA-404 */ + for (;;) { + c = grpc_json_reader_read_char(reader); + switch (c) { + /* Let's process the error cases first. */ + case GRPC_JSON_READ_CHAR_ERROR: + return GRPC_JSON_READ_ERROR; + + case GRPC_JSON_READ_CHAR_EAGAIN: + return GRPC_JSON_EAGAIN; + + case GRPC_JSON_READ_CHAR_EOF: + if (grpc_json_reader_is_complete(reader)) { + return GRPC_JSON_DONE; + } else { + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Processing whitespaces. */ + case ' ': + case '\t': + case '\n': + case '\r': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_OBJECT_KEY_END: + case GRPC_JSON_STATE_VALUE_BEGIN: + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_END: + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + if (c != ' ') return GRPC_JSON_PARSE_ERROR; + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = (uint32_t)json_reader_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Value, object or array terminations. */ + case ',': + case '}': + case ']': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate != 0) { + return GRPC_JSON_PARSE_ERROR; + } + json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + if (reader->depth == 0) { + return GRPC_JSON_PARSE_ERROR; + } else if ((c == '}') && !reader->in_object) { + return GRPC_JSON_PARSE_ERROR; + } else if ((c == ']') && !reader->in_array) { + return GRPC_JSON_PARSE_ERROR; + } + success = (uint32_t)json_reader_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + /* The missing break here is intentional. */ + /* fallthrough */ + + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_VALUE_BEGIN: + if (c == ',') { + if (reader->state != GRPC_JSON_STATE_VALUE_END) { + return GRPC_JSON_PARSE_ERROR; + } + if (reader->in_object) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + } else if (reader->in_array) { + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + } else { + return GRPC_JSON_PARSE_ERROR; + } + } else { + if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; + if ((c == '}') && !reader->in_object) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == '}') && + (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR; + if ((c == ']') && + (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + reader->state = GRPC_JSON_STATE_VALUE_END; + switch (grpc_json_reader_container_ends(reader)) { + case GRPC_JSON_OBJECT: + reader->in_object = 1; + reader->in_array = 0; + break; + case GRPC_JSON_ARRAY: + reader->in_object = 0; + reader->in_array = 1; + break; + case GRPC_JSON_TOP_LEVEL: + GPR_ASSERT(reader->depth == 0); + reader->in_object = 0; + reader->in_array = 0; + reader->state = GRPC_JSON_STATE_END; + break; + default: + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); + } + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* In-string escaping. */ + case '\\': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + reader->escaped_string_was_key = 1; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + case GRPC_JSON_STATE_VALUE_STRING: + reader->escaped_string_was_key = 0; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + /* This is the \\ case. */ + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, '\\'); + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + default: + reader->container_just_begun = 0; + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + if (c != '"') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + if (reader->unicode_high_surrogate != 0) { + return GRPC_JSON_PARSE_ERROR; + } + if (c == '"') { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; + json_reader_set_key(reader); + json_reader_string_clear(reader); + } else { + if (c < 32) return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate != 0) { + return GRPC_JSON_PARSE_ERROR; + } + if (c == '"') { + reader->state = GRPC_JSON_STATE_VALUE_END; + json_reader_set_string(reader); + json_reader_string_clear(reader); + } else { + if (c < 32) return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_OBJECT_KEY_END: + if (c != ':') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + break; + + case GRPC_JSON_STATE_VALUE_BEGIN: + switch (c) { + case 't': + reader->state = GRPC_JSON_STATE_VALUE_TRUE_R; + break; + + case 'f': + reader->state = GRPC_JSON_STATE_VALUE_FALSE_A; + break; + + case 'n': + reader->state = GRPC_JSON_STATE_VALUE_NULL_U; + break; + + case '"': + reader->state = GRPC_JSON_STATE_VALUE_STRING; + break; + + case '0': + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER; + break; + + case '{': + reader->container_just_begun = 1; + json_reader_container_begins(reader, GRPC_JSON_OBJECT); + reader->depth++; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + reader->in_object = 1; + reader->in_array = 0; + break; + + case '[': + reader->container_just_begun = 1; + json_reader_container_begins(reader, GRPC_JSON_ARRAY); + reader->depth++; + reader->in_object = 0; + reader->in_array = 1; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + if (reader->unicode_high_surrogate && c != 'u') { + return GRPC_JSON_PARSE_ERROR; + } + switch (c) { + case '"': + case '/': + json_reader_string_add_char(reader, c); + break; + case 'b': + json_reader_string_add_char(reader, '\b'); + break; + case 'f': + json_reader_string_add_char(reader, '\f'); + break; + case 'n': + json_reader_string_add_char(reader, '\n'); + break; + case 'r': + json_reader_string_add_char(reader, '\r'); + break; + case 't': + json_reader_string_add_char(reader, '\t'); + break; + case 'u': + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; + reader->unicode_char = 0; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + if ((c >= '0') && (c <= '9')) { + c -= '0'; + } else if ((c >= 'A') && (c <= 'F')) { + c -= 'A' - 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a' - 10; + } else { + return GRPC_JSON_PARSE_ERROR; + } + reader->unicode_char = (uint16_t)(reader->unicode_char << 4); + reader->unicode_char = (uint16_t)(reader->unicode_char | c); + + switch (reader->state) { + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + /* See grpc_json_writer_escape_string to have a description + * of what's going on here. + */ + if ((reader->unicode_char & 0xfc00) == 0xd800) { + /* high surrogate utf-16 */ + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + reader->unicode_high_surrogate = reader->unicode_char; + } else if ((reader->unicode_char & 0xfc00) == 0xdc00) { + /* low surrogate utf-16 */ + uint32_t utf32; + if (reader->unicode_high_surrogate == 0) + return GRPC_JSON_PARSE_ERROR; + utf32 = 0x10000; + utf32 += (uint32_t)( + (reader->unicode_high_surrogate - 0xd800) * 0x400); + utf32 += (uint32_t)(reader->unicode_char - 0xdc00); + json_reader_string_add_utf32(reader, utf32); + reader->unicode_high_surrogate = 0; + } else { + /* anything else */ + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_utf32(reader, reader->unicode_char); + } + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + default: + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + case '.': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + if (c != '.') return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_DOT: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_E: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_TRUE_R: + if (c != 'r') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_U; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_E; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + json_reader_set_true(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_A: + if (c != 'a') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_L; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_L: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_S; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_S: + if (c != 's') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_E; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + json_reader_set_false(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_NULL_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L1; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L1: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L2; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L2: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + json_reader_set_null(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + /* All of the VALUE_END cases are handled in the specialized case + * above. */ + case GRPC_JSON_STATE_VALUE_END: + switch (c) { + case ',': + case '}': + case ']': + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_END: + return GRPC_JSON_PARSE_ERROR; + } + } + } + + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); +} diff --git a/src/core/lib/json/json_string.c b/src/core/lib/json/json_string.c deleted file mode 100644 index 3178d2d2b4..0000000000 --- a/src/core/lib/json/json_string.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include -#include - -#include "src/core/lib/json/json.h" -#include "src/core/lib/json/json_reader.h" -#include "src/core/lib/json/json_writer.h" - -/* The json reader will construct a bunch of grpc_json objects and - * link them all up together in a tree-like structure that will represent - * the json data in memory. - * - * It also uses its own input as a scratchpad to store all of the decoded, - * unescaped strings. So we need to keep track of all these pointers in - * that opaque structure the reader will carry for us. - * - * Note that this works because the act of parsing json always reduces its - * input size, and never expands it. - */ -typedef struct { - grpc_json *top; - grpc_json *current_container; - grpc_json *current_value; - uint8_t *input; - uint8_t *key; - uint8_t *string; - uint8_t *string_ptr; - size_t remaining_input; -} json_reader_userdata; - -/* This json writer will put everything in a big string. - * The point is that we allocate that string in chunks of 256 bytes. - */ -typedef struct { - char *output; - size_t free_space; - size_t string_len; - size_t allocated; -} json_writer_userdata; - -/* This function checks if there's enough space left in the output buffer, - * and will enlarge it if necessary. We're only allocating chunks of 256 - * bytes at a time (or multiples thereof). - */ -static void json_writer_output_check(void *userdata, size_t needed) { - json_writer_userdata *state = (json_writer_userdata *)userdata; - if (state->free_space >= needed) return; - needed -= state->free_space; - /* Round up by 256 bytes. */ - needed = (needed + 0xff) & ~0xffU; - state->output = (char *)gpr_realloc(state->output, state->allocated + needed); - state->free_space += needed; - state->allocated += needed; -} - -/* These are needed by the writer's implementation. */ -static void json_writer_output_char(void *userdata, char c) { - json_writer_userdata *state = (json_writer_userdata *)userdata; - json_writer_output_check(userdata, 1); - state->output[state->string_len++] = c; - state->free_space--; -} - -static void json_writer_output_string_with_len(void *userdata, const char *str, - size_t len) { - json_writer_userdata *state = (json_writer_userdata *)userdata; - json_writer_output_check(userdata, len); - memcpy(state->output + state->string_len, str, len); - state->string_len += len; - state->free_space -= len; -} - -static void json_writer_output_string(void *userdata, const char *str) { - size_t len = strlen(str); - json_writer_output_string_with_len(userdata, str, len); -} - -/* The reader asks us to clear our scratchpad. In our case, we'll simply mark - * the end of the current string, and advance our output pointer. - */ -static void json_reader_string_clear(void *userdata) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - if (state->string) { - GPR_ASSERT(state->string_ptr < state->input); - *state->string_ptr++ = 0; - } - state->string = state->string_ptr; -} - -static void json_reader_string_add_char(void *userdata, uint32_t c) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - GPR_ASSERT(state->string_ptr < state->input); - GPR_ASSERT(c <= 0xff); - *state->string_ptr++ = (uint8_t)c; -} - -/* We are converting a UTF-32 character into UTF-8 here, - * as described by RFC3629. - */ -static void json_reader_string_add_utf32(void *userdata, uint32_t c) { - if (c <= 0x7f) { - json_reader_string_add_char(userdata, c); - } else if (c <= 0x7ff) { - uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f); - uint32_t b2 = 0x80 | (c & 0x3f); - json_reader_string_add_char(userdata, b1); - json_reader_string_add_char(userdata, b2); - } else if (c <= 0xffff) { - uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f); - uint32_t b2 = 0x80 | ((c >> 6) & 0x3f); - uint32_t b3 = 0x80 | (c & 0x3f); - json_reader_string_add_char(userdata, b1); - json_reader_string_add_char(userdata, b2); - json_reader_string_add_char(userdata, b3); - } else if (c <= 0x1fffff) { - uint32_t b1 = 0xf0 | ((c >> 18) & 0x07); - uint32_t b2 = 0x80 | ((c >> 12) & 0x3f); - uint32_t b3 = 0x80 | ((c >> 6) & 0x3f); - uint32_t b4 = 0x80 | (c & 0x3f); - json_reader_string_add_char(userdata, b1); - json_reader_string_add_char(userdata, b2); - json_reader_string_add_char(userdata, b3); - json_reader_string_add_char(userdata, b4); - } -} - -/* We consider that the input may be a zero-terminated string. So we - * can end up hitting eof before the end of the alleged string length. - */ -static uint32_t json_reader_read_char(void *userdata) { - uint32_t r; - json_reader_userdata *state = (json_reader_userdata *)userdata; - - if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; - - r = *state->input++; - state->remaining_input--; - - if (r == 0) { - state->remaining_input = 0; - return GRPC_JSON_READ_CHAR_EOF; - } - - return r; -} - -/* Helper function to create a new grpc_json object and link it into - * our tree-in-progress inside our opaque structure. - */ -static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - grpc_json *json = grpc_json_create(type); - - json->parent = state->current_container; - json->prev = state->current_value; - state->current_value = json; - - if (json->prev) { - json->prev->next = json; - } - if (json->parent) { - if (!json->parent->child) { - json->parent->child = json; - } - if (json->parent->type == GRPC_JSON_OBJECT) { - json->key = (char *)state->key; - } - } - if (!state->top) { - state->top = json; - } - - return json; -} - -static void json_reader_container_begins(void *userdata, grpc_json_type type) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - grpc_json *container; - - GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); - - container = json_create_and_link(userdata, type); - state->current_container = container; - state->current_value = NULL; -} - -/* It's important to remember that the reader is mostly stateless, so it - * isn't trying to remember what the container was prior the one that just - * ends. Since we're keeping track of these for our own purpose, we are - * able to return that information back, which is useful for it to validate - * the input json stream. - * - * Also note that if we're at the top of the tree, and the last container - * ends, we have to return GRPC_JSON_TOP_LEVEL. - */ -static grpc_json_type json_reader_container_ends(void *userdata) { - grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; - json_reader_userdata *state = (json_reader_userdata *)userdata; - - GPR_ASSERT(state->current_container); - - state->current_value = state->current_container; - state->current_container = state->current_container->parent; - - if (state->current_container) { - container_type = state->current_container->type; - } - - return container_type; -} - -/* The next 3 functions basically are the reader asking us to use our string - * scratchpad for one of these 3 purposes. - * - * Note that in the set_number case, we're not going to try interpreting it. - * We'll keep it as a string, and leave it to the caller to evaluate it. - */ -static void json_reader_set_key(void *userdata) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - state->key = state->string; -} - -static void json_reader_set_string(void *userdata) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - grpc_json *json = json_create_and_link(userdata, GRPC_JSON_STRING); - json->value = (char *)state->string; -} - -static int json_reader_set_number(void *userdata) { - json_reader_userdata *state = (json_reader_userdata *)userdata; - grpc_json *json = json_create_and_link(userdata, GRPC_JSON_NUMBER); - json->value = (char *)state->string; - return 1; -} - -/* The object types true, false and null are self-sufficient, and don't need - * any more information beside their type. - */ -static void json_reader_set_true(void *userdata) { - json_create_and_link(userdata, GRPC_JSON_TRUE); -} - -static void json_reader_set_false(void *userdata) { - json_create_and_link(userdata, GRPC_JSON_FALSE); -} - -static void json_reader_set_null(void *userdata) { - json_create_and_link(userdata, GRPC_JSON_NULL); -} - -static grpc_json_reader_vtable reader_vtable = { - json_reader_string_clear, json_reader_string_add_char, - json_reader_string_add_utf32, json_reader_read_char, - json_reader_container_begins, json_reader_container_ends, - json_reader_set_key, json_reader_set_string, - json_reader_set_number, json_reader_set_true, - json_reader_set_false, json_reader_set_null}; - -/* And finally, let's define our public API. */ -grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { - grpc_json_reader reader; - json_reader_userdata state; - grpc_json *json = NULL; - grpc_json_reader_status status; - - if (!input) return NULL; - - state.top = state.current_container = state.current_value = NULL; - state.string = state.key = NULL; - state.string_ptr = state.input = (uint8_t *)input; - state.remaining_input = size; - grpc_json_reader_init(&reader, &reader_vtable, &state); - - status = grpc_json_reader_run(&reader); - json = state.top; - - if ((status != GRPC_JSON_DONE) && json) { - grpc_json_destroy(json); - json = NULL; - } - - return json; -} - -#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff - -grpc_json *grpc_json_parse_string(char *input) { - return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH); -} - -static void json_dump_recursive(grpc_json_writer *writer, grpc_json *json, - int in_object) { - while (json) { - if (in_object) grpc_json_writer_object_key(writer, json->key); - - switch (json->type) { - case GRPC_JSON_OBJECT: - case GRPC_JSON_ARRAY: - grpc_json_writer_container_begins(writer, json->type); - if (json->child) - json_dump_recursive(writer, json->child, - json->type == GRPC_JSON_OBJECT); - grpc_json_writer_container_ends(writer, json->type); - break; - case GRPC_JSON_STRING: - grpc_json_writer_value_string(writer, json->value); - break; - case GRPC_JSON_NUMBER: - grpc_json_writer_value_raw(writer, json->value); - break; - case GRPC_JSON_TRUE: - grpc_json_writer_value_raw_with_len(writer, "true", 4); - break; - case GRPC_JSON_FALSE: - grpc_json_writer_value_raw_with_len(writer, "false", 5); - break; - case GRPC_JSON_NULL: - grpc_json_writer_value_raw_with_len(writer, "null", 4); - break; - default: - GPR_UNREACHABLE_CODE(abort()); - } - json = json->next; - } -} - -static grpc_json_writer_vtable writer_vtable = { - json_writer_output_char, json_writer_output_string, - json_writer_output_string_with_len}; - -char *grpc_json_dump_to_string(grpc_json *json, int indent) { - grpc_json_writer writer; - json_writer_userdata state; - - state.output = NULL; - state.free_space = state.string_len = state.allocated = 0; - grpc_json_writer_init(&writer, indent, &writer_vtable, &state); - - json_dump_recursive(&writer, json, 0); - - json_writer_output_char(&state, 0); - - return state.output; -} diff --git a/src/core/lib/json/json_string.cc b/src/core/lib/json/json_string.cc new file mode 100644 index 0000000000..3178d2d2b4 --- /dev/null +++ b/src/core/lib/json/json_string.cc @@ -0,0 +1,364 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include + +#include "src/core/lib/json/json.h" +#include "src/core/lib/json/json_reader.h" +#include "src/core/lib/json/json_writer.h" + +/* The json reader will construct a bunch of grpc_json objects and + * link them all up together in a tree-like structure that will represent + * the json data in memory. + * + * It also uses its own input as a scratchpad to store all of the decoded, + * unescaped strings. So we need to keep track of all these pointers in + * that opaque structure the reader will carry for us. + * + * Note that this works because the act of parsing json always reduces its + * input size, and never expands it. + */ +typedef struct { + grpc_json *top; + grpc_json *current_container; + grpc_json *current_value; + uint8_t *input; + uint8_t *key; + uint8_t *string; + uint8_t *string_ptr; + size_t remaining_input; +} json_reader_userdata; + +/* This json writer will put everything in a big string. + * The point is that we allocate that string in chunks of 256 bytes. + */ +typedef struct { + char *output; + size_t free_space; + size_t string_len; + size_t allocated; +} json_writer_userdata; + +/* This function checks if there's enough space left in the output buffer, + * and will enlarge it if necessary. We're only allocating chunks of 256 + * bytes at a time (or multiples thereof). + */ +static void json_writer_output_check(void *userdata, size_t needed) { + json_writer_userdata *state = (json_writer_userdata *)userdata; + if (state->free_space >= needed) return; + needed -= state->free_space; + /* Round up by 256 bytes. */ + needed = (needed + 0xff) & ~0xffU; + state->output = (char *)gpr_realloc(state->output, state->allocated + needed); + state->free_space += needed; + state->allocated += needed; +} + +/* These are needed by the writer's implementation. */ +static void json_writer_output_char(void *userdata, char c) { + json_writer_userdata *state = (json_writer_userdata *)userdata; + json_writer_output_check(userdata, 1); + state->output[state->string_len++] = c; + state->free_space--; +} + +static void json_writer_output_string_with_len(void *userdata, const char *str, + size_t len) { + json_writer_userdata *state = (json_writer_userdata *)userdata; + json_writer_output_check(userdata, len); + memcpy(state->output + state->string_len, str, len); + state->string_len += len; + state->free_space -= len; +} + +static void json_writer_output_string(void *userdata, const char *str) { + size_t len = strlen(str); + json_writer_output_string_with_len(userdata, str, len); +} + +/* The reader asks us to clear our scratchpad. In our case, we'll simply mark + * the end of the current string, and advance our output pointer. + */ +static void json_reader_string_clear(void *userdata) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + if (state->string) { + GPR_ASSERT(state->string_ptr < state->input); + *state->string_ptr++ = 0; + } + state->string = state->string_ptr; +} + +static void json_reader_string_add_char(void *userdata, uint32_t c) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + GPR_ASSERT(state->string_ptr < state->input); + GPR_ASSERT(c <= 0xff); + *state->string_ptr++ = (uint8_t)c; +} + +/* We are converting a UTF-32 character into UTF-8 here, + * as described by RFC3629. + */ +static void json_reader_string_add_utf32(void *userdata, uint32_t c) { + if (c <= 0x7f) { + json_reader_string_add_char(userdata, c); + } else if (c <= 0x7ff) { + uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f); + uint32_t b2 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + } else if (c <= 0xffff) { + uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f); + uint32_t b2 = 0x80 | ((c >> 6) & 0x3f); + uint32_t b3 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + json_reader_string_add_char(userdata, b3); + } else if (c <= 0x1fffff) { + uint32_t b1 = 0xf0 | ((c >> 18) & 0x07); + uint32_t b2 = 0x80 | ((c >> 12) & 0x3f); + uint32_t b3 = 0x80 | ((c >> 6) & 0x3f); + uint32_t b4 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + json_reader_string_add_char(userdata, b3); + json_reader_string_add_char(userdata, b4); + } +} + +/* We consider that the input may be a zero-terminated string. So we + * can end up hitting eof before the end of the alleged string length. + */ +static uint32_t json_reader_read_char(void *userdata) { + uint32_t r; + json_reader_userdata *state = (json_reader_userdata *)userdata; + + if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; + + r = *state->input++; + state->remaining_input--; + + if (r == 0) { + state->remaining_input = 0; + return GRPC_JSON_READ_CHAR_EOF; + } + + return r; +} + +/* Helper function to create a new grpc_json object and link it into + * our tree-in-progress inside our opaque structure. + */ +static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + grpc_json *json = grpc_json_create(type); + + json->parent = state->current_container; + json->prev = state->current_value; + state->current_value = json; + + if (json->prev) { + json->prev->next = json; + } + if (json->parent) { + if (!json->parent->child) { + json->parent->child = json; + } + if (json->parent->type == GRPC_JSON_OBJECT) { + json->key = (char *)state->key; + } + } + if (!state->top) { + state->top = json; + } + + return json; +} + +static void json_reader_container_begins(void *userdata, grpc_json_type type) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + grpc_json *container; + + GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); + + container = json_create_and_link(userdata, type); + state->current_container = container; + state->current_value = NULL; +} + +/* It's important to remember that the reader is mostly stateless, so it + * isn't trying to remember what the container was prior the one that just + * ends. Since we're keeping track of these for our own purpose, we are + * able to return that information back, which is useful for it to validate + * the input json stream. + * + * Also note that if we're at the top of the tree, and the last container + * ends, we have to return GRPC_JSON_TOP_LEVEL. + */ +static grpc_json_type json_reader_container_ends(void *userdata) { + grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; + json_reader_userdata *state = (json_reader_userdata *)userdata; + + GPR_ASSERT(state->current_container); + + state->current_value = state->current_container; + state->current_container = state->current_container->parent; + + if (state->current_container) { + container_type = state->current_container->type; + } + + return container_type; +} + +/* The next 3 functions basically are the reader asking us to use our string + * scratchpad for one of these 3 purposes. + * + * Note that in the set_number case, we're not going to try interpreting it. + * We'll keep it as a string, and leave it to the caller to evaluate it. + */ +static void json_reader_set_key(void *userdata) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + state->key = state->string; +} + +static void json_reader_set_string(void *userdata) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + grpc_json *json = json_create_and_link(userdata, GRPC_JSON_STRING); + json->value = (char *)state->string; +} + +static int json_reader_set_number(void *userdata) { + json_reader_userdata *state = (json_reader_userdata *)userdata; + grpc_json *json = json_create_and_link(userdata, GRPC_JSON_NUMBER); + json->value = (char *)state->string; + return 1; +} + +/* The object types true, false and null are self-sufficient, and don't need + * any more information beside their type. + */ +static void json_reader_set_true(void *userdata) { + json_create_and_link(userdata, GRPC_JSON_TRUE); +} + +static void json_reader_set_false(void *userdata) { + json_create_and_link(userdata, GRPC_JSON_FALSE); +} + +static void json_reader_set_null(void *userdata) { + json_create_and_link(userdata, GRPC_JSON_NULL); +} + +static grpc_json_reader_vtable reader_vtable = { + json_reader_string_clear, json_reader_string_add_char, + json_reader_string_add_utf32, json_reader_read_char, + json_reader_container_begins, json_reader_container_ends, + json_reader_set_key, json_reader_set_string, + json_reader_set_number, json_reader_set_true, + json_reader_set_false, json_reader_set_null}; + +/* And finally, let's define our public API. */ +grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { + grpc_json_reader reader; + json_reader_userdata state; + grpc_json *json = NULL; + grpc_json_reader_status status; + + if (!input) return NULL; + + state.top = state.current_container = state.current_value = NULL; + state.string = state.key = NULL; + state.string_ptr = state.input = (uint8_t *)input; + state.remaining_input = size; + grpc_json_reader_init(&reader, &reader_vtable, &state); + + status = grpc_json_reader_run(&reader); + json = state.top; + + if ((status != GRPC_JSON_DONE) && json) { + grpc_json_destroy(json); + json = NULL; + } + + return json; +} + +#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff + +grpc_json *grpc_json_parse_string(char *input) { + return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH); +} + +static void json_dump_recursive(grpc_json_writer *writer, grpc_json *json, + int in_object) { + while (json) { + if (in_object) grpc_json_writer_object_key(writer, json->key); + + switch (json->type) { + case GRPC_JSON_OBJECT: + case GRPC_JSON_ARRAY: + grpc_json_writer_container_begins(writer, json->type); + if (json->child) + json_dump_recursive(writer, json->child, + json->type == GRPC_JSON_OBJECT); + grpc_json_writer_container_ends(writer, json->type); + break; + case GRPC_JSON_STRING: + grpc_json_writer_value_string(writer, json->value); + break; + case GRPC_JSON_NUMBER: + grpc_json_writer_value_raw(writer, json->value); + break; + case GRPC_JSON_TRUE: + grpc_json_writer_value_raw_with_len(writer, "true", 4); + break; + case GRPC_JSON_FALSE: + grpc_json_writer_value_raw_with_len(writer, "false", 5); + break; + case GRPC_JSON_NULL: + grpc_json_writer_value_raw_with_len(writer, "null", 4); + break; + default: + GPR_UNREACHABLE_CODE(abort()); + } + json = json->next; + } +} + +static grpc_json_writer_vtable writer_vtable = { + json_writer_output_char, json_writer_output_string, + json_writer_output_string_with_len}; + +char *grpc_json_dump_to_string(grpc_json *json, int indent) { + grpc_json_writer writer; + json_writer_userdata state; + + state.output = NULL; + state.free_space = state.string_len = state.allocated = 0; + grpc_json_writer_init(&writer, indent, &writer_vtable, &state); + + json_dump_recursive(&writer, json, 0); + + json_writer_output_char(&state, 0); + + return state.output; +} diff --git a/src/core/lib/json/json_writer.c b/src/core/lib/json/json_writer.c deleted file mode 100644 index eab1bff7a6..0000000000 --- a/src/core/lib/json/json_writer.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include "src/core/lib/json/json_writer.h" - -static void json_writer_output_char(grpc_json_writer *writer, char c) { - writer->vtable->output_char(writer->userdata, c); -} - -static void json_writer_output_string(grpc_json_writer *writer, - const char *str) { - writer->vtable->output_string(writer->userdata, str); -} - -static void json_writer_output_string_with_len(grpc_json_writer *writer, - const char *str, size_t len) { - writer->vtable->output_string_with_len(writer->userdata, str, len); -} - -void grpc_json_writer_init(grpc_json_writer *writer, int indent, - grpc_json_writer_vtable *vtable, void *userdata) { - memset(writer, 0, sizeof(*writer)); - writer->container_empty = 1; - writer->indent = indent; - writer->vtable = vtable; - writer->userdata = userdata; -} - -static void json_writer_output_indent(grpc_json_writer *writer) { - static const char spacesstr[] = - " " - " " - " " - " "; - - unsigned spaces = (unsigned)(writer->depth * writer->indent); - - if (writer->indent == 0) return; - - if (writer->got_key) { - json_writer_output_char(writer, ' '); - return; - } - - while (spaces >= (sizeof(spacesstr) - 1)) { - json_writer_output_string_with_len(writer, spacesstr, - sizeof(spacesstr) - 1); - spaces -= (unsigned)(sizeof(spacesstr) - 1); - } - - if (spaces == 0) return; - - json_writer_output_string_with_len( - writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); -} - -static void json_writer_value_end(grpc_json_writer *writer) { - if (writer->container_empty) { - writer->container_empty = 0; - if ((writer->indent == 0) || (writer->depth == 0)) return; - json_writer_output_char(writer, '\n'); - } else { - json_writer_output_char(writer, ','); - if (writer->indent == 0) return; - json_writer_output_char(writer, '\n'); - } -} - -static void json_writer_escape_utf16(grpc_json_writer *writer, uint16_t utf16) { - static const char hex[] = "0123456789abcdef"; - - json_writer_output_string_with_len(writer, "\\u", 2); - json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]); - json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]); - json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]); - json_writer_output_char(writer, hex[(utf16)&0x0f]); -} - -static void json_writer_escape_string(grpc_json_writer *writer, - const char *string) { - json_writer_output_char(writer, '"'); - - for (;;) { - uint8_t c = (uint8_t)*string++; - if (c == 0) { - break; - } else if ((c >= 32) && (c <= 126)) { - if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\'); - json_writer_output_char(writer, (char)c); - } else if ((c < 32) || (c == 127)) { - switch (c) { - case '\b': - json_writer_output_string_with_len(writer, "\\b", 2); - break; - case '\f': - json_writer_output_string_with_len(writer, "\\f", 2); - break; - case '\n': - json_writer_output_string_with_len(writer, "\\n", 2); - break; - case '\r': - json_writer_output_string_with_len(writer, "\\r", 2); - break; - case '\t': - json_writer_output_string_with_len(writer, "\\t", 2); - break; - default: - json_writer_escape_utf16(writer, c); - break; - } - } else { - uint32_t utf32 = 0; - int extra = 0; - int i; - int valid = 1; - if ((c & 0xe0) == 0xc0) { - utf32 = c & 0x1f; - extra = 1; - } else if ((c & 0xf0) == 0xe0) { - utf32 = c & 0x0f; - extra = 2; - } else if ((c & 0xf8) == 0xf0) { - utf32 = c & 0x07; - extra = 3; - } else { - break; - } - for (i = 0; i < extra; i++) { - utf32 <<= 6; - c = (uint8_t)(*string++); - /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */ - if ((c & 0xc0) != 0x80) { - valid = 0; - break; - } - utf32 |= c & 0x3f; - } - if (!valid) break; - /* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam. - * Any other range is technically reserved for future usage, so if we - * don't want the software to break in the future, we have to allow - * anything else. The first non-unicode character is 0x110000. */ - if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000)) - break; - if (utf32 >= 0x10000) { - /* If utf32 contains a character that is above 0xffff, it needs to be - * broken down into a utf-16 surrogate pair. A surrogate pair is first - * a high surrogate, followed by a low surrogate. Each surrogate holds - * 10 bits of usable data, thus allowing a total of 20 bits of data. - * The high surrogate marker is 0xd800, while the low surrogate marker - * is 0xdc00. The low 10 bits of each will be the usable data. - * - * After re-combining the 20 bits of data, one has to add 0x10000 to - * the resulting value, in order to obtain the original character. - * This is obviously because the range 0x0000 - 0xffff can be written - * without any special trick. - * - * Since 0x10ffff is the highest allowed character, we're working in - * the range 0x00000 - 0xfffff after we decrement it by 0x10000. - * That range is exactly 20 bits. - */ - utf32 -= 0x10000; - json_writer_escape_utf16(writer, (uint16_t)(0xd800 | (utf32 >> 10))); - json_writer_escape_utf16(writer, (uint16_t)(0xdc00 | (utf32 & 0x3ff))); - } else { - json_writer_escape_utf16(writer, (uint16_t)utf32); - } - } - } - - json_writer_output_char(writer, '"'); -} - -void grpc_json_writer_container_begins(grpc_json_writer *writer, - grpc_json_type type) { - if (!writer->got_key) json_writer_value_end(writer); - json_writer_output_indent(writer); - json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); - writer->container_empty = 1; - writer->got_key = 0; - writer->depth++; -} - -void grpc_json_writer_container_ends(grpc_json_writer *writer, - grpc_json_type type) { - if (writer->indent && !writer->container_empty) - json_writer_output_char(writer, '\n'); - writer->depth--; - if (!writer->container_empty) json_writer_output_indent(writer); - json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']'); - writer->container_empty = 0; - writer->got_key = 0; -} - -void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) { - json_writer_value_end(writer); - json_writer_output_indent(writer); - json_writer_escape_string(writer, string); - json_writer_output_char(writer, ':'); - writer->got_key = 1; -} - -void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string) { - if (!writer->got_key) json_writer_value_end(writer); - json_writer_output_indent(writer); - json_writer_output_string(writer, string); - writer->got_key = 0; -} - -void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, - const char *string, size_t len) { - if (!writer->got_key) json_writer_value_end(writer); - json_writer_output_indent(writer); - json_writer_output_string_with_len(writer, string, len); - writer->got_key = 0; -} - -void grpc_json_writer_value_string(grpc_json_writer *writer, - const char *string) { - if (!writer->got_key) json_writer_value_end(writer); - json_writer_output_indent(writer); - json_writer_escape_string(writer, string); - writer->got_key = 0; -} diff --git a/src/core/lib/json/json_writer.cc b/src/core/lib/json/json_writer.cc new file mode 100644 index 0000000000..eab1bff7a6 --- /dev/null +++ b/src/core/lib/json/json_writer.cc @@ -0,0 +1,243 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include "src/core/lib/json/json_writer.h" + +static void json_writer_output_char(grpc_json_writer *writer, char c) { + writer->vtable->output_char(writer->userdata, c); +} + +static void json_writer_output_string(grpc_json_writer *writer, + const char *str) { + writer->vtable->output_string(writer->userdata, str); +} + +static void json_writer_output_string_with_len(grpc_json_writer *writer, + const char *str, size_t len) { + writer->vtable->output_string_with_len(writer->userdata, str, len); +} + +void grpc_json_writer_init(grpc_json_writer *writer, int indent, + grpc_json_writer_vtable *vtable, void *userdata) { + memset(writer, 0, sizeof(*writer)); + writer->container_empty = 1; + writer->indent = indent; + writer->vtable = vtable; + writer->userdata = userdata; +} + +static void json_writer_output_indent(grpc_json_writer *writer) { + static const char spacesstr[] = + " " + " " + " " + " "; + + unsigned spaces = (unsigned)(writer->depth * writer->indent); + + if (writer->indent == 0) return; + + if (writer->got_key) { + json_writer_output_char(writer, ' '); + return; + } + + while (spaces >= (sizeof(spacesstr) - 1)) { + json_writer_output_string_with_len(writer, spacesstr, + sizeof(spacesstr) - 1); + spaces -= (unsigned)(sizeof(spacesstr) - 1); + } + + if (spaces == 0) return; + + json_writer_output_string_with_len( + writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); +} + +static void json_writer_value_end(grpc_json_writer *writer) { + if (writer->container_empty) { + writer->container_empty = 0; + if ((writer->indent == 0) || (writer->depth == 0)) return; + json_writer_output_char(writer, '\n'); + } else { + json_writer_output_char(writer, ','); + if (writer->indent == 0) return; + json_writer_output_char(writer, '\n'); + } +} + +static void json_writer_escape_utf16(grpc_json_writer *writer, uint16_t utf16) { + static const char hex[] = "0123456789abcdef"; + + json_writer_output_string_with_len(writer, "\\u", 2); + json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]); + json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]); + json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]); + json_writer_output_char(writer, hex[(utf16)&0x0f]); +} + +static void json_writer_escape_string(grpc_json_writer *writer, + const char *string) { + json_writer_output_char(writer, '"'); + + for (;;) { + uint8_t c = (uint8_t)*string++; + if (c == 0) { + break; + } else if ((c >= 32) && (c <= 126)) { + if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\'); + json_writer_output_char(writer, (char)c); + } else if ((c < 32) || (c == 127)) { + switch (c) { + case '\b': + json_writer_output_string_with_len(writer, "\\b", 2); + break; + case '\f': + json_writer_output_string_with_len(writer, "\\f", 2); + break; + case '\n': + json_writer_output_string_with_len(writer, "\\n", 2); + break; + case '\r': + json_writer_output_string_with_len(writer, "\\r", 2); + break; + case '\t': + json_writer_output_string_with_len(writer, "\\t", 2); + break; + default: + json_writer_escape_utf16(writer, c); + break; + } + } else { + uint32_t utf32 = 0; + int extra = 0; + int i; + int valid = 1; + if ((c & 0xe0) == 0xc0) { + utf32 = c & 0x1f; + extra = 1; + } else if ((c & 0xf0) == 0xe0) { + utf32 = c & 0x0f; + extra = 2; + } else if ((c & 0xf8) == 0xf0) { + utf32 = c & 0x07; + extra = 3; + } else { + break; + } + for (i = 0; i < extra; i++) { + utf32 <<= 6; + c = (uint8_t)(*string++); + /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */ + if ((c & 0xc0) != 0x80) { + valid = 0; + break; + } + utf32 |= c & 0x3f; + } + if (!valid) break; + /* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam. + * Any other range is technically reserved for future usage, so if we + * don't want the software to break in the future, we have to allow + * anything else. The first non-unicode character is 0x110000. */ + if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000)) + break; + if (utf32 >= 0x10000) { + /* If utf32 contains a character that is above 0xffff, it needs to be + * broken down into a utf-16 surrogate pair. A surrogate pair is first + * a high surrogate, followed by a low surrogate. Each surrogate holds + * 10 bits of usable data, thus allowing a total of 20 bits of data. + * The high surrogate marker is 0xd800, while the low surrogate marker + * is 0xdc00. The low 10 bits of each will be the usable data. + * + * After re-combining the 20 bits of data, one has to add 0x10000 to + * the resulting value, in order to obtain the original character. + * This is obviously because the range 0x0000 - 0xffff can be written + * without any special trick. + * + * Since 0x10ffff is the highest allowed character, we're working in + * the range 0x00000 - 0xfffff after we decrement it by 0x10000. + * That range is exactly 20 bits. + */ + utf32 -= 0x10000; + json_writer_escape_utf16(writer, (uint16_t)(0xd800 | (utf32 >> 10))); + json_writer_escape_utf16(writer, (uint16_t)(0xdc00 | (utf32 & 0x3ff))); + } else { + json_writer_escape_utf16(writer, (uint16_t)utf32); + } + } + } + + json_writer_output_char(writer, '"'); +} + +void grpc_json_writer_container_begins(grpc_json_writer *writer, + grpc_json_type type) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); + writer->container_empty = 1; + writer->got_key = 0; + writer->depth++; +} + +void grpc_json_writer_container_ends(grpc_json_writer *writer, + grpc_json_type type) { + if (writer->indent && !writer->container_empty) + json_writer_output_char(writer, '\n'); + writer->depth--; + if (!writer->container_empty) json_writer_output_indent(writer); + json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']'); + writer->container_empty = 0; + writer->got_key = 0; +} + +void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) { + json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_escape_string(writer, string); + json_writer_output_char(writer, ':'); + writer->got_key = 1; +} + +void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_string(writer, string); + writer->got_key = 0; +} + +void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, + const char *string, size_t len) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_string_with_len(writer, string, len); + writer->got_key = 0; +} + +void grpc_json_writer_value_string(grpc_json_writer *writer, + const char *string) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_escape_string(writer, string); + writer->got_key = 0; +} diff --git a/src/core/lib/profiling/basic_timers.c b/src/core/lib/profiling/basic_timers.c deleted file mode 100644 index ab9d60481c..0000000000 --- a/src/core/lib/profiling/basic_timers.c +++ /dev/null @@ -1,283 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/lib/profiling/timers.h" - -#ifdef GRPC_BASIC_PROFILER - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/support/env.h" - -typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type; - -typedef struct gpr_timer_entry { - gpr_timespec tm; - const char *tagstr; - const char *file; - short line; - char type; - uint8_t important; - int thd; -} gpr_timer_entry; - -#define MAX_COUNT 1000000 - -typedef struct gpr_timer_log { - size_t num_entries; - struct gpr_timer_log *next; - struct gpr_timer_log *prev; - gpr_timer_entry log[MAX_COUNT]; -} gpr_timer_log; - -typedef struct gpr_timer_log_list { - gpr_timer_log *head; - /* valid iff head!=NULL */ - gpr_timer_log *tail; -} gpr_timer_log_list; - -static __thread gpr_timer_log *g_thread_log; -static gpr_once g_once_init = GPR_ONCE_INIT; -static FILE *output_file; -static const char *output_filename_or_null = NULL; -static pthread_mutex_t g_mu; -static pthread_cond_t g_cv; -static gpr_timer_log_list g_in_progress_logs; -static gpr_timer_log_list g_done_logs; -static int g_shutdown; -static gpr_thd_id g_writing_thread; -static __thread int g_thread_id; -static int g_next_thread_id; -static int g_writing_enabled = 1; - -static const char *output_filename() { - if (output_filename_or_null == NULL) { - output_filename_or_null = gpr_getenv("LATENCY_TRACE"); - if (output_filename_or_null == NULL || - strlen(output_filename_or_null) == 0) { - output_filename_or_null = "latency_trace.txt"; - } - } - return output_filename_or_null; -} - -static int timer_log_push_back(gpr_timer_log_list *list, gpr_timer_log *log) { - if (list->head == NULL) { - list->head = list->tail = log; - log->next = log->prev = NULL; - return 1; - } else { - log->prev = list->tail; - log->next = NULL; - list->tail->next = log; - list->tail = log; - return 0; - } -} - -static gpr_timer_log *timer_log_pop_front(gpr_timer_log_list *list) { - gpr_timer_log *out = list->head; - if (out != NULL) { - list->head = out->next; - if (list->head != NULL) { - list->head->prev = NULL; - } else { - list->tail = NULL; - } - } - return out; -} - -static void timer_log_remove(gpr_timer_log_list *list, gpr_timer_log *log) { - if (log->prev == NULL) { - list->head = log->next; - if (list->head != NULL) { - list->head->prev = NULL; - } - } else { - log->prev->next = log->next; - } - if (log->next == NULL) { - list->tail = log->prev; - if (list->tail != NULL) { - list->tail->next = NULL; - } - } else { - log->next->prev = log->prev; - } -} - -static void write_log(gpr_timer_log *log) { - size_t i; - if (output_file == NULL) { - output_file = fopen(output_filename(), "w"); - } - for (i = 0; i < log->num_entries; i++) { - gpr_timer_entry *entry = &(log->log[i]); - if (gpr_time_cmp(entry->tm, gpr_time_0(entry->tm.clock_type)) < 0) { - entry->tm = gpr_time_0(entry->tm.clock_type); - } - fprintf(output_file, - "{\"t\": %" PRId64 - ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": " - "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n", - entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type, - entry->tagstr, entry->file, entry->line, entry->important); - } -} - -static void writing_thread(void *unused) { - gpr_timer_log *log; - pthread_mutex_lock(&g_mu); - for (;;) { - while ((log = timer_log_pop_front(&g_done_logs)) == NULL && !g_shutdown) { - pthread_cond_wait(&g_cv, &g_mu); - } - if (log != NULL) { - pthread_mutex_unlock(&g_mu); - write_log(log); - free(log); - pthread_mutex_lock(&g_mu); - } - if (g_shutdown) { - pthread_mutex_unlock(&g_mu); - return; - } - } -} - -static void flush_logs(gpr_timer_log_list *list) { - gpr_timer_log *log; - while ((log = timer_log_pop_front(list)) != NULL) { - write_log(log); - free(log); - } -} - -static void finish_writing(void) { - pthread_mutex_lock(&g_mu); - g_shutdown = 1; - pthread_cond_signal(&g_cv); - pthread_mutex_unlock(&g_mu); - gpr_thd_join(g_writing_thread); - - gpr_log(GPR_INFO, "flushing logs"); - - pthread_mutex_lock(&g_mu); - flush_logs(&g_done_logs); - flush_logs(&g_in_progress_logs); - pthread_mutex_unlock(&g_mu); - - if (output_file) { - fclose(output_file); - } -} - -void gpr_timers_set_log_filename(const char *filename) { - output_filename_or_null = filename; -} - -static void init_output() { - gpr_thd_options options = gpr_thd_options_default(); - gpr_thd_options_set_joinable(&options); - GPR_ASSERT(gpr_thd_new(&g_writing_thread, writing_thread, NULL, &options)); - atexit(finish_writing); -} - -static void rotate_log() { - /* Using malloc here, as this code could end up being called by gpr_malloc */ - gpr_timer_log *new = malloc(sizeof(*new)); - gpr_once_init(&g_once_init, init_output); - new->num_entries = 0; - pthread_mutex_lock(&g_mu); - if (g_thread_log != NULL) { - timer_log_remove(&g_in_progress_logs, g_thread_log); - if (timer_log_push_back(&g_done_logs, g_thread_log)) { - pthread_cond_signal(&g_cv); - } - } else { - g_thread_id = g_next_thread_id++; - } - timer_log_push_back(&g_in_progress_logs, new); - pthread_mutex_unlock(&g_mu); - g_thread_log = new; -} - -static void gpr_timers_log_add(const char *tagstr, marker_type type, - int important, const char *file, int line) { - gpr_timer_entry *entry; - - if (!g_writing_enabled) { - return; - } - - if (g_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) { - rotate_log(); - } - - entry = &g_thread_log->log[g_thread_log->num_entries++]; - - entry->tm = gpr_now(GPR_CLOCK_PRECISE); - entry->tagstr = tagstr; - entry->type = type; - entry->file = file; - entry->line = (short)line; - entry->important = important != 0; - entry->thd = g_thread_id; -} - -/* Latency profiler API implementation. */ -void gpr_timer_add_mark(const char *tagstr, int important, const char *file, - int line) { - gpr_timers_log_add(tagstr, MARK, important, file, line); -} - -void gpr_timer_begin(const char *tagstr, int important, const char *file, - int line) { - gpr_timers_log_add(tagstr, BEGIN, important, file, line); -} - -void gpr_timer_end(const char *tagstr, int important, const char *file, - int line) { - gpr_timers_log_add(tagstr, END, important, file, line); -} - -void gpr_timer_set_enabled(int enabled) { g_writing_enabled = enabled; } - -/* Basic profiler specific API functions. */ -void gpr_timers_global_init(void) {} - -void gpr_timers_global_destroy(void) {} - -#else /* !GRPC_BASIC_PROFILER */ -void gpr_timers_global_init(void) {} - -void gpr_timers_global_destroy(void) {} - -void gpr_timers_set_log_filename(const char *filename) {} - -void gpr_timer_set_enabled(int enabled) {} -#endif /* GRPC_BASIC_PROFILER */ diff --git a/src/core/lib/profiling/basic_timers.cc b/src/core/lib/profiling/basic_timers.cc new file mode 100644 index 0000000000..ab9d60481c --- /dev/null +++ b/src/core/lib/profiling/basic_timers.cc @@ -0,0 +1,283 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/profiling/timers.h" + +#ifdef GRPC_BASIC_PROFILER + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/support/env.h" + +typedef enum { BEGIN = '{', END = '}', MARK = '.' } marker_type; + +typedef struct gpr_timer_entry { + gpr_timespec tm; + const char *tagstr; + const char *file; + short line; + char type; + uint8_t important; + int thd; +} gpr_timer_entry; + +#define MAX_COUNT 1000000 + +typedef struct gpr_timer_log { + size_t num_entries; + struct gpr_timer_log *next; + struct gpr_timer_log *prev; + gpr_timer_entry log[MAX_COUNT]; +} gpr_timer_log; + +typedef struct gpr_timer_log_list { + gpr_timer_log *head; + /* valid iff head!=NULL */ + gpr_timer_log *tail; +} gpr_timer_log_list; + +static __thread gpr_timer_log *g_thread_log; +static gpr_once g_once_init = GPR_ONCE_INIT; +static FILE *output_file; +static const char *output_filename_or_null = NULL; +static pthread_mutex_t g_mu; +static pthread_cond_t g_cv; +static gpr_timer_log_list g_in_progress_logs; +static gpr_timer_log_list g_done_logs; +static int g_shutdown; +static gpr_thd_id g_writing_thread; +static __thread int g_thread_id; +static int g_next_thread_id; +static int g_writing_enabled = 1; + +static const char *output_filename() { + if (output_filename_or_null == NULL) { + output_filename_or_null = gpr_getenv("LATENCY_TRACE"); + if (output_filename_or_null == NULL || + strlen(output_filename_or_null) == 0) { + output_filename_or_null = "latency_trace.txt"; + } + } + return output_filename_or_null; +} + +static int timer_log_push_back(gpr_timer_log_list *list, gpr_timer_log *log) { + if (list->head == NULL) { + list->head = list->tail = log; + log->next = log->prev = NULL; + return 1; + } else { + log->prev = list->tail; + log->next = NULL; + list->tail->next = log; + list->tail = log; + return 0; + } +} + +static gpr_timer_log *timer_log_pop_front(gpr_timer_log_list *list) { + gpr_timer_log *out = list->head; + if (out != NULL) { + list->head = out->next; + if (list->head != NULL) { + list->head->prev = NULL; + } else { + list->tail = NULL; + } + } + return out; +} + +static void timer_log_remove(gpr_timer_log_list *list, gpr_timer_log *log) { + if (log->prev == NULL) { + list->head = log->next; + if (list->head != NULL) { + list->head->prev = NULL; + } + } else { + log->prev->next = log->next; + } + if (log->next == NULL) { + list->tail = log->prev; + if (list->tail != NULL) { + list->tail->next = NULL; + } + } else { + log->next->prev = log->prev; + } +} + +static void write_log(gpr_timer_log *log) { + size_t i; + if (output_file == NULL) { + output_file = fopen(output_filename(), "w"); + } + for (i = 0; i < log->num_entries; i++) { + gpr_timer_entry *entry = &(log->log[i]); + if (gpr_time_cmp(entry->tm, gpr_time_0(entry->tm.clock_type)) < 0) { + entry->tm = gpr_time_0(entry->tm.clock_type); + } + fprintf(output_file, + "{\"t\": %" PRId64 + ".%09d, \"thd\": \"%d\", \"type\": \"%c\", \"tag\": " + "\"%s\", \"file\": \"%s\", \"line\": %d, \"imp\": %d}\n", + entry->tm.tv_sec, entry->tm.tv_nsec, entry->thd, entry->type, + entry->tagstr, entry->file, entry->line, entry->important); + } +} + +static void writing_thread(void *unused) { + gpr_timer_log *log; + pthread_mutex_lock(&g_mu); + for (;;) { + while ((log = timer_log_pop_front(&g_done_logs)) == NULL && !g_shutdown) { + pthread_cond_wait(&g_cv, &g_mu); + } + if (log != NULL) { + pthread_mutex_unlock(&g_mu); + write_log(log); + free(log); + pthread_mutex_lock(&g_mu); + } + if (g_shutdown) { + pthread_mutex_unlock(&g_mu); + return; + } + } +} + +static void flush_logs(gpr_timer_log_list *list) { + gpr_timer_log *log; + while ((log = timer_log_pop_front(list)) != NULL) { + write_log(log); + free(log); + } +} + +static void finish_writing(void) { + pthread_mutex_lock(&g_mu); + g_shutdown = 1; + pthread_cond_signal(&g_cv); + pthread_mutex_unlock(&g_mu); + gpr_thd_join(g_writing_thread); + + gpr_log(GPR_INFO, "flushing logs"); + + pthread_mutex_lock(&g_mu); + flush_logs(&g_done_logs); + flush_logs(&g_in_progress_logs); + pthread_mutex_unlock(&g_mu); + + if (output_file) { + fclose(output_file); + } +} + +void gpr_timers_set_log_filename(const char *filename) { + output_filename_or_null = filename; +} + +static void init_output() { + gpr_thd_options options = gpr_thd_options_default(); + gpr_thd_options_set_joinable(&options); + GPR_ASSERT(gpr_thd_new(&g_writing_thread, writing_thread, NULL, &options)); + atexit(finish_writing); +} + +static void rotate_log() { + /* Using malloc here, as this code could end up being called by gpr_malloc */ + gpr_timer_log *new = malloc(sizeof(*new)); + gpr_once_init(&g_once_init, init_output); + new->num_entries = 0; + pthread_mutex_lock(&g_mu); + if (g_thread_log != NULL) { + timer_log_remove(&g_in_progress_logs, g_thread_log); + if (timer_log_push_back(&g_done_logs, g_thread_log)) { + pthread_cond_signal(&g_cv); + } + } else { + g_thread_id = g_next_thread_id++; + } + timer_log_push_back(&g_in_progress_logs, new); + pthread_mutex_unlock(&g_mu); + g_thread_log = new; +} + +static void gpr_timers_log_add(const char *tagstr, marker_type type, + int important, const char *file, int line) { + gpr_timer_entry *entry; + + if (!g_writing_enabled) { + return; + } + + if (g_thread_log == NULL || g_thread_log->num_entries == MAX_COUNT) { + rotate_log(); + } + + entry = &g_thread_log->log[g_thread_log->num_entries++]; + + entry->tm = gpr_now(GPR_CLOCK_PRECISE); + entry->tagstr = tagstr; + entry->type = type; + entry->file = file; + entry->line = (short)line; + entry->important = important != 0; + entry->thd = g_thread_id; +} + +/* Latency profiler API implementation. */ +void gpr_timer_add_mark(const char *tagstr, int important, const char *file, + int line) { + gpr_timers_log_add(tagstr, MARK, important, file, line); +} + +void gpr_timer_begin(const char *tagstr, int important, const char *file, + int line) { + gpr_timers_log_add(tagstr, BEGIN, important, file, line); +} + +void gpr_timer_end(const char *tagstr, int important, const char *file, + int line) { + gpr_timers_log_add(tagstr, END, important, file, line); +} + +void gpr_timer_set_enabled(int enabled) { g_writing_enabled = enabled; } + +/* Basic profiler specific API functions. */ +void gpr_timers_global_init(void) {} + +void gpr_timers_global_destroy(void) {} + +#else /* !GRPC_BASIC_PROFILER */ +void gpr_timers_global_init(void) {} + +void gpr_timers_global_destroy(void) {} + +void gpr_timers_set_log_filename(const char *filename) {} + +void gpr_timer_set_enabled(int enabled) {} +#endif /* GRPC_BASIC_PROFILER */ diff --git a/src/core/lib/profiling/stap_timers.c b/src/core/lib/profiling/stap_timers.c deleted file mode 100644 index c86d74f058..0000000000 --- a/src/core/lib/profiling/stap_timers.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GRPC_STAP_PROFILER - -#include "src/core/lib/profiling/timers.h" - -#include -/* Generated from src/core/profiling/stap_probes.d */ -#include "src/core/lib/profiling/stap_probes.h" - -/* Latency profiler API implementation. */ -void gpr_timer_add_mark(int tag, const char *tagstr, void *id, const char *file, - int line) { - _STAP_ADD_MARK(tag); -} - -void gpr_timer_add_important_mark(int tag, const char *tagstr, void *id, - const char *file, int line) { - _STAP_ADD_IMPORTANT_MARK(tag); -} - -void gpr_timer_begin(int tag, const char *tagstr, void *id, const char *file, - int line) { - _STAP_TIMING_NS_BEGIN(tag); -} - -void gpr_timer_end(int tag, const char *tagstr, void *id, const char *file, - int line) { - _STAP_TIMING_NS_END(tag); -} - -#endif /* GRPC_STAP_PROFILER */ diff --git a/src/core/lib/profiling/stap_timers.cc b/src/core/lib/profiling/stap_timers.cc new file mode 100644 index 0000000000..c86d74f058 --- /dev/null +++ b/src/core/lib/profiling/stap_timers.cc @@ -0,0 +1,50 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GRPC_STAP_PROFILER + +#include "src/core/lib/profiling/timers.h" + +#include +/* Generated from src/core/profiling/stap_probes.d */ +#include "src/core/lib/profiling/stap_probes.h" + +/* Latency profiler API implementation. */ +void gpr_timer_add_mark(int tag, const char *tagstr, void *id, const char *file, + int line) { + _STAP_ADD_MARK(tag); +} + +void gpr_timer_add_important_mark(int tag, const char *tagstr, void *id, + const char *file, int line) { + _STAP_ADD_IMPORTANT_MARK(tag); +} + +void gpr_timer_begin(int tag, const char *tagstr, void *id, const char *file, + int line) { + _STAP_TIMING_NS_BEGIN(tag); +} + +void gpr_timer_end(int tag, const char *tagstr, void *id, const char *file, + int line) { + _STAP_TIMING_NS_END(tag); +} + +#endif /* GRPC_STAP_PROFILER */ diff --git a/src/core/lib/security/context/security_context.c b/src/core/lib/security/context/security_context.c deleted file mode 100644 index 31d800b9b4..0000000000 --- a/src/core/lib/security/context/security_context.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/context/security_context.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" - -#include -#include -#include -#include - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_auth_context_refcount = - GRPC_TRACER_INITIALIZER(false, "auth_context_refcount"); -#endif - -/* --- grpc_call --- */ - -grpc_call_error grpc_call_set_credentials(grpc_call *call, - grpc_call_credentials *creds) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_client_security_context *ctx = NULL; - GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2, - (call, creds)); - if (!grpc_call_is_client(call)) { - gpr_log(GPR_ERROR, "Method is client-side only."); - return GRPC_CALL_ERROR_NOT_ON_SERVER; - } - ctx = (grpc_client_security_context *)grpc_call_context_get( - call, GRPC_CONTEXT_SECURITY); - if (ctx == NULL) { - ctx = grpc_client_security_context_create(); - ctx->creds = grpc_call_credentials_ref(creds); - grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx, - grpc_client_security_context_destroy); - } else { - grpc_call_credentials_unref(&exec_ctx, ctx->creds); - ctx->creds = grpc_call_credentials_ref(creds); - } - grpc_exec_ctx_finish(&exec_ctx); - return GRPC_CALL_OK; -} - -grpc_auth_context *grpc_call_auth_context(grpc_call *call) { - void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY); - GRPC_API_TRACE("grpc_call_auth_context(call=%p)", 1, (call)); - if (sec_ctx == NULL) return NULL; - return grpc_call_is_client(call) - ? GRPC_AUTH_CONTEXT_REF( - ((grpc_client_security_context *)sec_ctx)->auth_context, - "grpc_call_auth_context client") - : GRPC_AUTH_CONTEXT_REF( - ((grpc_server_security_context *)sec_ctx)->auth_context, - "grpc_call_auth_context server"); -} - -void grpc_auth_context_release(grpc_auth_context *context) { - GRPC_API_TRACE("grpc_auth_context_release(context=%p)", 1, (context)); - GRPC_AUTH_CONTEXT_UNREF(context, "grpc_auth_context_unref"); -} - -/* --- grpc_client_security_context --- */ - -grpc_client_security_context *grpc_client_security_context_create(void) { - return (grpc_client_security_context *)gpr_zalloc( - sizeof(grpc_client_security_context)); -} - -void grpc_client_security_context_destroy(void *ctx) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_client_security_context *c = (grpc_client_security_context *)ctx; - grpc_call_credentials_unref(&exec_ctx, c->creds); - GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "client_security_context"); - if (c->extension.instance != NULL && c->extension.destroy != NULL) { - c->extension.destroy(c->extension.instance); - } - gpr_free(ctx); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* --- grpc_server_security_context --- */ - -grpc_server_security_context *grpc_server_security_context_create(void) { - return (grpc_server_security_context *)gpr_zalloc( - sizeof(grpc_server_security_context)); -} - -void grpc_server_security_context_destroy(void *ctx) { - grpc_server_security_context *c = (grpc_server_security_context *)ctx; - GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "server_security_context"); - if (c->extension.instance != NULL && c->extension.destroy != NULL) { - c->extension.destroy(c->extension.instance); - } - gpr_free(ctx); -} - -/* --- grpc_auth_context --- */ - -static grpc_auth_property_iterator empty_iterator = {NULL, 0, NULL}; - -grpc_auth_context *grpc_auth_context_create(grpc_auth_context *chained) { - grpc_auth_context *ctx = - (grpc_auth_context *)gpr_zalloc(sizeof(grpc_auth_context)); - gpr_ref_init(&ctx->refcount, 1); - if (chained != NULL) { - ctx->chained = GRPC_AUTH_CONTEXT_REF(chained, "chained"); - ctx->peer_identity_property_name = - ctx->chained->peer_identity_property_name; - } - return ctx; -} - -#ifndef NDEBUG -grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *ctx, - const char *file, int line, - const char *reason) { - if (ctx == NULL) return NULL; - if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "AUTH_CONTEXT:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val, - val + 1, reason); - } -#else -grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *ctx) { - if (ctx == NULL) return NULL; -#endif - gpr_ref(&ctx->refcount); - return ctx; -} - -#ifndef NDEBUG -void grpc_auth_context_unref(grpc_auth_context *ctx, const char *file, int line, - const char *reason) { - if (ctx == NULL) return; - if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "AUTH_CONTEXT:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val, - val - 1, reason); - } -#else -void grpc_auth_context_unref(grpc_auth_context *ctx) { - if (ctx == NULL) return; -#endif - if (gpr_unref(&ctx->refcount)) { - size_t i; - GRPC_AUTH_CONTEXT_UNREF(ctx->chained, "chained"); - if (ctx->properties.array != NULL) { - for (i = 0; i < ctx->properties.count; i++) { - grpc_auth_property_reset(&ctx->properties.array[i]); - } - gpr_free(ctx->properties.array); - } - gpr_free(ctx); - } -} - -const char *grpc_auth_context_peer_identity_property_name( - const grpc_auth_context *ctx) { - GRPC_API_TRACE("grpc_auth_context_peer_identity_property_name(ctx=%p)", 1, - (ctx)); - return ctx->peer_identity_property_name; -} - -int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context *ctx, - const char *name) { - grpc_auth_property_iterator it = - grpc_auth_context_find_properties_by_name(ctx, name); - const grpc_auth_property *prop = grpc_auth_property_iterator_next(&it); - GRPC_API_TRACE( - "grpc_auth_context_set_peer_identity_property_name(ctx=%p, name=%s)", 2, - (ctx, name)); - if (prop == NULL) { - gpr_log(GPR_ERROR, "Property name %s not found in auth context.", - name != NULL ? name : "NULL"); - return 0; - } - ctx->peer_identity_property_name = prop->name; - return 1; -} - -int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx) { - GRPC_API_TRACE("grpc_auth_context_peer_is_authenticated(ctx=%p)", 1, (ctx)); - return ctx->peer_identity_property_name == NULL ? 0 : 1; -} - -grpc_auth_property_iterator grpc_auth_context_property_iterator( - const grpc_auth_context *ctx) { - grpc_auth_property_iterator it = empty_iterator; - GRPC_API_TRACE("grpc_auth_context_property_iterator(ctx=%p)", 1, (ctx)); - if (ctx == NULL) return it; - it.ctx = ctx; - return it; -} - -const grpc_auth_property *grpc_auth_property_iterator_next( - grpc_auth_property_iterator *it) { - GRPC_API_TRACE("grpc_auth_property_iterator_next(it=%p)", 1, (it)); - if (it == NULL || it->ctx == NULL) return NULL; - while (it->index == it->ctx->properties.count) { - if (it->ctx->chained == NULL) return NULL; - it->ctx = it->ctx->chained; - it->index = 0; - } - if (it->name == NULL) { - return &it->ctx->properties.array[it->index++]; - } else { - while (it->index < it->ctx->properties.count) { - const grpc_auth_property *prop = &it->ctx->properties.array[it->index++]; - GPR_ASSERT(prop->name != NULL); - if (strcmp(it->name, prop->name) == 0) { - return prop; - } - } - /* We could not find the name, try another round. */ - return grpc_auth_property_iterator_next(it); - } -} - -grpc_auth_property_iterator grpc_auth_context_find_properties_by_name( - const grpc_auth_context *ctx, const char *name) { - grpc_auth_property_iterator it = empty_iterator; - GRPC_API_TRACE("grpc_auth_context_find_properties_by_name(ctx=%p, name=%s)", - 2, (ctx, name)); - if (ctx == NULL || name == NULL) return empty_iterator; - it.ctx = ctx; - it.name = name; - return it; -} - -grpc_auth_property_iterator grpc_auth_context_peer_identity( - const grpc_auth_context *ctx) { - GRPC_API_TRACE("grpc_auth_context_peer_identity(ctx=%p)", 1, (ctx)); - if (ctx == NULL) return empty_iterator; - return grpc_auth_context_find_properties_by_name( - ctx, ctx->peer_identity_property_name); -} - -static void ensure_auth_context_capacity(grpc_auth_context *ctx) { - if (ctx->properties.count == ctx->properties.capacity) { - ctx->properties.capacity = - GPR_MAX(ctx->properties.capacity + 8, ctx->properties.capacity * 2); - ctx->properties.array = (grpc_auth_property *)gpr_realloc( - ctx->properties.array, - ctx->properties.capacity * sizeof(grpc_auth_property)); - } -} - -void grpc_auth_context_add_property(grpc_auth_context *ctx, const char *name, - const char *value, size_t value_length) { - grpc_auth_property *prop; - GRPC_API_TRACE( - "grpc_auth_context_add_property(ctx=%p, name=%s, value=%*.*s, " - "value_length=%lu)", - 6, (ctx, name, (int)value_length, (int)value_length, value, - (unsigned long)value_length)); - ensure_auth_context_capacity(ctx); - prop = &ctx->properties.array[ctx->properties.count++]; - prop->name = gpr_strdup(name); - prop->value = (char *)gpr_malloc(value_length + 1); - memcpy(prop->value, value, value_length); - prop->value[value_length] = '\0'; - prop->value_length = value_length; -} - -void grpc_auth_context_add_cstring_property(grpc_auth_context *ctx, - const char *name, - const char *value) { - grpc_auth_property *prop; - GRPC_API_TRACE( - "grpc_auth_context_add_cstring_property(ctx=%p, name=%s, value=%s)", 3, - (ctx, name, value)); - ensure_auth_context_capacity(ctx); - prop = &ctx->properties.array[ctx->properties.count++]; - prop->name = gpr_strdup(name); - prop->value = gpr_strdup(value); - prop->value_length = strlen(value); -} - -void grpc_auth_property_reset(grpc_auth_property *property) { - gpr_free(property->name); - gpr_free(property->value); - memset(property, 0, sizeof(grpc_auth_property)); -} - -static void auth_context_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { - GRPC_AUTH_CONTEXT_UNREF((grpc_auth_context *)p, "auth_context_pointer_arg"); -} - -static void *auth_context_pointer_arg_copy(void *p) { - return GRPC_AUTH_CONTEXT_REF((grpc_auth_context *)p, - "auth_context_pointer_arg"); -} - -static int auth_context_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); } - -static const grpc_arg_pointer_vtable auth_context_pointer_vtable = { - auth_context_pointer_arg_copy, auth_context_pointer_arg_destroy, - auth_context_pointer_cmp}; - -grpc_arg grpc_auth_context_to_arg(grpc_auth_context *p) { - return grpc_channel_arg_pointer_create((char *)GRPC_AUTH_CONTEXT_ARG, p, - &auth_context_pointer_vtable); -} - -grpc_auth_context *grpc_auth_context_from_arg(const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_AUTH_CONTEXT_ARG) != 0) return NULL; - if (arg->type != GRPC_ARG_POINTER) { - gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_AUTH_CONTEXT_ARG); - return NULL; - } - return (grpc_auth_context *)arg->value.pointer.p; -} - -grpc_auth_context *grpc_find_auth_context_in_args( - const grpc_channel_args *args) { - size_t i; - if (args == NULL) return NULL; - for (i = 0; i < args->num_args; i++) { - grpc_auth_context *p = grpc_auth_context_from_arg(&args->args[i]); - if (p != NULL) return p; - } - return NULL; -} diff --git a/src/core/lib/security/context/security_context.cc b/src/core/lib/security/context/security_context.cc new file mode 100644 index 0000000000..31d800b9b4 --- /dev/null +++ b/src/core/lib/security/context/security_context.cc @@ -0,0 +1,346 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" + +#include +#include +#include +#include + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_auth_context_refcount = + GRPC_TRACER_INITIALIZER(false, "auth_context_refcount"); +#endif + +/* --- grpc_call --- */ + +grpc_call_error grpc_call_set_credentials(grpc_call *call, + grpc_call_credentials *creds) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_client_security_context *ctx = NULL; + GRPC_API_TRACE("grpc_call_set_credentials(call=%p, creds=%p)", 2, + (call, creds)); + if (!grpc_call_is_client(call)) { + gpr_log(GPR_ERROR, "Method is client-side only."); + return GRPC_CALL_ERROR_NOT_ON_SERVER; + } + ctx = (grpc_client_security_context *)grpc_call_context_get( + call, GRPC_CONTEXT_SECURITY); + if (ctx == NULL) { + ctx = grpc_client_security_context_create(); + ctx->creds = grpc_call_credentials_ref(creds); + grpc_call_context_set(call, GRPC_CONTEXT_SECURITY, ctx, + grpc_client_security_context_destroy); + } else { + grpc_call_credentials_unref(&exec_ctx, ctx->creds); + ctx->creds = grpc_call_credentials_ref(creds); + } + grpc_exec_ctx_finish(&exec_ctx); + return GRPC_CALL_OK; +} + +grpc_auth_context *grpc_call_auth_context(grpc_call *call) { + void *sec_ctx = grpc_call_context_get(call, GRPC_CONTEXT_SECURITY); + GRPC_API_TRACE("grpc_call_auth_context(call=%p)", 1, (call)); + if (sec_ctx == NULL) return NULL; + return grpc_call_is_client(call) + ? GRPC_AUTH_CONTEXT_REF( + ((grpc_client_security_context *)sec_ctx)->auth_context, + "grpc_call_auth_context client") + : GRPC_AUTH_CONTEXT_REF( + ((grpc_server_security_context *)sec_ctx)->auth_context, + "grpc_call_auth_context server"); +} + +void grpc_auth_context_release(grpc_auth_context *context) { + GRPC_API_TRACE("grpc_auth_context_release(context=%p)", 1, (context)); + GRPC_AUTH_CONTEXT_UNREF(context, "grpc_auth_context_unref"); +} + +/* --- grpc_client_security_context --- */ + +grpc_client_security_context *grpc_client_security_context_create(void) { + return (grpc_client_security_context *)gpr_zalloc( + sizeof(grpc_client_security_context)); +} + +void grpc_client_security_context_destroy(void *ctx) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_client_security_context *c = (grpc_client_security_context *)ctx; + grpc_call_credentials_unref(&exec_ctx, c->creds); + GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "client_security_context"); + if (c->extension.instance != NULL && c->extension.destroy != NULL) { + c->extension.destroy(c->extension.instance); + } + gpr_free(ctx); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* --- grpc_server_security_context --- */ + +grpc_server_security_context *grpc_server_security_context_create(void) { + return (grpc_server_security_context *)gpr_zalloc( + sizeof(grpc_server_security_context)); +} + +void grpc_server_security_context_destroy(void *ctx) { + grpc_server_security_context *c = (grpc_server_security_context *)ctx; + GRPC_AUTH_CONTEXT_UNREF(c->auth_context, "server_security_context"); + if (c->extension.instance != NULL && c->extension.destroy != NULL) { + c->extension.destroy(c->extension.instance); + } + gpr_free(ctx); +} + +/* --- grpc_auth_context --- */ + +static grpc_auth_property_iterator empty_iterator = {NULL, 0, NULL}; + +grpc_auth_context *grpc_auth_context_create(grpc_auth_context *chained) { + grpc_auth_context *ctx = + (grpc_auth_context *)gpr_zalloc(sizeof(grpc_auth_context)); + gpr_ref_init(&ctx->refcount, 1); + if (chained != NULL) { + ctx->chained = GRPC_AUTH_CONTEXT_REF(chained, "chained"); + ctx->peer_identity_property_name = + ctx->chained->peer_identity_property_name; + } + return ctx; +} + +#ifndef NDEBUG +grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *ctx, + const char *file, int line, + const char *reason) { + if (ctx == NULL) return NULL; + if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "AUTH_CONTEXT:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val, + val + 1, reason); + } +#else +grpc_auth_context *grpc_auth_context_ref(grpc_auth_context *ctx) { + if (ctx == NULL) return NULL; +#endif + gpr_ref(&ctx->refcount); + return ctx; +} + +#ifndef NDEBUG +void grpc_auth_context_unref(grpc_auth_context *ctx, const char *file, int line, + const char *reason) { + if (ctx == NULL) return; + if (GRPC_TRACER_ON(grpc_trace_auth_context_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&ctx->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "AUTH_CONTEXT:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", ctx, val, + val - 1, reason); + } +#else +void grpc_auth_context_unref(grpc_auth_context *ctx) { + if (ctx == NULL) return; +#endif + if (gpr_unref(&ctx->refcount)) { + size_t i; + GRPC_AUTH_CONTEXT_UNREF(ctx->chained, "chained"); + if (ctx->properties.array != NULL) { + for (i = 0; i < ctx->properties.count; i++) { + grpc_auth_property_reset(&ctx->properties.array[i]); + } + gpr_free(ctx->properties.array); + } + gpr_free(ctx); + } +} + +const char *grpc_auth_context_peer_identity_property_name( + const grpc_auth_context *ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_identity_property_name(ctx=%p)", 1, + (ctx)); + return ctx->peer_identity_property_name; +} + +int grpc_auth_context_set_peer_identity_property_name(grpc_auth_context *ctx, + const char *name) { + grpc_auth_property_iterator it = + grpc_auth_context_find_properties_by_name(ctx, name); + const grpc_auth_property *prop = grpc_auth_property_iterator_next(&it); + GRPC_API_TRACE( + "grpc_auth_context_set_peer_identity_property_name(ctx=%p, name=%s)", 2, + (ctx, name)); + if (prop == NULL) { + gpr_log(GPR_ERROR, "Property name %s not found in auth context.", + name != NULL ? name : "NULL"); + return 0; + } + ctx->peer_identity_property_name = prop->name; + return 1; +} + +int grpc_auth_context_peer_is_authenticated(const grpc_auth_context *ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_is_authenticated(ctx=%p)", 1, (ctx)); + return ctx->peer_identity_property_name == NULL ? 0 : 1; +} + +grpc_auth_property_iterator grpc_auth_context_property_iterator( + const grpc_auth_context *ctx) { + grpc_auth_property_iterator it = empty_iterator; + GRPC_API_TRACE("grpc_auth_context_property_iterator(ctx=%p)", 1, (ctx)); + if (ctx == NULL) return it; + it.ctx = ctx; + return it; +} + +const grpc_auth_property *grpc_auth_property_iterator_next( + grpc_auth_property_iterator *it) { + GRPC_API_TRACE("grpc_auth_property_iterator_next(it=%p)", 1, (it)); + if (it == NULL || it->ctx == NULL) return NULL; + while (it->index == it->ctx->properties.count) { + if (it->ctx->chained == NULL) return NULL; + it->ctx = it->ctx->chained; + it->index = 0; + } + if (it->name == NULL) { + return &it->ctx->properties.array[it->index++]; + } else { + while (it->index < it->ctx->properties.count) { + const grpc_auth_property *prop = &it->ctx->properties.array[it->index++]; + GPR_ASSERT(prop->name != NULL); + if (strcmp(it->name, prop->name) == 0) { + return prop; + } + } + /* We could not find the name, try another round. */ + return grpc_auth_property_iterator_next(it); + } +} + +grpc_auth_property_iterator grpc_auth_context_find_properties_by_name( + const grpc_auth_context *ctx, const char *name) { + grpc_auth_property_iterator it = empty_iterator; + GRPC_API_TRACE("grpc_auth_context_find_properties_by_name(ctx=%p, name=%s)", + 2, (ctx, name)); + if (ctx == NULL || name == NULL) return empty_iterator; + it.ctx = ctx; + it.name = name; + return it; +} + +grpc_auth_property_iterator grpc_auth_context_peer_identity( + const grpc_auth_context *ctx) { + GRPC_API_TRACE("grpc_auth_context_peer_identity(ctx=%p)", 1, (ctx)); + if (ctx == NULL) return empty_iterator; + return grpc_auth_context_find_properties_by_name( + ctx, ctx->peer_identity_property_name); +} + +static void ensure_auth_context_capacity(grpc_auth_context *ctx) { + if (ctx->properties.count == ctx->properties.capacity) { + ctx->properties.capacity = + GPR_MAX(ctx->properties.capacity + 8, ctx->properties.capacity * 2); + ctx->properties.array = (grpc_auth_property *)gpr_realloc( + ctx->properties.array, + ctx->properties.capacity * sizeof(grpc_auth_property)); + } +} + +void grpc_auth_context_add_property(grpc_auth_context *ctx, const char *name, + const char *value, size_t value_length) { + grpc_auth_property *prop; + GRPC_API_TRACE( + "grpc_auth_context_add_property(ctx=%p, name=%s, value=%*.*s, " + "value_length=%lu)", + 6, (ctx, name, (int)value_length, (int)value_length, value, + (unsigned long)value_length)); + ensure_auth_context_capacity(ctx); + prop = &ctx->properties.array[ctx->properties.count++]; + prop->name = gpr_strdup(name); + prop->value = (char *)gpr_malloc(value_length + 1); + memcpy(prop->value, value, value_length); + prop->value[value_length] = '\0'; + prop->value_length = value_length; +} + +void grpc_auth_context_add_cstring_property(grpc_auth_context *ctx, + const char *name, + const char *value) { + grpc_auth_property *prop; + GRPC_API_TRACE( + "grpc_auth_context_add_cstring_property(ctx=%p, name=%s, value=%s)", 3, + (ctx, name, value)); + ensure_auth_context_capacity(ctx); + prop = &ctx->properties.array[ctx->properties.count++]; + prop->name = gpr_strdup(name); + prop->value = gpr_strdup(value); + prop->value_length = strlen(value); +} + +void grpc_auth_property_reset(grpc_auth_property *property) { + gpr_free(property->name); + gpr_free(property->value); + memset(property, 0, sizeof(grpc_auth_property)); +} + +static void auth_context_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { + GRPC_AUTH_CONTEXT_UNREF((grpc_auth_context *)p, "auth_context_pointer_arg"); +} + +static void *auth_context_pointer_arg_copy(void *p) { + return GRPC_AUTH_CONTEXT_REF((grpc_auth_context *)p, + "auth_context_pointer_arg"); +} + +static int auth_context_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable auth_context_pointer_vtable = { + auth_context_pointer_arg_copy, auth_context_pointer_arg_destroy, + auth_context_pointer_cmp}; + +grpc_arg grpc_auth_context_to_arg(grpc_auth_context *p) { + return grpc_channel_arg_pointer_create((char *)GRPC_AUTH_CONTEXT_ARG, p, + &auth_context_pointer_vtable); +} + +grpc_auth_context *grpc_auth_context_from_arg(const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_AUTH_CONTEXT_ARG) != 0) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_AUTH_CONTEXT_ARG); + return NULL; + } + return (grpc_auth_context *)arg->value.pointer.p; +} + +grpc_auth_context *grpc_find_auth_context_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_auth_context *p = grpc_auth_context_from_arg(&args->args[i]); + if (p != NULL) return p; + } + return NULL; +} diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c deleted file mode 100644 index 779300ac07..0000000000 --- a/src/core/lib/security/credentials/composite/composite_credentials.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * - * Copyright 2015-2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/composite/composite_credentials.h" - -#include - -#include "src/core/lib/iomgr/polling_entity.h" -#include "src/core/lib/surface/api_trace.h" - -#include -#include -#include - -/* -- Composite call credentials. -- */ - -typedef struct { - grpc_composite_call_credentials *composite_creds; - size_t creds_index; - grpc_polling_entity *pollent; - grpc_auth_metadata_context auth_md_context; - grpc_credentials_mdelem_array *md_array; - grpc_closure *on_request_metadata; - grpc_closure internal_on_request_metadata; -} grpc_composite_call_credentials_metadata_context; - -static void composite_call_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; - for (size_t i = 0; i < c->inner.num_creds; i++) { - grpc_call_credentials_unref(exec_ctx, c->inner.creds_array[i]); - } - gpr_free(c->inner.creds_array); -} - -static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_composite_call_credentials_metadata_context *ctx = - (grpc_composite_call_credentials_metadata_context *)arg; - if (error == GRPC_ERROR_NONE) { - /* See if we need to get some more metadata. */ - if (ctx->creds_index < ctx->composite_creds->inner.num_creds) { - grpc_call_credentials *inner_creds = - ctx->composite_creds->inner.creds_array[ctx->creds_index++]; - if (grpc_call_credentials_get_request_metadata( - exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, - ctx->md_array, &ctx->internal_on_request_metadata, &error)) { - // Synchronous response, so call ourselves recursively. - composite_call_metadata_cb(exec_ctx, arg, error); - GRPC_ERROR_UNREF(error); - } - return; - } - // We're done! - } - GRPC_CLOSURE_SCHED(exec_ctx, ctx->on_request_metadata, GRPC_ERROR_REF(error)); - gpr_free(ctx); -} - -static bool composite_call_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_polling_entity *pollent, grpc_auth_metadata_context auth_md_context, - grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; - grpc_composite_call_credentials_metadata_context *ctx; - ctx = (grpc_composite_call_credentials_metadata_context *)gpr_zalloc( - sizeof(grpc_composite_call_credentials_metadata_context)); - ctx->composite_creds = c; - ctx->pollent = pollent; - ctx->auth_md_context = auth_md_context; - ctx->md_array = md_array; - ctx->on_request_metadata = on_request_metadata; - GRPC_CLOSURE_INIT(&ctx->internal_on_request_metadata, - composite_call_metadata_cb, ctx, grpc_schedule_on_exec_ctx); - bool synchronous = true; - while (ctx->creds_index < ctx->composite_creds->inner.num_creds) { - grpc_call_credentials *inner_creds = - ctx->composite_creds->inner.creds_array[ctx->creds_index++]; - if (grpc_call_credentials_get_request_metadata( - exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, - ctx->md_array, &ctx->internal_on_request_metadata, error)) { - if (*error != GRPC_ERROR_NONE) break; - } else { - synchronous = false; // Async return. - break; - } - } - if (synchronous) gpr_free(ctx); - return synchronous; -} - -static void composite_call_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; - for (size_t i = 0; i < c->inner.num_creds; ++i) { - grpc_call_credentials_cancel_get_request_metadata( - exec_ctx, c->inner.creds_array[i], md_array, GRPC_ERROR_REF(error)); - } - GRPC_ERROR_UNREF(error); -} - -static grpc_call_credentials_vtable composite_call_credentials_vtable = { - composite_call_destruct, composite_call_get_request_metadata, - composite_call_cancel_get_request_metadata}; - -static grpc_call_credentials_array get_creds_array( - grpc_call_credentials **creds_addr) { - grpc_call_credentials_array result; - grpc_call_credentials *creds = *creds_addr; - result.creds_array = creds_addr; - result.num_creds = 1; - if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) { - result = *grpc_composite_call_credentials_get_credentials(creds); - } - return result; -} - -grpc_call_credentials *grpc_composite_call_credentials_create( - grpc_call_credentials *creds1, grpc_call_credentials *creds2, - void *reserved) { - size_t i; - size_t creds_array_byte_size; - grpc_call_credentials_array creds1_array; - grpc_call_credentials_array creds2_array; - grpc_composite_call_credentials *c; - GRPC_API_TRACE( - "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, " - "reserved=%p)", - 3, (creds1, creds2, reserved)); - GPR_ASSERT(reserved == NULL); - GPR_ASSERT(creds1 != NULL); - GPR_ASSERT(creds2 != NULL); - c = (grpc_composite_call_credentials *)gpr_zalloc( - sizeof(grpc_composite_call_credentials)); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE; - c->base.vtable = &composite_call_credentials_vtable; - gpr_ref_init(&c->base.refcount, 1); - creds1_array = get_creds_array(&creds1); - creds2_array = get_creds_array(&creds2); - c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds; - creds_array_byte_size = c->inner.num_creds * sizeof(grpc_call_credentials *); - c->inner.creds_array = - (grpc_call_credentials **)gpr_zalloc(creds_array_byte_size); - for (i = 0; i < creds1_array.num_creds; i++) { - grpc_call_credentials *cur_creds = creds1_array.creds_array[i]; - c->inner.creds_array[i] = grpc_call_credentials_ref(cur_creds); - } - for (i = 0; i < creds2_array.num_creds; i++) { - grpc_call_credentials *cur_creds = creds2_array.creds_array[i]; - c->inner.creds_array[i + creds1_array.num_creds] = - grpc_call_credentials_ref(cur_creds); - } - return &c->base; -} - -const grpc_call_credentials_array * -grpc_composite_call_credentials_get_credentials(grpc_call_credentials *creds) { - const grpc_composite_call_credentials *c = - (const grpc_composite_call_credentials *)creds; - GPR_ASSERT(strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0); - return &c->inner; -} - -grpc_call_credentials *grpc_credentials_contains_type( - grpc_call_credentials *creds, const char *type, - grpc_call_credentials **composite_creds) { - size_t i; - if (strcmp(creds->type, type) == 0) { - if (composite_creds != NULL) *composite_creds = NULL; - return creds; - } else if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) { - const grpc_call_credentials_array *inner_creds_array = - grpc_composite_call_credentials_get_credentials(creds); - for (i = 0; i < inner_creds_array->num_creds; i++) { - if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) { - if (composite_creds != NULL) *composite_creds = creds; - return inner_creds_array->creds_array[i]; - } - } - } - return NULL; -} - -/* -- Composite channel credentials. -- */ - -static void composite_channel_destruct(grpc_exec_ctx *exec_ctx, - grpc_channel_credentials *creds) { - grpc_composite_channel_credentials *c = - (grpc_composite_channel_credentials *)creds; - grpc_channel_credentials_unref(exec_ctx, c->inner_creds); - grpc_call_credentials_unref(exec_ctx, c->call_creds); -} - -static grpc_security_status composite_channel_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds, - grpc_call_credentials *call_creds, const char *target, - const grpc_channel_args *args, grpc_channel_security_connector **sc, - grpc_channel_args **new_args) { - grpc_composite_channel_credentials *c = - (grpc_composite_channel_credentials *)creds; - grpc_security_status status = GRPC_SECURITY_ERROR; - - GPR_ASSERT(c->inner_creds != NULL && c->call_creds != NULL && - c->inner_creds->vtable != NULL && - c->inner_creds->vtable->create_security_connector != NULL); - /* If we are passed a call_creds, create a call composite to pass it - downstream. */ - if (call_creds != NULL) { - grpc_call_credentials *composite_call_creds = - grpc_composite_call_credentials_create(c->call_creds, call_creds, NULL); - status = c->inner_creds->vtable->create_security_connector( - exec_ctx, c->inner_creds, composite_call_creds, target, args, sc, - new_args); - grpc_call_credentials_unref(exec_ctx, composite_call_creds); - } else { - status = c->inner_creds->vtable->create_security_connector( - exec_ctx, c->inner_creds, c->call_creds, target, args, sc, new_args); - } - return status; -} - -static grpc_channel_credentials * -composite_channel_duplicate_without_call_credentials( - grpc_channel_credentials *creds) { - grpc_composite_channel_credentials *c = - (grpc_composite_channel_credentials *)creds; - return grpc_channel_credentials_ref(c->inner_creds); -} - -static grpc_channel_credentials_vtable composite_channel_credentials_vtable = { - composite_channel_destruct, composite_channel_create_security_connector, - composite_channel_duplicate_without_call_credentials}; - -grpc_channel_credentials *grpc_composite_channel_credentials_create( - grpc_channel_credentials *channel_creds, grpc_call_credentials *call_creds, - void *reserved) { - grpc_composite_channel_credentials *c = - (grpc_composite_channel_credentials *)gpr_zalloc(sizeof(*c)); - GPR_ASSERT(channel_creds != NULL && call_creds != NULL && reserved == NULL); - GRPC_API_TRACE( - "grpc_composite_channel_credentials_create(channel_creds=%p, " - "call_creds=%p, reserved=%p)", - 3, (channel_creds, call_creds, reserved)); - c->base.type = channel_creds->type; - c->base.vtable = &composite_channel_credentials_vtable; - gpr_ref_init(&c->base.refcount, 1); - c->inner_creds = grpc_channel_credentials_ref(channel_creds); - c->call_creds = grpc_call_credentials_ref(call_creds); - return &c->base; -} diff --git a/src/core/lib/security/credentials/composite/composite_credentials.cc b/src/core/lib/security/credentials/composite/composite_credentials.cc new file mode 100644 index 0000000000..779300ac07 --- /dev/null +++ b/src/core/lib/security/credentials/composite/composite_credentials.cc @@ -0,0 +1,267 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/composite/composite_credentials.h" + +#include + +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include + +/* -- Composite call credentials. -- */ + +typedef struct { + grpc_composite_call_credentials *composite_creds; + size_t creds_index; + grpc_polling_entity *pollent; + grpc_auth_metadata_context auth_md_context; + grpc_credentials_mdelem_array *md_array; + grpc_closure *on_request_metadata; + grpc_closure internal_on_request_metadata; +} grpc_composite_call_credentials_metadata_context; + +static void composite_call_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; + for (size_t i = 0; i < c->inner.num_creds; i++) { + grpc_call_credentials_unref(exec_ctx, c->inner.creds_array[i]); + } + gpr_free(c->inner.creds_array); +} + +static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_composite_call_credentials_metadata_context *ctx = + (grpc_composite_call_credentials_metadata_context *)arg; + if (error == GRPC_ERROR_NONE) { + /* See if we need to get some more metadata. */ + if (ctx->creds_index < ctx->composite_creds->inner.num_creds) { + grpc_call_credentials *inner_creds = + ctx->composite_creds->inner.creds_array[ctx->creds_index++]; + if (grpc_call_credentials_get_request_metadata( + exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, + ctx->md_array, &ctx->internal_on_request_metadata, &error)) { + // Synchronous response, so call ourselves recursively. + composite_call_metadata_cb(exec_ctx, arg, error); + GRPC_ERROR_UNREF(error); + } + return; + } + // We're done! + } + GRPC_CLOSURE_SCHED(exec_ctx, ctx->on_request_metadata, GRPC_ERROR_REF(error)); + gpr_free(ctx); +} + +static bool composite_call_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_polling_entity *pollent, grpc_auth_metadata_context auth_md_context, + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; + grpc_composite_call_credentials_metadata_context *ctx; + ctx = (grpc_composite_call_credentials_metadata_context *)gpr_zalloc( + sizeof(grpc_composite_call_credentials_metadata_context)); + ctx->composite_creds = c; + ctx->pollent = pollent; + ctx->auth_md_context = auth_md_context; + ctx->md_array = md_array; + ctx->on_request_metadata = on_request_metadata; + GRPC_CLOSURE_INIT(&ctx->internal_on_request_metadata, + composite_call_metadata_cb, ctx, grpc_schedule_on_exec_ctx); + bool synchronous = true; + while (ctx->creds_index < ctx->composite_creds->inner.num_creds) { + grpc_call_credentials *inner_creds = + ctx->composite_creds->inner.creds_array[ctx->creds_index++]; + if (grpc_call_credentials_get_request_metadata( + exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, + ctx->md_array, &ctx->internal_on_request_metadata, error)) { + if (*error != GRPC_ERROR_NONE) break; + } else { + synchronous = false; // Async return. + break; + } + } + if (synchronous) gpr_free(ctx); + return synchronous; +} + +static void composite_call_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; + for (size_t i = 0; i < c->inner.num_creds; ++i) { + grpc_call_credentials_cancel_get_request_metadata( + exec_ctx, c->inner.creds_array[i], md_array, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable composite_call_credentials_vtable = { + composite_call_destruct, composite_call_get_request_metadata, + composite_call_cancel_get_request_metadata}; + +static grpc_call_credentials_array get_creds_array( + grpc_call_credentials **creds_addr) { + grpc_call_credentials_array result; + grpc_call_credentials *creds = *creds_addr; + result.creds_array = creds_addr; + result.num_creds = 1; + if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) { + result = *grpc_composite_call_credentials_get_credentials(creds); + } + return result; +} + +grpc_call_credentials *grpc_composite_call_credentials_create( + grpc_call_credentials *creds1, grpc_call_credentials *creds2, + void *reserved) { + size_t i; + size_t creds_array_byte_size; + grpc_call_credentials_array creds1_array; + grpc_call_credentials_array creds2_array; + grpc_composite_call_credentials *c; + GRPC_API_TRACE( + "grpc_composite_call_credentials_create(creds1=%p, creds2=%p, " + "reserved=%p)", + 3, (creds1, creds2, reserved)); + GPR_ASSERT(reserved == NULL); + GPR_ASSERT(creds1 != NULL); + GPR_ASSERT(creds2 != NULL); + c = (grpc_composite_call_credentials *)gpr_zalloc( + sizeof(grpc_composite_call_credentials)); + c->base.type = GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE; + c->base.vtable = &composite_call_credentials_vtable; + gpr_ref_init(&c->base.refcount, 1); + creds1_array = get_creds_array(&creds1); + creds2_array = get_creds_array(&creds2); + c->inner.num_creds = creds1_array.num_creds + creds2_array.num_creds; + creds_array_byte_size = c->inner.num_creds * sizeof(grpc_call_credentials *); + c->inner.creds_array = + (grpc_call_credentials **)gpr_zalloc(creds_array_byte_size); + for (i = 0; i < creds1_array.num_creds; i++) { + grpc_call_credentials *cur_creds = creds1_array.creds_array[i]; + c->inner.creds_array[i] = grpc_call_credentials_ref(cur_creds); + } + for (i = 0; i < creds2_array.num_creds; i++) { + grpc_call_credentials *cur_creds = creds2_array.creds_array[i]; + c->inner.creds_array[i + creds1_array.num_creds] = + grpc_call_credentials_ref(cur_creds); + } + return &c->base; +} + +const grpc_call_credentials_array * +grpc_composite_call_credentials_get_credentials(grpc_call_credentials *creds) { + const grpc_composite_call_credentials *c = + (const grpc_composite_call_credentials *)creds; + GPR_ASSERT(strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0); + return &c->inner; +} + +grpc_call_credentials *grpc_credentials_contains_type( + grpc_call_credentials *creds, const char *type, + grpc_call_credentials **composite_creds) { + size_t i; + if (strcmp(creds->type, type) == 0) { + if (composite_creds != NULL) *composite_creds = NULL; + return creds; + } else if (strcmp(creds->type, GRPC_CALL_CREDENTIALS_TYPE_COMPOSITE) == 0) { + const grpc_call_credentials_array *inner_creds_array = + grpc_composite_call_credentials_get_credentials(creds); + for (i = 0; i < inner_creds_array->num_creds; i++) { + if (strcmp(type, inner_creds_array->creds_array[i]->type) == 0) { + if (composite_creds != NULL) *composite_creds = creds; + return inner_creds_array->creds_array[i]; + } + } + } + return NULL; +} + +/* -- Composite channel credentials. -- */ + +static void composite_channel_destruct(grpc_exec_ctx *exec_ctx, + grpc_channel_credentials *creds) { + grpc_composite_channel_credentials *c = + (grpc_composite_channel_credentials *)creds; + grpc_channel_credentials_unref(exec_ctx, c->inner_creds); + grpc_call_credentials_unref(exec_ctx, c->call_creds); +} + +static grpc_security_status composite_channel_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds, + grpc_call_credentials *call_creds, const char *target, + const grpc_channel_args *args, grpc_channel_security_connector **sc, + grpc_channel_args **new_args) { + grpc_composite_channel_credentials *c = + (grpc_composite_channel_credentials *)creds; + grpc_security_status status = GRPC_SECURITY_ERROR; + + GPR_ASSERT(c->inner_creds != NULL && c->call_creds != NULL && + c->inner_creds->vtable != NULL && + c->inner_creds->vtable->create_security_connector != NULL); + /* If we are passed a call_creds, create a call composite to pass it + downstream. */ + if (call_creds != NULL) { + grpc_call_credentials *composite_call_creds = + grpc_composite_call_credentials_create(c->call_creds, call_creds, NULL); + status = c->inner_creds->vtable->create_security_connector( + exec_ctx, c->inner_creds, composite_call_creds, target, args, sc, + new_args); + grpc_call_credentials_unref(exec_ctx, composite_call_creds); + } else { + status = c->inner_creds->vtable->create_security_connector( + exec_ctx, c->inner_creds, c->call_creds, target, args, sc, new_args); + } + return status; +} + +static grpc_channel_credentials * +composite_channel_duplicate_without_call_credentials( + grpc_channel_credentials *creds) { + grpc_composite_channel_credentials *c = + (grpc_composite_channel_credentials *)creds; + return grpc_channel_credentials_ref(c->inner_creds); +} + +static grpc_channel_credentials_vtable composite_channel_credentials_vtable = { + composite_channel_destruct, composite_channel_create_security_connector, + composite_channel_duplicate_without_call_credentials}; + +grpc_channel_credentials *grpc_composite_channel_credentials_create( + grpc_channel_credentials *channel_creds, grpc_call_credentials *call_creds, + void *reserved) { + grpc_composite_channel_credentials *c = + (grpc_composite_channel_credentials *)gpr_zalloc(sizeof(*c)); + GPR_ASSERT(channel_creds != NULL && call_creds != NULL && reserved == NULL); + GRPC_API_TRACE( + "grpc_composite_channel_credentials_create(channel_creds=%p, " + "call_creds=%p, reserved=%p)", + 3, (channel_creds, call_creds, reserved)); + c->base.type = channel_creds->type; + c->base.vtable = &composite_channel_credentials_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->inner_creds = grpc_channel_credentials_ref(channel_creds); + c->call_creds = grpc_call_credentials_ref(call_creds); + return &c->base; +} diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c deleted file mode 100644 index ebbf350865..0000000000 --- a/src/core/lib/security/credentials/credentials.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/credentials.h" - -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/http/httpcli.h" -#include "src/core/lib/http/parser.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/json/json.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" - -#include -#include -#include -#include -#include - -/* -- Common. -- */ - -grpc_credentials_metadata_request *grpc_credentials_metadata_request_create( - grpc_call_credentials *creds) { - grpc_credentials_metadata_request *r = - (grpc_credentials_metadata_request *)gpr_zalloc( - sizeof(grpc_credentials_metadata_request)); - r->creds = grpc_call_credentials_ref(creds); - return r; -} - -void grpc_credentials_metadata_request_destroy( - grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *r) { - grpc_call_credentials_unref(exec_ctx, r->creds); - grpc_http_response_destroy(&r->response); - gpr_free(r); -} - -grpc_channel_credentials *grpc_channel_credentials_ref( - grpc_channel_credentials *creds) { - if (creds == NULL) return NULL; - gpr_ref(&creds->refcount); - return creds; -} - -void grpc_channel_credentials_unref(grpc_exec_ctx *exec_ctx, - grpc_channel_credentials *creds) { - if (creds == NULL) return; - if (gpr_unref(&creds->refcount)) { - if (creds->vtable->destruct != NULL) { - creds->vtable->destruct(exec_ctx, creds); - } - gpr_free(creds); - } -} - -void grpc_channel_credentials_release(grpc_channel_credentials *creds) { - GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_channel_credentials_unref(&exec_ctx, creds); - grpc_exec_ctx_finish(&exec_ctx); -} - -grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) { - if (creds == NULL) return NULL; - gpr_ref(&creds->refcount); - return creds; -} - -void grpc_call_credentials_unref(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - if (creds == NULL) return; - if (gpr_unref(&creds->refcount)) { - if (creds->vtable->destruct != NULL) { - creds->vtable->destruct(exec_ctx, creds); - } - gpr_free(creds); - } -} - -void grpc_call_credentials_release(grpc_call_credentials *creds) { - GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call_credentials_unref(&exec_ctx, creds); - grpc_exec_ctx_finish(&exec_ctx); -} - -bool grpc_call_credentials_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, - grpc_error **error) { - if (creds == NULL || creds->vtable->get_request_metadata == NULL) { - return true; - } - return creds->vtable->get_request_metadata( - exec_ctx, creds, pollent, context, md_array, on_request_metadata, error); -} - -void grpc_call_credentials_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - if (creds == NULL || creds->vtable->cancel_get_request_metadata == NULL) { - return; - } - creds->vtable->cancel_get_request_metadata(exec_ctx, creds, md_array, error); -} - -grpc_security_status grpc_channel_credentials_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_channel_credentials *channel_creds, - const char *target, const grpc_channel_args *args, - grpc_channel_security_connector **sc, grpc_channel_args **new_args) { - *new_args = NULL; - if (channel_creds == NULL) { - return GRPC_SECURITY_ERROR; - } - GPR_ASSERT(channel_creds->vtable->create_security_connector != NULL); - return channel_creds->vtable->create_security_connector( - exec_ctx, channel_creds, NULL, target, args, sc, new_args); -} - -grpc_channel_credentials * -grpc_channel_credentials_duplicate_without_call_credentials( - grpc_channel_credentials *channel_creds) { - if (channel_creds != NULL && channel_creds->vtable != NULL && - channel_creds->vtable->duplicate_without_call_credentials != NULL) { - return channel_creds->vtable->duplicate_without_call_credentials( - channel_creds); - } else { - return grpc_channel_credentials_ref(channel_creds); - } -} - -static void credentials_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { - grpc_channel_credentials_unref(exec_ctx, (grpc_channel_credentials *)p); -} - -static void *credentials_pointer_arg_copy(void *p) { - return grpc_channel_credentials_ref((grpc_channel_credentials *)p); -} - -static int credentials_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); } - -static const grpc_arg_pointer_vtable credentials_pointer_vtable = { - credentials_pointer_arg_copy, credentials_pointer_arg_destroy, - credentials_pointer_cmp}; - -grpc_arg grpc_channel_credentials_to_arg( - grpc_channel_credentials *credentials) { - return grpc_channel_arg_pointer_create((char *)GRPC_ARG_CHANNEL_CREDENTIALS, - credentials, - &credentials_pointer_vtable); -} - -grpc_channel_credentials *grpc_channel_credentials_from_arg( - const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_ARG_CHANNEL_CREDENTIALS)) return NULL; - if (arg->type != GRPC_ARG_POINTER) { - gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_ARG_CHANNEL_CREDENTIALS); - return NULL; - } - return (grpc_channel_credentials *)arg->value.pointer.p; -} - -grpc_channel_credentials *grpc_channel_credentials_find_in_args( - const grpc_channel_args *args) { - size_t i; - if (args == NULL) return NULL; - for (i = 0; i < args->num_args; i++) { - grpc_channel_credentials *credentials = - grpc_channel_credentials_from_arg(&args->args[i]); - if (credentials != NULL) return credentials; - } - return NULL; -} - -grpc_server_credentials *grpc_server_credentials_ref( - grpc_server_credentials *creds) { - if (creds == NULL) return NULL; - gpr_ref(&creds->refcount); - return creds; -} - -void grpc_server_credentials_unref(grpc_exec_ctx *exec_ctx, - grpc_server_credentials *creds) { - if (creds == NULL) return; - if (gpr_unref(&creds->refcount)) { - if (creds->vtable->destruct != NULL) { - creds->vtable->destruct(exec_ctx, creds); - } - if (creds->processor.destroy != NULL && creds->processor.state != NULL) { - creds->processor.destroy(creds->processor.state); - } - gpr_free(creds); - } -} - -void grpc_server_credentials_release(grpc_server_credentials *creds) { - GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_server_credentials_unref(&exec_ctx, creds); - grpc_exec_ctx_finish(&exec_ctx); -} - -grpc_security_status grpc_server_credentials_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds, - grpc_server_security_connector **sc) { - if (creds == NULL || creds->vtable->create_security_connector == NULL) { - gpr_log(GPR_ERROR, "Server credentials cannot create security context."); - return GRPC_SECURITY_ERROR; - } - return creds->vtable->create_security_connector(exec_ctx, creds, sc); -} - -void grpc_server_credentials_set_auth_metadata_processor( - grpc_server_credentials *creds, grpc_auth_metadata_processor processor) { - GRPC_API_TRACE( - "grpc_server_credentials_set_auth_metadata_processor(" - "creds=%p, " - "processor=grpc_auth_metadata_processor { process: %p, state: %p })", - 3, (creds, (void *)(intptr_t)processor.process, processor.state)); - if (creds == NULL) return; - if (creds->processor.destroy != NULL && creds->processor.state != NULL) { - creds->processor.destroy(creds->processor.state); - } - creds->processor = processor; -} - -static void server_credentials_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, - void *p) { - grpc_server_credentials_unref(exec_ctx, (grpc_server_credentials *)p); -} - -static void *server_credentials_pointer_arg_copy(void *p) { - return grpc_server_credentials_ref((grpc_server_credentials *)p); -} - -static int server_credentials_pointer_cmp(void *a, void *b) { - return GPR_ICMP(a, b); -} - -static const grpc_arg_pointer_vtable cred_ptr_vtable = { - server_credentials_pointer_arg_copy, server_credentials_pointer_arg_destroy, - server_credentials_pointer_cmp}; - -grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) { - return grpc_channel_arg_pointer_create((char *)GRPC_SERVER_CREDENTIALS_ARG, p, - &cred_ptr_vtable); -} - -grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return NULL; - if (arg->type != GRPC_ARG_POINTER) { - gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_SERVER_CREDENTIALS_ARG); - return NULL; - } - return (grpc_server_credentials *)arg->value.pointer.p; -} - -grpc_server_credentials *grpc_find_server_credentials_in_args( - const grpc_channel_args *args) { - size_t i; - if (args == NULL) return NULL; - for (i = 0; i < args->num_args; i++) { - grpc_server_credentials *p = - grpc_server_credentials_from_arg(&args->args[i]); - if (p != NULL) return p; - } - return NULL; -} diff --git a/src/core/lib/security/credentials/credentials.cc b/src/core/lib/security/credentials/credentials.cc new file mode 100644 index 0000000000..ebbf350865 --- /dev/null +++ b/src/core/lib/security/credentials/credentials.cc @@ -0,0 +1,289 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/credentials.h" + +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/json/json.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include +#include +#include + +/* -- Common. -- */ + +grpc_credentials_metadata_request *grpc_credentials_metadata_request_create( + grpc_call_credentials *creds) { + grpc_credentials_metadata_request *r = + (grpc_credentials_metadata_request *)gpr_zalloc( + sizeof(grpc_credentials_metadata_request)); + r->creds = grpc_call_credentials_ref(creds); + return r; +} + +void grpc_credentials_metadata_request_destroy( + grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *r) { + grpc_call_credentials_unref(exec_ctx, r->creds); + grpc_http_response_destroy(&r->response); + gpr_free(r); +} + +grpc_channel_credentials *grpc_channel_credentials_ref( + grpc_channel_credentials *creds) { + if (creds == NULL) return NULL; + gpr_ref(&creds->refcount); + return creds; +} + +void grpc_channel_credentials_unref(grpc_exec_ctx *exec_ctx, + grpc_channel_credentials *creds) { + if (creds == NULL) return; + if (gpr_unref(&creds->refcount)) { + if (creds->vtable->destruct != NULL) { + creds->vtable->destruct(exec_ctx, creds); + } + gpr_free(creds); + } +} + +void grpc_channel_credentials_release(grpc_channel_credentials *creds) { + GRPC_API_TRACE("grpc_channel_credentials_release(creds=%p)", 1, (creds)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_channel_credentials_unref(&exec_ctx, creds); + grpc_exec_ctx_finish(&exec_ctx); +} + +grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds) { + if (creds == NULL) return NULL; + gpr_ref(&creds->refcount); + return creds; +} + +void grpc_call_credentials_unref(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + if (creds == NULL) return; + if (gpr_unref(&creds->refcount)) { + if (creds->vtable->destruct != NULL) { + creds->vtable->destruct(exec_ctx, creds); + } + gpr_free(creds); + } +} + +void grpc_call_credentials_release(grpc_call_credentials *creds) { + GRPC_API_TRACE("grpc_call_credentials_release(creds=%p)", 1, (creds)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call_credentials_unref(&exec_ctx, creds); + grpc_exec_ctx_finish(&exec_ctx); +} + +bool grpc_call_credentials_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_polling_entity *pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { + if (creds == NULL || creds->vtable->get_request_metadata == NULL) { + return true; + } + return creds->vtable->get_request_metadata( + exec_ctx, creds, pollent, context, md_array, on_request_metadata, error); +} + +void grpc_call_credentials_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + if (creds == NULL || creds->vtable->cancel_get_request_metadata == NULL) { + return; + } + creds->vtable->cancel_get_request_metadata(exec_ctx, creds, md_array, error); +} + +grpc_security_status grpc_channel_credentials_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_channel_credentials *channel_creds, + const char *target, const grpc_channel_args *args, + grpc_channel_security_connector **sc, grpc_channel_args **new_args) { + *new_args = NULL; + if (channel_creds == NULL) { + return GRPC_SECURITY_ERROR; + } + GPR_ASSERT(channel_creds->vtable->create_security_connector != NULL); + return channel_creds->vtable->create_security_connector( + exec_ctx, channel_creds, NULL, target, args, sc, new_args); +} + +grpc_channel_credentials * +grpc_channel_credentials_duplicate_without_call_credentials( + grpc_channel_credentials *channel_creds) { + if (channel_creds != NULL && channel_creds->vtable != NULL && + channel_creds->vtable->duplicate_without_call_credentials != NULL) { + return channel_creds->vtable->duplicate_without_call_credentials( + channel_creds); + } else { + return grpc_channel_credentials_ref(channel_creds); + } +} + +static void credentials_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { + grpc_channel_credentials_unref(exec_ctx, (grpc_channel_credentials *)p); +} + +static void *credentials_pointer_arg_copy(void *p) { + return grpc_channel_credentials_ref((grpc_channel_credentials *)p); +} + +static int credentials_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable credentials_pointer_vtable = { + credentials_pointer_arg_copy, credentials_pointer_arg_destroy, + credentials_pointer_cmp}; + +grpc_arg grpc_channel_credentials_to_arg( + grpc_channel_credentials *credentials) { + return grpc_channel_arg_pointer_create((char *)GRPC_ARG_CHANNEL_CREDENTIALS, + credentials, + &credentials_pointer_vtable); +} + +grpc_channel_credentials *grpc_channel_credentials_from_arg( + const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_ARG_CHANNEL_CREDENTIALS)) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_ARG_CHANNEL_CREDENTIALS); + return NULL; + } + return (grpc_channel_credentials *)arg->value.pointer.p; +} + +grpc_channel_credentials *grpc_channel_credentials_find_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_channel_credentials *credentials = + grpc_channel_credentials_from_arg(&args->args[i]); + if (credentials != NULL) return credentials; + } + return NULL; +} + +grpc_server_credentials *grpc_server_credentials_ref( + grpc_server_credentials *creds) { + if (creds == NULL) return NULL; + gpr_ref(&creds->refcount); + return creds; +} + +void grpc_server_credentials_unref(grpc_exec_ctx *exec_ctx, + grpc_server_credentials *creds) { + if (creds == NULL) return; + if (gpr_unref(&creds->refcount)) { + if (creds->vtable->destruct != NULL) { + creds->vtable->destruct(exec_ctx, creds); + } + if (creds->processor.destroy != NULL && creds->processor.state != NULL) { + creds->processor.destroy(creds->processor.state); + } + gpr_free(creds); + } +} + +void grpc_server_credentials_release(grpc_server_credentials *creds) { + GRPC_API_TRACE("grpc_server_credentials_release(creds=%p)", 1, (creds)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_server_credentials_unref(&exec_ctx, creds); + grpc_exec_ctx_finish(&exec_ctx); +} + +grpc_security_status grpc_server_credentials_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds, + grpc_server_security_connector **sc) { + if (creds == NULL || creds->vtable->create_security_connector == NULL) { + gpr_log(GPR_ERROR, "Server credentials cannot create security context."); + return GRPC_SECURITY_ERROR; + } + return creds->vtable->create_security_connector(exec_ctx, creds, sc); +} + +void grpc_server_credentials_set_auth_metadata_processor( + grpc_server_credentials *creds, grpc_auth_metadata_processor processor) { + GRPC_API_TRACE( + "grpc_server_credentials_set_auth_metadata_processor(" + "creds=%p, " + "processor=grpc_auth_metadata_processor { process: %p, state: %p })", + 3, (creds, (void *)(intptr_t)processor.process, processor.state)); + if (creds == NULL) return; + if (creds->processor.destroy != NULL && creds->processor.state != NULL) { + creds->processor.destroy(creds->processor.state); + } + creds->processor = processor; +} + +static void server_credentials_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, + void *p) { + grpc_server_credentials_unref(exec_ctx, (grpc_server_credentials *)p); +} + +static void *server_credentials_pointer_arg_copy(void *p) { + return grpc_server_credentials_ref((grpc_server_credentials *)p); +} + +static int server_credentials_pointer_cmp(void *a, void *b) { + return GPR_ICMP(a, b); +} + +static const grpc_arg_pointer_vtable cred_ptr_vtable = { + server_credentials_pointer_arg_copy, server_credentials_pointer_arg_destroy, + server_credentials_pointer_cmp}; + +grpc_arg grpc_server_credentials_to_arg(grpc_server_credentials *p) { + return grpc_channel_arg_pointer_create((char *)GRPC_SERVER_CREDENTIALS_ARG, p, + &cred_ptr_vtable); +} + +grpc_server_credentials *grpc_server_credentials_from_arg(const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_SERVER_CREDENTIALS_ARG) != 0) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_SERVER_CREDENTIALS_ARG); + return NULL; + } + return (grpc_server_credentials *)arg->value.pointer.p; +} + +grpc_server_credentials *grpc_find_server_credentials_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_server_credentials *p = + grpc_server_credentials_from_arg(&args->args[i]); + if (p != NULL) return p; + } + return NULL; +} diff --git a/src/core/lib/security/credentials/credentials_metadata.c b/src/core/lib/security/credentials/credentials_metadata.c deleted file mode 100644 index 5ba98bda4e..0000000000 --- a/src/core/lib/security/credentials/credentials_metadata.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/credentials.h" - -#include - -#include - -#include "src/core/lib/slice/slice_internal.h" - -static void mdelem_list_ensure_capacity(grpc_credentials_mdelem_array *list, - size_t additional_space_needed) { - size_t target_size = list->size + additional_space_needed; - // Find the next power of two greater than the target size (i.e., - // whenever we add more space, we double what we already have). - size_t new_size = 2; - while (new_size < target_size) { - new_size *= 2; - } - list->md = - (grpc_mdelem *)gpr_realloc(list->md, sizeof(grpc_mdelem) * new_size); -} - -void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array *list, - grpc_mdelem md) { - mdelem_list_ensure_capacity(list, 1); - list->md[list->size++] = GRPC_MDELEM_REF(md); -} - -void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array *dst, - grpc_credentials_mdelem_array *src) { - mdelem_list_ensure_capacity(dst, src->size); - for (size_t i = 0; i < src->size; ++i) { - dst->md[dst->size++] = GRPC_MDELEM_REF(src->md[i]); - } -} - -void grpc_credentials_mdelem_array_destroy( - grpc_exec_ctx *exec_ctx, grpc_credentials_mdelem_array *list) { - for (size_t i = 0; i < list->size; ++i) { - GRPC_MDELEM_UNREF(exec_ctx, list->md[i]); - } - gpr_free(list->md); -} diff --git a/src/core/lib/security/credentials/credentials_metadata.cc b/src/core/lib/security/credentials/credentials_metadata.cc new file mode 100644 index 0000000000..5ba98bda4e --- /dev/null +++ b/src/core/lib/security/credentials/credentials_metadata.cc @@ -0,0 +1,60 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/credentials.h" + +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" + +static void mdelem_list_ensure_capacity(grpc_credentials_mdelem_array *list, + size_t additional_space_needed) { + size_t target_size = list->size + additional_space_needed; + // Find the next power of two greater than the target size (i.e., + // whenever we add more space, we double what we already have). + size_t new_size = 2; + while (new_size < target_size) { + new_size *= 2; + } + list->md = + (grpc_mdelem *)gpr_realloc(list->md, sizeof(grpc_mdelem) * new_size); +} + +void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array *list, + grpc_mdelem md) { + mdelem_list_ensure_capacity(list, 1); + list->md[list->size++] = GRPC_MDELEM_REF(md); +} + +void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array *dst, + grpc_credentials_mdelem_array *src) { + mdelem_list_ensure_capacity(dst, src->size); + for (size_t i = 0; i < src->size; ++i) { + dst->md[dst->size++] = GRPC_MDELEM_REF(src->md[i]); + } +} + +void grpc_credentials_mdelem_array_destroy( + grpc_exec_ctx *exec_ctx, grpc_credentials_mdelem_array *list) { + for (size_t i = 0; i < list->size; ++i) { + GRPC_MDELEM_UNREF(exec_ctx, list->md[i]); + } + gpr_free(list->md); +} diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c deleted file mode 100644 index 795ca0660a..0000000000 --- a/src/core/lib/security/credentials/fake/fake_credentials.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/fake/fake_credentials.h" - -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/support/string.h" - -/* -- Fake transport security credentials. -- */ - -#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ - "grpc.fake_security.expected_targets" - -static grpc_security_status fake_transport_security_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c, - grpc_call_credentials *call_creds, const char *target, - const grpc_channel_args *args, grpc_channel_security_connector **sc, - grpc_channel_args **new_args) { - *sc = grpc_fake_channel_security_connector_create(call_creds, target, args); - return GRPC_SECURITY_OK; -} - -static grpc_security_status -fake_transport_security_server_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_server_credentials *c, - grpc_server_security_connector **sc) { - *sc = grpc_fake_server_security_connector_create(); - return GRPC_SECURITY_OK; -} - -static grpc_channel_credentials_vtable - fake_transport_security_credentials_vtable = { - NULL, fake_transport_security_create_security_connector, NULL}; - -static grpc_server_credentials_vtable - fake_transport_security_server_credentials_vtable = { - NULL, fake_transport_security_server_create_security_connector}; - -grpc_channel_credentials *grpc_fake_transport_security_credentials_create( - void) { - grpc_channel_credentials *c = - (grpc_channel_credentials *)gpr_zalloc(sizeof(grpc_channel_credentials)); - c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; - c->vtable = &fake_transport_security_credentials_vtable; - gpr_ref_init(&c->refcount, 1); - return c; -} - -grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( - void) { - grpc_server_credentials *c = - (grpc_server_credentials *)gpr_malloc(sizeof(grpc_server_credentials)); - memset(c, 0, sizeof(grpc_server_credentials)); - c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; - gpr_ref_init(&c->refcount, 1); - c->vtable = &fake_transport_security_server_credentials_vtable; - return c; -} - -grpc_arg grpc_fake_transport_expected_targets_arg(char *expected_targets) { - return grpc_channel_arg_string_create( - (char *)GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS, expected_targets); -} - -const char *grpc_fake_transport_get_expected_targets( - const grpc_channel_args *args) { - const grpc_arg *expected_target_arg = - grpc_channel_args_find(args, GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS); - if (expected_target_arg != NULL && - expected_target_arg->type == GRPC_ARG_STRING) { - return expected_target_arg->value.string; - } - return NULL; -} - -/* -- Metadata-only test credentials. -- */ - -static void md_only_test_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; - GRPC_MDELEM_UNREF(exec_ctx, c->md); -} - -static bool md_only_test_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; - grpc_credentials_mdelem_array_add(md_array, c->md); - if (c->is_async) { - GRPC_CLOSURE_SCHED(exec_ctx, on_request_metadata, GRPC_ERROR_NONE); - return false; - } - return true; -} - -static void md_only_test_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - GRPC_ERROR_UNREF(error); -} - -static grpc_call_credentials_vtable md_only_test_vtable = { - md_only_test_destruct, md_only_test_get_request_metadata, - md_only_test_cancel_get_request_metadata}; - -grpc_call_credentials *grpc_md_only_test_credentials_create( - grpc_exec_ctx *exec_ctx, const char *md_key, const char *md_value, - bool is_async) { - grpc_md_only_test_credentials *c = - (grpc_md_only_test_credentials *)gpr_zalloc( - sizeof(grpc_md_only_test_credentials)); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; - c->base.vtable = &md_only_test_vtable; - gpr_ref_init(&c->base.refcount, 1); - c->md = - grpc_mdelem_from_slices(exec_ctx, grpc_slice_from_copied_string(md_key), - grpc_slice_from_copied_string(md_value)); - c->is_async = is_async; - return &c->base; -} diff --git a/src/core/lib/security/credentials/fake/fake_credentials.cc b/src/core/lib/security/credentials/fake/fake_credentials.cc new file mode 100644 index 0000000000..795ca0660a --- /dev/null +++ b/src/core/lib/security/credentials/fake/fake_credentials.cc @@ -0,0 +1,144 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/fake/fake_credentials.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/support/string.h" + +/* -- Fake transport security credentials. -- */ + +#define GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS \ + "grpc.fake_security.expected_targets" + +static grpc_security_status fake_transport_security_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_channel_credentials *c, + grpc_call_credentials *call_creds, const char *target, + const grpc_channel_args *args, grpc_channel_security_connector **sc, + grpc_channel_args **new_args) { + *sc = grpc_fake_channel_security_connector_create(call_creds, target, args); + return GRPC_SECURITY_OK; +} + +static grpc_security_status +fake_transport_security_server_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_server_credentials *c, + grpc_server_security_connector **sc) { + *sc = grpc_fake_server_security_connector_create(); + return GRPC_SECURITY_OK; +} + +static grpc_channel_credentials_vtable + fake_transport_security_credentials_vtable = { + NULL, fake_transport_security_create_security_connector, NULL}; + +static grpc_server_credentials_vtable + fake_transport_security_server_credentials_vtable = { + NULL, fake_transport_security_server_create_security_connector}; + +grpc_channel_credentials *grpc_fake_transport_security_credentials_create( + void) { + grpc_channel_credentials *c = + (grpc_channel_credentials *)gpr_zalloc(sizeof(grpc_channel_credentials)); + c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; + c->vtable = &fake_transport_security_credentials_vtable; + gpr_ref_init(&c->refcount, 1); + return c; +} + +grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( + void) { + grpc_server_credentials *c = + (grpc_server_credentials *)gpr_malloc(sizeof(grpc_server_credentials)); + memset(c, 0, sizeof(grpc_server_credentials)); + c->type = GRPC_CHANNEL_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY; + gpr_ref_init(&c->refcount, 1); + c->vtable = &fake_transport_security_server_credentials_vtable; + return c; +} + +grpc_arg grpc_fake_transport_expected_targets_arg(char *expected_targets) { + return grpc_channel_arg_string_create( + (char *)GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS, expected_targets); +} + +const char *grpc_fake_transport_get_expected_targets( + const grpc_channel_args *args) { + const grpc_arg *expected_target_arg = + grpc_channel_args_find(args, GRPC_ARG_FAKE_SECURITY_EXPECTED_TARGETS); + if (expected_target_arg != NULL && + expected_target_arg->type == GRPC_ARG_STRING) { + return expected_target_arg->value.string; + } + return NULL; +} + +/* -- Metadata-only test credentials. -- */ + +static void md_only_test_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; + GRPC_MDELEM_UNREF(exec_ctx, c->md); +} + +static bool md_only_test_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_polling_entity *pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; + grpc_credentials_mdelem_array_add(md_array, c->md); + if (c->is_async) { + GRPC_CLOSURE_SCHED(exec_ctx, on_request_metadata, GRPC_ERROR_NONE); + return false; + } + return true; +} + +static void md_only_test_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable md_only_test_vtable = { + md_only_test_destruct, md_only_test_get_request_metadata, + md_only_test_cancel_get_request_metadata}; + +grpc_call_credentials *grpc_md_only_test_credentials_create( + grpc_exec_ctx *exec_ctx, const char *md_key, const char *md_value, + bool is_async) { + grpc_md_only_test_credentials *c = + (grpc_md_only_test_credentials *)gpr_zalloc( + sizeof(grpc_md_only_test_credentials)); + c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; + c->base.vtable = &md_only_test_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->md = + grpc_mdelem_from_slices(exec_ctx, grpc_slice_from_copied_string(md_key), + grpc_slice_from_copied_string(md_value)); + c->is_async = is_async; + return &c->base; +} diff --git a/src/core/lib/security/credentials/google_default/credentials_generic.c b/src/core/lib/security/credentials/google_default/credentials_generic.c deleted file mode 100644 index 4f79718f3d..0000000000 --- a/src/core/lib/security/credentials/google_default/credentials_generic.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/google_default/google_default_credentials.h" - -#include -#include -#include - -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" - -char *grpc_get_well_known_google_credentials_file_path_impl(void) { - char *result = NULL; - char *base = gpr_getenv(GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR); - if (base == NULL) { - gpr_log(GPR_ERROR, "Could not get " GRPC_GOOGLE_CREDENTIALS_ENV_VAR - " environment variable."); - return NULL; - } - gpr_asprintf(&result, "%s/%s", base, GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX); - gpr_free(base); - return result; -} diff --git a/src/core/lib/security/credentials/google_default/credentials_generic.cc b/src/core/lib/security/credentials/google_default/credentials_generic.cc new file mode 100644 index 0000000000..4f79718f3d --- /dev/null +++ b/src/core/lib/security/credentials/google_default/credentials_generic.cc @@ -0,0 +1,39 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/google_default/google_default_credentials.h" + +#include +#include +#include + +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +char *grpc_get_well_known_google_credentials_file_path_impl(void) { + char *result = NULL; + char *base = gpr_getenv(GRPC_GOOGLE_CREDENTIALS_PATH_ENV_VAR); + if (base == NULL) { + gpr_log(GPR_ERROR, "Could not get " GRPC_GOOGLE_CREDENTIALS_ENV_VAR + " environment variable."); + return NULL; + } + gpr_asprintf(&result, "%s/%s", base, GRPC_GOOGLE_CREDENTIALS_PATH_SUFFIX); + gpr_free(base); + return result; +} diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.c b/src/core/lib/security/credentials/google_default/google_default_credentials.c deleted file mode 100644 index 11a06e994f..0000000000 --- a/src/core/lib/security/credentials/google_default/google_default_credentials.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/credentials.h" - -#include - -#include -#include -#include - -#include "src/core/lib/http/httpcli.h" -#include "src/core/lib/http/parser.h" -#include "src/core/lib/iomgr/load_file.h" -#include "src/core/lib/iomgr/polling_entity.h" -#include "src/core/lib/security/credentials/jwt/jwt_credentials.h" -#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" - -/* -- Constants. -- */ - -#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal" - -/* -- Default credentials. -- */ - -static grpc_channel_credentials *default_credentials = NULL; -static int compute_engine_detection_done = 0; -static gpr_mu g_state_mu; -static gpr_mu *g_polling_mu; -static gpr_once g_once = GPR_ONCE_INIT; - -static void init_default_credentials(void) { gpr_mu_init(&g_state_mu); } - -typedef struct { - grpc_polling_entity pollent; - int is_done; - int success; - grpc_http_response response; -} compute_engine_detector; - -static void on_compute_engine_detection_http_response(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_error *error) { - compute_engine_detector *detector = (compute_engine_detector *)user_data; - if (error == GRPC_ERROR_NONE && detector->response.status == 200 && - detector->response.hdr_count > 0) { - /* Internet providers can return a generic response to all requests, so - it is necessary to check that metadata header is present also. */ - size_t i; - for (i = 0; i < detector->response.hdr_count; i++) { - grpc_http_header *header = &detector->response.hdrs[i]; - if (strcmp(header->key, "Metadata-Flavor") == 0 && - strcmp(header->value, "Google") == 0) { - detector->success = 1; - break; - } - } - } - gpr_mu_lock(g_polling_mu); - detector->is_done = 1; - GRPC_LOG_IF_ERROR( - "Pollset kick", - grpc_pollset_kick(exec_ctx, - grpc_polling_entity_pollset(&detector->pollent), NULL)); - gpr_mu_unlock(g_polling_mu); -} - -static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, grpc_error *e) { - grpc_pollset_destroy(exec_ctx, (grpc_pollset *)p); -} - -static int is_stack_running_on_compute_engine(grpc_exec_ctx *exec_ctx) { - compute_engine_detector detector; - grpc_httpcli_request request; - grpc_httpcli_context context; - grpc_closure destroy_closure; - - /* The http call is local. If it takes more than one sec, it is for sure not - on compute engine. */ - gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN); - - grpc_pollset *pollset = (grpc_pollset *)gpr_zalloc(grpc_pollset_size()); - grpc_pollset_init(pollset, &g_polling_mu); - detector.pollent = grpc_polling_entity_create_from_pollset(pollset); - detector.is_done = 0; - detector.success = 0; - - memset(&detector.response, 0, sizeof(detector.response)); - memset(&request, 0, sizeof(grpc_httpcli_request)); - request.host = (char *)GRPC_COMPUTE_ENGINE_DETECTION_HOST; - request.http.path = (char *)"/"; - - grpc_httpcli_context_init(&context); - - grpc_resource_quota *resource_quota = - grpc_resource_quota_create("google_default_credentials"); - grpc_httpcli_get( - exec_ctx, &context, &detector.pollent, resource_quota, &request, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay), - GRPC_CLOSURE_CREATE(on_compute_engine_detection_http_response, &detector, - grpc_schedule_on_exec_ctx), - &detector.response); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - - grpc_exec_ctx_flush(exec_ctx); - - /* Block until we get the response. This is not ideal but this should only be - called once for the lifetime of the process by the default credentials. */ - gpr_mu_lock(g_polling_mu); - while (!detector.is_done) { - grpc_pollset_worker *worker = NULL; - if (!GRPC_LOG_IF_ERROR( - "pollset_work", - grpc_pollset_work(exec_ctx, - grpc_polling_entity_pollset(&detector.pollent), - &worker, gpr_now(GPR_CLOCK_MONOTONIC), - gpr_inf_future(GPR_CLOCK_MONOTONIC)))) { - detector.is_done = 1; - detector.success = 0; - } - } - gpr_mu_unlock(g_polling_mu); - - grpc_httpcli_context_destroy(exec_ctx, &context); - GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset, - grpc_polling_entity_pollset(&detector.pollent), - grpc_schedule_on_exec_ctx); - grpc_pollset_shutdown(exec_ctx, - grpc_polling_entity_pollset(&detector.pollent), - &destroy_closure); - g_polling_mu = NULL; - grpc_exec_ctx_flush(exec_ctx); - - gpr_free(grpc_polling_entity_pollset(&detector.pollent)); - grpc_http_response_destroy(&detector.response); - - return detector.success; -} - -/* Takes ownership of creds_path if not NULL. */ -static grpc_error *create_default_creds_from_path( - grpc_exec_ctx *exec_ctx, char *creds_path, grpc_call_credentials **creds) { - grpc_json *json = NULL; - grpc_auth_json_key key; - grpc_auth_refresh_token token; - grpc_call_credentials *result = NULL; - grpc_slice creds_data = grpc_empty_slice(); - grpc_error *error = GRPC_ERROR_NONE; - if (creds_path == NULL) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("creds_path unset"); - goto end; - } - error = grpc_load_file(creds_path, 0, &creds_data); - if (error != GRPC_ERROR_NONE) { - goto end; - } - json = grpc_json_parse_string_with_len( - (char *)GRPC_SLICE_START_PTR(creds_data), GRPC_SLICE_LENGTH(creds_data)); - if (json == NULL) { - error = grpc_error_set_str( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to parse JSON"), - GRPC_ERROR_STR_RAW_BYTES, grpc_slice_ref_internal(creds_data)); - goto end; - } - - /* First, try an auth json key. */ - key = grpc_auth_json_key_create_from_json(json); - if (grpc_auth_json_key_is_valid(&key)) { - result = - grpc_service_account_jwt_access_credentials_create_from_auth_json_key( - exec_ctx, key, grpc_max_auth_token_lifetime()); - if (result == NULL) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "grpc_service_account_jwt_access_credentials_create_from_auth_json_" - "key failed"); - } - goto end; - } - - /* Then try a refresh token if the auth json key was invalid. */ - token = grpc_auth_refresh_token_create_from_json(json); - if (grpc_auth_refresh_token_is_valid(&token)) { - result = - grpc_refresh_token_credentials_create_from_auth_refresh_token(token); - if (result == NULL) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "grpc_refresh_token_credentials_create_from_auth_refresh_token " - "failed"); - } - goto end; - } - -end: - GPR_ASSERT((result == NULL) + (error == GRPC_ERROR_NONE) == 1); - if (creds_path != NULL) gpr_free(creds_path); - grpc_slice_unref_internal(exec_ctx, creds_data); - if (json != NULL) grpc_json_destroy(json); - *creds = result; - return error; -} - -grpc_channel_credentials *grpc_google_default_credentials_create(void) { - grpc_channel_credentials *result = NULL; - grpc_call_credentials *call_creds = NULL; - grpc_error *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to create Google credentials"); - grpc_error *err; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ()); - - gpr_once_init(&g_once, init_default_credentials); - - gpr_mu_lock(&g_state_mu); - - if (default_credentials != NULL) { - result = grpc_channel_credentials_ref(default_credentials); - goto end; - } - - /* First, try the environment variable. */ - err = create_default_creds_from_path( - &exec_ctx, gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds); - if (err == GRPC_ERROR_NONE) goto end; - error = grpc_error_add_child(error, err); - - /* Then the well-known file. */ - err = create_default_creds_from_path( - &exec_ctx, grpc_get_well_known_google_credentials_file_path(), - &call_creds); - if (err == GRPC_ERROR_NONE) goto end; - error = grpc_error_add_child(error, err); - - /* At last try to see if we're on compute engine (do the detection only once - since it requires a network test). */ - if (!compute_engine_detection_done) { - int need_compute_engine_creds = - is_stack_running_on_compute_engine(&exec_ctx); - compute_engine_detection_done = 1; - if (need_compute_engine_creds) { - call_creds = grpc_google_compute_engine_credentials_create(NULL); - if (call_creds == NULL) { - error = grpc_error_add_child( - error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to get credentials from network")); - } - } - } - -end: - if (result == NULL) { - if (call_creds != NULL) { - /* Blend with default ssl credentials and add a global reference so that - it - can be cached and re-served. */ - grpc_channel_credentials *ssl_creds = - grpc_ssl_credentials_create(NULL, NULL, NULL); - default_credentials = grpc_channel_credentials_ref( - grpc_composite_channel_credentials_create(ssl_creds, call_creds, - NULL)); - GPR_ASSERT(default_credentials != NULL); - grpc_channel_credentials_unref(&exec_ctx, ssl_creds); - grpc_call_credentials_unref(&exec_ctx, call_creds); - result = default_credentials; - } else { - gpr_log(GPR_ERROR, "Could not create google default credentials."); - } - } - gpr_mu_unlock(&g_state_mu); - if (result == NULL) { - GRPC_LOG_IF_ERROR("grpc_google_default_credentials_create", error); - } else { - GRPC_ERROR_UNREF(error); - } - grpc_exec_ctx_finish(&exec_ctx); - return result; -} - -void grpc_flush_cached_google_default_credentials(void) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_once_init(&g_once, init_default_credentials); - gpr_mu_lock(&g_state_mu); - if (default_credentials != NULL) { - grpc_channel_credentials_unref(&exec_ctx, default_credentials); - default_credentials = NULL; - } - compute_engine_detection_done = 0; - gpr_mu_unlock(&g_state_mu); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* -- Well known credentials path. -- */ - -static grpc_well_known_credentials_path_getter creds_path_getter = NULL; - -char *grpc_get_well_known_google_credentials_file_path(void) { - if (creds_path_getter != NULL) return creds_path_getter(); - return grpc_get_well_known_google_credentials_file_path_impl(); -} - -void grpc_override_well_known_credentials_path_getter( - grpc_well_known_credentials_path_getter getter) { - creds_path_getter = getter; -} diff --git a/src/core/lib/security/credentials/google_default/google_default_credentials.cc b/src/core/lib/security/credentials/google_default/google_default_credentials.cc new file mode 100644 index 0000000000..8fe5802d49 --- /dev/null +++ b/src/core/lib/security/credentials/google_default/google_default_credentials.cc @@ -0,0 +1,324 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/credentials.h" + +#include + +#include +#include +#include + +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/security/credentials/google_default/google_default_credentials.h" +#include "src/core/lib/security/credentials/jwt/jwt_credentials.h" +#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" + +/* -- Constants. -- */ + +#define GRPC_COMPUTE_ENGINE_DETECTION_HOST "metadata.google.internal" + +/* -- Default credentials. -- */ + +static grpc_channel_credentials *default_credentials = NULL; +static int compute_engine_detection_done = 0; +static gpr_mu g_state_mu; +static gpr_mu *g_polling_mu; +static gpr_once g_once = GPR_ONCE_INIT; + +static void init_default_credentials(void) { gpr_mu_init(&g_state_mu); } + +typedef struct { + grpc_polling_entity pollent; + int is_done; + int success; + grpc_http_response response; +} compute_engine_detector; + +static void on_compute_engine_detection_http_response(grpc_exec_ctx *exec_ctx, + void *user_data, + grpc_error *error) { + compute_engine_detector *detector = (compute_engine_detector *)user_data; + if (error == GRPC_ERROR_NONE && detector->response.status == 200 && + detector->response.hdr_count > 0) { + /* Internet providers can return a generic response to all requests, so + it is necessary to check that metadata header is present also. */ + size_t i; + for (i = 0; i < detector->response.hdr_count; i++) { + grpc_http_header *header = &detector->response.hdrs[i]; + if (strcmp(header->key, "Metadata-Flavor") == 0 && + strcmp(header->value, "Google") == 0) { + detector->success = 1; + break; + } + } + } + gpr_mu_lock(g_polling_mu); + detector->is_done = 1; + GRPC_LOG_IF_ERROR( + "Pollset kick", + grpc_pollset_kick(exec_ctx, + grpc_polling_entity_pollset(&detector->pollent), NULL)); + gpr_mu_unlock(g_polling_mu); +} + +static void destroy_pollset(grpc_exec_ctx *exec_ctx, void *p, grpc_error *e) { + grpc_pollset_destroy(exec_ctx, (grpc_pollset *)p); +} + +static int is_stack_running_on_compute_engine(grpc_exec_ctx *exec_ctx) { + compute_engine_detector detector; + grpc_httpcli_request request; + grpc_httpcli_context context; + grpc_closure destroy_closure; + + /* The http call is local. If it takes more than one sec, it is for sure not + on compute engine. */ + gpr_timespec max_detection_delay = gpr_time_from_seconds(1, GPR_TIMESPAN); + + grpc_pollset *pollset = (grpc_pollset *)gpr_zalloc(grpc_pollset_size()); + grpc_pollset_init(pollset, &g_polling_mu); + detector.pollent = grpc_polling_entity_create_from_pollset(pollset); + detector.is_done = 0; + detector.success = 0; + + memset(&detector.response, 0, sizeof(detector.response)); + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = (char *)GRPC_COMPUTE_ENGINE_DETECTION_HOST; + request.http.path = (char *)"/"; + + grpc_httpcli_context_init(&context); + + grpc_resource_quota *resource_quota = + grpc_resource_quota_create("google_default_credentials"); + grpc_httpcli_get( + exec_ctx, &context, &detector.pollent, resource_quota, &request, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), max_detection_delay), + GRPC_CLOSURE_CREATE(on_compute_engine_detection_http_response, &detector, + grpc_schedule_on_exec_ctx), + &detector.response); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + + grpc_exec_ctx_flush(exec_ctx); + + /* Block until we get the response. This is not ideal but this should only be + called once for the lifetime of the process by the default credentials. */ + gpr_mu_lock(g_polling_mu); + while (!detector.is_done) { + grpc_pollset_worker *worker = NULL; + if (!GRPC_LOG_IF_ERROR( + "pollset_work", + grpc_pollset_work(exec_ctx, + grpc_polling_entity_pollset(&detector.pollent), + &worker, gpr_now(GPR_CLOCK_MONOTONIC), + gpr_inf_future(GPR_CLOCK_MONOTONIC)))) { + detector.is_done = 1; + detector.success = 0; + } + } + gpr_mu_unlock(g_polling_mu); + + grpc_httpcli_context_destroy(exec_ctx, &context); + GRPC_CLOSURE_INIT(&destroy_closure, destroy_pollset, + grpc_polling_entity_pollset(&detector.pollent), + grpc_schedule_on_exec_ctx); + grpc_pollset_shutdown(exec_ctx, + grpc_polling_entity_pollset(&detector.pollent), + &destroy_closure); + g_polling_mu = NULL; + grpc_exec_ctx_flush(exec_ctx); + + gpr_free(grpc_polling_entity_pollset(&detector.pollent)); + grpc_http_response_destroy(&detector.response); + + return detector.success; +} + +/* Takes ownership of creds_path if not NULL. */ +static grpc_error *create_default_creds_from_path( + grpc_exec_ctx *exec_ctx, char *creds_path, grpc_call_credentials **creds) { + grpc_json *json = NULL; + grpc_auth_json_key key; + grpc_auth_refresh_token token; + grpc_call_credentials *result = NULL; + grpc_slice creds_data = grpc_empty_slice(); + grpc_error *error = GRPC_ERROR_NONE; + if (creds_path == NULL) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("creds_path unset"); + goto end; + } + error = grpc_load_file(creds_path, 0, &creds_data); + if (error != GRPC_ERROR_NONE) { + goto end; + } + json = grpc_json_parse_string_with_len( + (char *)GRPC_SLICE_START_PTR(creds_data), GRPC_SLICE_LENGTH(creds_data)); + if (json == NULL) { + error = grpc_error_set_str( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Failed to parse JSON"), + GRPC_ERROR_STR_RAW_BYTES, grpc_slice_ref_internal(creds_data)); + goto end; + } + + /* First, try an auth json key. */ + key = grpc_auth_json_key_create_from_json(json); + if (grpc_auth_json_key_is_valid(&key)) { + result = + grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + exec_ctx, key, grpc_max_auth_token_lifetime()); + if (result == NULL) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "grpc_service_account_jwt_access_credentials_create_from_auth_json_" + "key failed"); + } + goto end; + } + + /* Then try a refresh token if the auth json key was invalid. */ + token = grpc_auth_refresh_token_create_from_json(json); + if (grpc_auth_refresh_token_is_valid(&token)) { + result = + grpc_refresh_token_credentials_create_from_auth_refresh_token(token); + if (result == NULL) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "grpc_refresh_token_credentials_create_from_auth_refresh_token " + "failed"); + } + goto end; + } + +end: + GPR_ASSERT((result == NULL) + (error == GRPC_ERROR_NONE) == 1); + if (creds_path != NULL) gpr_free(creds_path); + grpc_slice_unref_internal(exec_ctx, creds_data); + if (json != NULL) grpc_json_destroy(json); + *creds = result; + return error; +} + +grpc_channel_credentials *grpc_google_default_credentials_create(void) { + grpc_channel_credentials *result = NULL; + grpc_call_credentials *call_creds = NULL; + grpc_error *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to create Google credentials"); + grpc_error *err; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_google_default_credentials_create(void)", 0, ()); + + gpr_once_init(&g_once, init_default_credentials); + + gpr_mu_lock(&g_state_mu); + + if (default_credentials != NULL) { + result = grpc_channel_credentials_ref(default_credentials); + goto end; + } + + /* First, try the environment variable. */ + err = create_default_creds_from_path( + &exec_ctx, gpr_getenv(GRPC_GOOGLE_CREDENTIALS_ENV_VAR), &call_creds); + if (err == GRPC_ERROR_NONE) goto end; + error = grpc_error_add_child(error, err); + + /* Then the well-known file. */ + err = create_default_creds_from_path( + &exec_ctx, grpc_get_well_known_google_credentials_file_path(), + &call_creds); + if (err == GRPC_ERROR_NONE) goto end; + error = grpc_error_add_child(error, err); + + /* At last try to see if we're on compute engine (do the detection only once + since it requires a network test). */ + if (!compute_engine_detection_done) { + int need_compute_engine_creds = + is_stack_running_on_compute_engine(&exec_ctx); + compute_engine_detection_done = 1; + if (need_compute_engine_creds) { + call_creds = grpc_google_compute_engine_credentials_create(NULL); + if (call_creds == NULL) { + error = grpc_error_add_child( + error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to get credentials from network")); + } + } + } + +end: + if (result == NULL) { + if (call_creds != NULL) { + /* Blend with default ssl credentials and add a global reference so that + it + can be cached and re-served. */ + grpc_channel_credentials *ssl_creds = + grpc_ssl_credentials_create(NULL, NULL, NULL); + default_credentials = grpc_channel_credentials_ref( + grpc_composite_channel_credentials_create(ssl_creds, call_creds, + NULL)); + GPR_ASSERT(default_credentials != NULL); + grpc_channel_credentials_unref(&exec_ctx, ssl_creds); + grpc_call_credentials_unref(&exec_ctx, call_creds); + result = default_credentials; + } else { + gpr_log(GPR_ERROR, "Could not create google default credentials."); + } + } + gpr_mu_unlock(&g_state_mu); + if (result == NULL) { + GRPC_LOG_IF_ERROR("grpc_google_default_credentials_create", error); + } else { + GRPC_ERROR_UNREF(error); + } + grpc_exec_ctx_finish(&exec_ctx); + return result; +} + +void grpc_flush_cached_google_default_credentials(void) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_once_init(&g_once, init_default_credentials); + gpr_mu_lock(&g_state_mu); + if (default_credentials != NULL) { + grpc_channel_credentials_unref(&exec_ctx, default_credentials); + default_credentials = NULL; + } + compute_engine_detection_done = 0; + gpr_mu_unlock(&g_state_mu); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* -- Well known credentials path. -- */ + +static grpc_well_known_credentials_path_getter creds_path_getter = NULL; + +char *grpc_get_well_known_google_credentials_file_path(void) { + if (creds_path_getter != NULL) return creds_path_getter(); + return grpc_get_well_known_google_credentials_file_path_impl(); +} + +void grpc_override_well_known_credentials_path_getter( + grpc_well_known_credentials_path_getter getter) { + creds_path_getter = getter; +} diff --git a/src/core/lib/security/credentials/iam/iam_credentials.c b/src/core/lib/security/credentials/iam/iam_credentials.c deleted file mode 100644 index e9cf208c16..0000000000 --- a/src/core/lib/security/credentials/iam/iam_credentials.c +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/iam/iam_credentials.h" - -#include - -#include "src/core/lib/surface/api_trace.h" - -#include -#include -#include -#include - -static void iam_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - grpc_credentials_mdelem_array_destroy(exec_ctx, &c->md_array); -} - -static bool iam_get_request_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds, - grpc_polling_entity *pollent, - grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, - grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - grpc_credentials_mdelem_array_append(md_array, &c->md_array); - return true; -} - -static void iam_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - GRPC_ERROR_UNREF(error); -} - -static grpc_call_credentials_vtable iam_vtable = { - iam_destruct, iam_get_request_metadata, iam_cancel_get_request_metadata}; - -grpc_call_credentials *grpc_google_iam_credentials_create( - const char *token, const char *authority_selector, void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( - "grpc_iam_credentials_create(token=%s, authority_selector=%s, " - "reserved=%p)", - 3, (token, authority_selector, reserved)); - GPR_ASSERT(reserved == NULL); - GPR_ASSERT(token != NULL); - GPR_ASSERT(authority_selector != NULL); - grpc_google_iam_credentials *c = - (grpc_google_iam_credentials *)gpr_zalloc(sizeof(*c)); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_IAM; - c->base.vtable = &iam_vtable; - gpr_ref_init(&c->base.refcount, 1); - grpc_mdelem md = grpc_mdelem_from_slices( - &exec_ctx, - grpc_slice_from_static_string(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), - grpc_slice_from_copied_string(token)); - grpc_credentials_mdelem_array_add(&c->md_array, md); - GRPC_MDELEM_UNREF(&exec_ctx, md); - md = grpc_mdelem_from_slices( - &exec_ctx, - grpc_slice_from_static_string(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), - grpc_slice_from_copied_string(authority_selector)); - grpc_credentials_mdelem_array_add(&c->md_array, md); - GRPC_MDELEM_UNREF(&exec_ctx, md); - grpc_exec_ctx_finish(&exec_ctx); - return &c->base; -} diff --git a/src/core/lib/security/credentials/iam/iam_credentials.cc b/src/core/lib/security/credentials/iam/iam_credentials.cc new file mode 100644 index 0000000000..e9cf208c16 --- /dev/null +++ b/src/core/lib/security/credentials/iam/iam_credentials.cc @@ -0,0 +1,86 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/iam/iam_credentials.h" + +#include + +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include +#include + +static void iam_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; + grpc_credentials_mdelem_array_destroy(exec_ctx, &c->md_array); +} + +static bool iam_get_request_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds, + grpc_polling_entity *pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; + grpc_credentials_mdelem_array_append(md_array, &c->md_array); + return true; +} + +static void iam_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable iam_vtable = { + iam_destruct, iam_get_request_metadata, iam_cancel_get_request_metadata}; + +grpc_call_credentials *grpc_google_iam_credentials_create( + const char *token, const char *authority_selector, void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE( + "grpc_iam_credentials_create(token=%s, authority_selector=%s, " + "reserved=%p)", + 3, (token, authority_selector, reserved)); + GPR_ASSERT(reserved == NULL); + GPR_ASSERT(token != NULL); + GPR_ASSERT(authority_selector != NULL); + grpc_google_iam_credentials *c = + (grpc_google_iam_credentials *)gpr_zalloc(sizeof(*c)); + c->base.type = GRPC_CALL_CREDENTIALS_TYPE_IAM; + c->base.vtable = &iam_vtable; + gpr_ref_init(&c->base.refcount, 1); + grpc_mdelem md = grpc_mdelem_from_slices( + &exec_ctx, + grpc_slice_from_static_string(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), + grpc_slice_from_copied_string(token)); + grpc_credentials_mdelem_array_add(&c->md_array, md); + GRPC_MDELEM_UNREF(&exec_ctx, md); + md = grpc_mdelem_from_slices( + &exec_ctx, + grpc_slice_from_static_string(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), + grpc_slice_from_copied_string(authority_selector)); + grpc_credentials_mdelem_array_add(&c->md_array, md); + GRPC_MDELEM_UNREF(&exec_ctx, md); + grpc_exec_ctx_finish(&exec_ctx); + return &c->base; +} diff --git a/src/core/lib/security/credentials/jwt/json_token.c b/src/core/lib/security/credentials/jwt/json_token.c deleted file mode 100644 index 1f5cc7059d..0000000000 --- a/src/core/lib/security/credentials/jwt/json_token.c +++ /dev/null @@ -1,307 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/jwt/json_token.h" - -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/security/util/json_util.h" -#include "src/core/lib/slice/b64.h" -#include "src/core/lib/support/string.h" - -#include -#include -#include - -/* --- Constants. --- */ - -/* 1 hour max. */ -gpr_timespec grpc_max_auth_token_lifetime() { - gpr_timespec out; - out.tv_sec = 3600; - out.tv_nsec = 0; - out.clock_type = GPR_TIMESPAN; - return out; -} - -#define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256" -#define GRPC_JWT_TYPE "JWT" - -/* --- Override for testing. --- */ - -static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL; - -/* --- grpc_auth_json_key. --- */ - -int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) { - return (json_key != NULL) && - strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID); -} - -grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json) { - grpc_auth_json_key result; - BIO *bio = NULL; - const char *prop_value; - int success = 0; - - memset(&result, 0, sizeof(grpc_auth_json_key)); - result.type = GRPC_AUTH_JSON_TYPE_INVALID; - if (json == NULL) { - gpr_log(GPR_ERROR, "Invalid json."); - goto end; - } - - prop_value = grpc_json_get_string_property(json, "type"); - if (prop_value == NULL || - strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) { - goto end; - } - result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT; - - if (!grpc_copy_json_string_property(json, "private_key_id", - &result.private_key_id) || - !grpc_copy_json_string_property(json, "client_id", &result.client_id) || - !grpc_copy_json_string_property(json, "client_email", - &result.client_email)) { - goto end; - } - - prop_value = grpc_json_get_string_property(json, "private_key"); - if (prop_value == NULL) { - goto end; - } - bio = BIO_new(BIO_s_mem()); - success = BIO_puts(bio, prop_value); - if ((success < 0) || ((size_t)success != strlen(prop_value))) { - gpr_log(GPR_ERROR, "Could not write into openssl BIO."); - goto end; - } - result.private_key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, (void *)""); - if (result.private_key == NULL) { - gpr_log(GPR_ERROR, "Could not deserialize private key."); - goto end; - } - success = 1; - -end: - if (bio != NULL) BIO_free(bio); - if (!success) grpc_auth_json_key_destruct(&result); - return result; -} - -grpc_auth_json_key grpc_auth_json_key_create_from_string( - const char *json_string) { - char *scratchpad = gpr_strdup(json_string); - grpc_json *json = grpc_json_parse_string(scratchpad); - grpc_auth_json_key result = grpc_auth_json_key_create_from_json(json); - if (json != NULL) grpc_json_destroy(json); - gpr_free(scratchpad); - return result; -} - -void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) { - if (json_key == NULL) return; - json_key->type = GRPC_AUTH_JSON_TYPE_INVALID; - if (json_key->client_id != NULL) { - gpr_free(json_key->client_id); - json_key->client_id = NULL; - } - if (json_key->private_key_id != NULL) { - gpr_free(json_key->private_key_id); - json_key->private_key_id = NULL; - } - if (json_key->client_email != NULL) { - gpr_free(json_key->client_email); - json_key->client_email = NULL; - } - if (json_key->private_key != NULL) { - RSA_free(json_key->private_key); - json_key->private_key = NULL; - } -} - -/* --- jwt encoding and signature. --- */ - -static grpc_json *create_child(grpc_json *brother, grpc_json *parent, - const char *key, const char *value, - grpc_json_type type) { - grpc_json *child = grpc_json_create(type); - if (brother) brother->next = child; - if (!parent->child) parent->child = child; - child->parent = parent; - child->value = value; - child->key = key; - return child; -} - -static char *encoded_jwt_header(const char *key_id, const char *algorithm) { - grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT); - grpc_json *child = NULL; - char *json_str = NULL; - char *result = NULL; - - child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING); - child = create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING); - create_child(child, json, "kid", key_id, GRPC_JSON_STRING); - - json_str = grpc_json_dump_to_string(json, 0); - result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); - gpr_free(json_str); - grpc_json_destroy(json); - return result; -} - -static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, - const char *audience, - gpr_timespec token_lifetime, const char *scope) { - grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT); - grpc_json *child = NULL; - char *json_str = NULL; - char *result = NULL; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - gpr_timespec expiration = gpr_time_add(now, token_lifetime); - char now_str[GPR_LTOA_MIN_BUFSIZE]; - char expiration_str[GPR_LTOA_MIN_BUFSIZE]; - if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) { - gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value."); - expiration = gpr_time_add(now, grpc_max_auth_token_lifetime()); - } - int64_ttoa(now.tv_sec, now_str); - int64_ttoa(expiration.tv_sec, expiration_str); - - child = - create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING); - if (scope != NULL) { - child = create_child(child, json, "scope", scope, GRPC_JSON_STRING); - } else { - /* Unscoped JWTs need a sub field. */ - child = create_child(child, json, "sub", json_key->client_email, - GRPC_JSON_STRING); - } - - child = create_child(child, json, "aud", audience, GRPC_JSON_STRING); - child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER); - create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER); - - json_str = grpc_json_dump_to_string(json, 0); - result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); - gpr_free(json_str); - grpc_json_destroy(json); - return result; -} - -static char *dot_concat_and_free_strings(char *str1, char *str2) { - size_t str1_len = strlen(str1); - size_t str2_len = strlen(str2); - size_t result_len = str1_len + 1 /* dot */ + str2_len; - char *result = (char *)gpr_malloc(result_len + 1 /* NULL terminated */); - char *current = result; - memcpy(current, str1, str1_len); - current += str1_len; - *(current++) = '.'; - memcpy(current, str2, str2_len); - current += str2_len; - GPR_ASSERT(current >= result); - GPR_ASSERT((uintptr_t)(current - result) == result_len); - *current = '\0'; - gpr_free(str1); - gpr_free(str2); - return result; -} - -const EVP_MD *openssl_digest_from_algorithm(const char *algorithm) { - if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) { - return EVP_sha256(); - } else { - gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm); - return NULL; - } -} - -char *compute_and_encode_signature(const grpc_auth_json_key *json_key, - const char *signature_algorithm, - const char *to_sign) { - const EVP_MD *md = openssl_digest_from_algorithm(signature_algorithm); - EVP_MD_CTX *md_ctx = NULL; - EVP_PKEY *key = EVP_PKEY_new(); - size_t sig_len = 0; - unsigned char *sig = NULL; - char *result = NULL; - if (md == NULL) return NULL; - md_ctx = EVP_MD_CTX_create(); - if (md_ctx == NULL) { - gpr_log(GPR_ERROR, "Could not create MD_CTX"); - goto end; - } - EVP_PKEY_set1_RSA(key, json_key->private_key); - if (EVP_DigestSignInit(md_ctx, NULL, md, NULL, key) != 1) { - gpr_log(GPR_ERROR, "DigestInit failed."); - goto end; - } - if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) { - gpr_log(GPR_ERROR, "DigestUpdate failed."); - goto end; - } - if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) != 1) { - gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed."); - goto end; - } - sig = (unsigned char *)gpr_malloc(sig_len); - if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) { - gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed."); - goto end; - } - result = grpc_base64_encode(sig, sig_len, 1, 0); - -end: - if (key != NULL) EVP_PKEY_free(key); - if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx); - if (sig != NULL) gpr_free(sig); - return result; -} - -char *grpc_jwt_encode_and_sign(const grpc_auth_json_key *json_key, - const char *audience, - gpr_timespec token_lifetime, const char *scope) { - if (g_jwt_encode_and_sign_override != NULL) { - return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime, - scope); - } else { - const char *sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM; - char *to_sign = dot_concat_and_free_strings( - encoded_jwt_header(json_key->private_key_id, sig_algo), - encoded_jwt_claim(json_key, audience, token_lifetime, scope)); - char *sig = compute_and_encode_signature(json_key, sig_algo, to_sign); - if (sig == NULL) { - gpr_free(to_sign); - return NULL; - } - return dot_concat_and_free_strings(to_sign, sig); - } -} - -void grpc_jwt_encode_and_sign_set_override( - grpc_jwt_encode_and_sign_override func) { - g_jwt_encode_and_sign_override = func; -} diff --git a/src/core/lib/security/credentials/jwt/json_token.cc b/src/core/lib/security/credentials/jwt/json_token.cc new file mode 100644 index 0000000000..1f5cc7059d --- /dev/null +++ b/src/core/lib/security/credentials/jwt/json_token.cc @@ -0,0 +1,307 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/jwt/json_token.h" + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/security/util/json_util.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/support/string.h" + +#include +#include +#include + +/* --- Constants. --- */ + +/* 1 hour max. */ +gpr_timespec grpc_max_auth_token_lifetime() { + gpr_timespec out; + out.tv_sec = 3600; + out.tv_nsec = 0; + out.clock_type = GPR_TIMESPAN; + return out; +} + +#define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256" +#define GRPC_JWT_TYPE "JWT" + +/* --- Override for testing. --- */ + +static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override = NULL; + +/* --- grpc_auth_json_key. --- */ + +int grpc_auth_json_key_is_valid(const grpc_auth_json_key *json_key) { + return (json_key != NULL) && + strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID); +} + +grpc_auth_json_key grpc_auth_json_key_create_from_json(const grpc_json *json) { + grpc_auth_json_key result; + BIO *bio = NULL; + const char *prop_value; + int success = 0; + + memset(&result, 0, sizeof(grpc_auth_json_key)); + result.type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json == NULL) { + gpr_log(GPR_ERROR, "Invalid json."); + goto end; + } + + prop_value = grpc_json_get_string_property(json, "type"); + if (prop_value == NULL || + strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT)) { + goto end; + } + result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT; + + if (!grpc_copy_json_string_property(json, "private_key_id", + &result.private_key_id) || + !grpc_copy_json_string_property(json, "client_id", &result.client_id) || + !grpc_copy_json_string_property(json, "client_email", + &result.client_email)) { + goto end; + } + + prop_value = grpc_json_get_string_property(json, "private_key"); + if (prop_value == NULL) { + goto end; + } + bio = BIO_new(BIO_s_mem()); + success = BIO_puts(bio, prop_value); + if ((success < 0) || ((size_t)success != strlen(prop_value))) { + gpr_log(GPR_ERROR, "Could not write into openssl BIO."); + goto end; + } + result.private_key = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, (void *)""); + if (result.private_key == NULL) { + gpr_log(GPR_ERROR, "Could not deserialize private key."); + goto end; + } + success = 1; + +end: + if (bio != NULL) BIO_free(bio); + if (!success) grpc_auth_json_key_destruct(&result); + return result; +} + +grpc_auth_json_key grpc_auth_json_key_create_from_string( + const char *json_string) { + char *scratchpad = gpr_strdup(json_string); + grpc_json *json = grpc_json_parse_string(scratchpad); + grpc_auth_json_key result = grpc_auth_json_key_create_from_json(json); + if (json != NULL) grpc_json_destroy(json); + gpr_free(scratchpad); + return result; +} + +void grpc_auth_json_key_destruct(grpc_auth_json_key *json_key) { + if (json_key == NULL) return; + json_key->type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json_key->client_id != NULL) { + gpr_free(json_key->client_id); + json_key->client_id = NULL; + } + if (json_key->private_key_id != NULL) { + gpr_free(json_key->private_key_id); + json_key->private_key_id = NULL; + } + if (json_key->client_email != NULL) { + gpr_free(json_key->client_email); + json_key->client_email = NULL; + } + if (json_key->private_key != NULL) { + RSA_free(json_key->private_key); + json_key->private_key = NULL; + } +} + +/* --- jwt encoding and signature. --- */ + +static grpc_json *create_child(grpc_json *brother, grpc_json *parent, + const char *key, const char *value, + grpc_json_type type) { + grpc_json *child = grpc_json_create(type); + if (brother) brother->next = child; + if (!parent->child) parent->child = child; + child->parent = parent; + child->value = value; + child->key = key; + return child; +} + +static char *encoded_jwt_header(const char *key_id, const char *algorithm) { + grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json *child = NULL; + char *json_str = NULL; + char *result = NULL; + + child = create_child(NULL, json, "alg", algorithm, GRPC_JSON_STRING); + child = create_child(child, json, "typ", GRPC_JWT_TYPE, GRPC_JSON_STRING); + create_child(child, json, "kid", key_id, GRPC_JSON_STRING); + + json_str = grpc_json_dump_to_string(json, 0); + result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); + gpr_free(json_str); + grpc_json_destroy(json); + return result; +} + +static char *encoded_jwt_claim(const grpc_auth_json_key *json_key, + const char *audience, + gpr_timespec token_lifetime, const char *scope) { + grpc_json *json = grpc_json_create(GRPC_JSON_OBJECT); + grpc_json *child = NULL; + char *json_str = NULL; + char *result = NULL; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + gpr_timespec expiration = gpr_time_add(now, token_lifetime); + char now_str[GPR_LTOA_MIN_BUFSIZE]; + char expiration_str[GPR_LTOA_MIN_BUFSIZE]; + if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) { + gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value."); + expiration = gpr_time_add(now, grpc_max_auth_token_lifetime()); + } + int64_ttoa(now.tv_sec, now_str); + int64_ttoa(expiration.tv_sec, expiration_str); + + child = + create_child(NULL, json, "iss", json_key->client_email, GRPC_JSON_STRING); + if (scope != NULL) { + child = create_child(child, json, "scope", scope, GRPC_JSON_STRING); + } else { + /* Unscoped JWTs need a sub field. */ + child = create_child(child, json, "sub", json_key->client_email, + GRPC_JSON_STRING); + } + + child = create_child(child, json, "aud", audience, GRPC_JSON_STRING); + child = create_child(child, json, "iat", now_str, GRPC_JSON_NUMBER); + create_child(child, json, "exp", expiration_str, GRPC_JSON_NUMBER); + + json_str = grpc_json_dump_to_string(json, 0); + result = grpc_base64_encode(json_str, strlen(json_str), 1, 0); + gpr_free(json_str); + grpc_json_destroy(json); + return result; +} + +static char *dot_concat_and_free_strings(char *str1, char *str2) { + size_t str1_len = strlen(str1); + size_t str2_len = strlen(str2); + size_t result_len = str1_len + 1 /* dot */ + str2_len; + char *result = (char *)gpr_malloc(result_len + 1 /* NULL terminated */); + char *current = result; + memcpy(current, str1, str1_len); + current += str1_len; + *(current++) = '.'; + memcpy(current, str2, str2_len); + current += str2_len; + GPR_ASSERT(current >= result); + GPR_ASSERT((uintptr_t)(current - result) == result_len); + *current = '\0'; + gpr_free(str1); + gpr_free(str2); + return result; +} + +const EVP_MD *openssl_digest_from_algorithm(const char *algorithm) { + if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) { + return EVP_sha256(); + } else { + gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm); + return NULL; + } +} + +char *compute_and_encode_signature(const grpc_auth_json_key *json_key, + const char *signature_algorithm, + const char *to_sign) { + const EVP_MD *md = openssl_digest_from_algorithm(signature_algorithm); + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY *key = EVP_PKEY_new(); + size_t sig_len = 0; + unsigned char *sig = NULL; + char *result = NULL; + if (md == NULL) return NULL; + md_ctx = EVP_MD_CTX_create(); + if (md_ctx == NULL) { + gpr_log(GPR_ERROR, "Could not create MD_CTX"); + goto end; + } + EVP_PKEY_set1_RSA(key, json_key->private_key); + if (EVP_DigestSignInit(md_ctx, NULL, md, NULL, key) != 1) { + gpr_log(GPR_ERROR, "DigestInit failed."); + goto end; + } + if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) { + gpr_log(GPR_ERROR, "DigestUpdate failed."); + goto end; + } + if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) != 1) { + gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed."); + goto end; + } + sig = (unsigned char *)gpr_malloc(sig_len); + if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) { + gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed."); + goto end; + } + result = grpc_base64_encode(sig, sig_len, 1, 0); + +end: + if (key != NULL) EVP_PKEY_free(key); + if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx); + if (sig != NULL) gpr_free(sig); + return result; +} + +char *grpc_jwt_encode_and_sign(const grpc_auth_json_key *json_key, + const char *audience, + gpr_timespec token_lifetime, const char *scope) { + if (g_jwt_encode_and_sign_override != NULL) { + return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime, + scope); + } else { + const char *sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM; + char *to_sign = dot_concat_and_free_strings( + encoded_jwt_header(json_key->private_key_id, sig_algo), + encoded_jwt_claim(json_key, audience, token_lifetime, scope)); + char *sig = compute_and_encode_signature(json_key, sig_algo, to_sign); + if (sig == NULL) { + gpr_free(to_sign); + return NULL; + } + return dot_concat_and_free_strings(to_sign, sig); + } +} + +void grpc_jwt_encode_and_sign_set_override( + grpc_jwt_encode_and_sign_override func) { + g_jwt_encode_and_sign_override = func; +} diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.c b/src/core/lib/security/credentials/jwt/jwt_credentials.c deleted file mode 100644 index b361265a7b..0000000000 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/jwt/jwt_credentials.h" - -#include - -#include "src/core/lib/surface/api_trace.h" - -#include -#include -#include -#include - -static void jwt_reset_cache(grpc_exec_ctx *exec_ctx, - grpc_service_account_jwt_access_credentials *c) { - GRPC_MDELEM_UNREF(exec_ctx, c->cached.jwt_md); - c->cached.jwt_md = GRPC_MDNULL; - if (c->cached.service_url != NULL) { - gpr_free(c->cached.service_url); - c->cached.service_url = NULL; - } - c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); -} - -static void jwt_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_service_account_jwt_access_credentials *c = - (grpc_service_account_jwt_access_credentials *)creds; - grpc_auth_json_key_destruct(&c->key); - jwt_reset_cache(exec_ctx, c); - gpr_mu_destroy(&c->cache_mu); -} - -static bool jwt_get_request_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds, - grpc_polling_entity *pollent, - grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, - grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_service_account_jwt_access_credentials *c = - (grpc_service_account_jwt_access_credentials *)creds; - gpr_timespec refresh_threshold = gpr_time_from_seconds( - GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); - - /* See if we can return a cached jwt. */ - grpc_mdelem jwt_md = GRPC_MDNULL; - { - gpr_mu_lock(&c->cache_mu); - if (c->cached.service_url != NULL && - strcmp(c->cached.service_url, context.service_url) == 0 && - !GRPC_MDISNULL(c->cached.jwt_md) && - (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, - gpr_now(GPR_CLOCK_REALTIME)), - refresh_threshold) > 0)) { - jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md); - } - gpr_mu_unlock(&c->cache_mu); - } - - if (GRPC_MDISNULL(jwt_md)) { - char *jwt = NULL; - /* Generate a new jwt. */ - gpr_mu_lock(&c->cache_mu); - jwt_reset_cache(exec_ctx, c); - jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url, - c->jwt_lifetime, NULL); - if (jwt != NULL) { - char *md_value; - gpr_asprintf(&md_value, "Bearer %s", jwt); - gpr_free(jwt); - c->cached.jwt_expiration = - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime); - c->cached.service_url = gpr_strdup(context.service_url); - c->cached.jwt_md = grpc_mdelem_from_slices( - exec_ctx, - grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), - grpc_slice_from_copied_string(md_value)); - gpr_free(md_value); - jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md); - } - gpr_mu_unlock(&c->cache_mu); - } - - if (!GRPC_MDISNULL(jwt_md)) { - grpc_credentials_mdelem_array_add(md_array, jwt_md); - GRPC_MDELEM_UNREF(exec_ctx, jwt_md); - } else { - *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT."); - } - return true; -} - -static void jwt_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - GRPC_ERROR_UNREF(error); -} - -static grpc_call_credentials_vtable jwt_vtable = { - jwt_destruct, jwt_get_request_metadata, jwt_cancel_get_request_metadata}; - -grpc_call_credentials * -grpc_service_account_jwt_access_credentials_create_from_auth_json_key( - grpc_exec_ctx *exec_ctx, grpc_auth_json_key key, - gpr_timespec token_lifetime) { - grpc_service_account_jwt_access_credentials *c; - if (!grpc_auth_json_key_is_valid(&key)) { - gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); - return NULL; - } - c = (grpc_service_account_jwt_access_credentials *)gpr_zalloc( - sizeof(grpc_service_account_jwt_access_credentials)); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_JWT; - gpr_ref_init(&c->base.refcount, 1); - c->base.vtable = &jwt_vtable; - c->key = key; - gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime(); - if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) { - gpr_log(GPR_INFO, - "Cropping token lifetime to maximum allowed value (%d secs).", - (int)max_token_lifetime.tv_sec); - token_lifetime = grpc_max_auth_token_lifetime(); - } - c->jwt_lifetime = token_lifetime; - gpr_mu_init(&c->cache_mu); - jwt_reset_cache(exec_ctx, c); - return &c->base; -} - -static char *redact_private_key(const char *json_key) { - char *json_copy = gpr_strdup(json_key); - grpc_json *json = grpc_json_parse_string(json_copy); - if (!json) { - gpr_free(json_copy); - return gpr_strdup(""); - } - const char *redacted = ""; - grpc_json *current = json->child; - while (current) { - if (current->type == GRPC_JSON_STRING && - strcmp(current->key, "private_key") == 0) { - current->value = (char *)redacted; - break; - } - current = current->next; - } - char *clean_json = grpc_json_dump_to_string(json, 2); - gpr_free(json_copy); - grpc_json_destroy(json); - return clean_json; -} - -grpc_call_credentials *grpc_service_account_jwt_access_credentials_create( - const char *json_key, gpr_timespec token_lifetime, void *reserved) { - if (GRPC_TRACER_ON(grpc_api_trace)) { - char *clean_json = redact_private_key(json_key); - gpr_log(GPR_INFO, - "grpc_service_account_jwt_access_credentials_create(" - "json_key=%s, " - "token_lifetime=" - "gpr_timespec { tv_sec: %" PRId64 - ", tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec, - (int)token_lifetime.clock_type, reserved); - gpr_free(clean_json); - } - GPR_ASSERT(reserved == NULL); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call_credentials *creds = - grpc_service_account_jwt_access_credentials_create_from_auth_json_key( - &exec_ctx, grpc_auth_json_key_create_from_string(json_key), - token_lifetime); - grpc_exec_ctx_finish(&exec_ctx); - return creds; -} diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc new file mode 100644 index 0000000000..b361265a7b --- /dev/null +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -0,0 +1,193 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/jwt/jwt_credentials.h" + +#include + +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include +#include + +static void jwt_reset_cache(grpc_exec_ctx *exec_ctx, + grpc_service_account_jwt_access_credentials *c) { + GRPC_MDELEM_UNREF(exec_ctx, c->cached.jwt_md); + c->cached.jwt_md = GRPC_MDNULL; + if (c->cached.service_url != NULL) { + gpr_free(c->cached.service_url); + c->cached.service_url = NULL; + } + c->cached.jwt_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); +} + +static void jwt_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_service_account_jwt_access_credentials *c = + (grpc_service_account_jwt_access_credentials *)creds; + grpc_auth_json_key_destruct(&c->key); + jwt_reset_cache(exec_ctx, c); + gpr_mu_destroy(&c->cache_mu); +} + +static bool jwt_get_request_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds, + grpc_polling_entity *pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_service_account_jwt_access_credentials *c = + (grpc_service_account_jwt_access_credentials *)creds; + gpr_timespec refresh_threshold = gpr_time_from_seconds( + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); + + /* See if we can return a cached jwt. */ + grpc_mdelem jwt_md = GRPC_MDNULL; + { + gpr_mu_lock(&c->cache_mu); + if (c->cached.service_url != NULL && + strcmp(c->cached.service_url, context.service_url) == 0 && + !GRPC_MDISNULL(c->cached.jwt_md) && + (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, + gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { + jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md); + } + gpr_mu_unlock(&c->cache_mu); + } + + if (GRPC_MDISNULL(jwt_md)) { + char *jwt = NULL; + /* Generate a new jwt. */ + gpr_mu_lock(&c->cache_mu); + jwt_reset_cache(exec_ctx, c); + jwt = grpc_jwt_encode_and_sign(&c->key, context.service_url, + c->jwt_lifetime, NULL); + if (jwt != NULL) { + char *md_value; + gpr_asprintf(&md_value, "Bearer %s", jwt); + gpr_free(jwt); + c->cached.jwt_expiration = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime); + c->cached.service_url = gpr_strdup(context.service_url); + c->cached.jwt_md = grpc_mdelem_from_slices( + exec_ctx, + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(md_value)); + gpr_free(md_value); + jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md); + } + gpr_mu_unlock(&c->cache_mu); + } + + if (!GRPC_MDISNULL(jwt_md)) { + grpc_credentials_mdelem_array_add(md_array, jwt_md); + GRPC_MDELEM_UNREF(exec_ctx, jwt_md); + } else { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT."); + } + return true; +} + +static void jwt_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable jwt_vtable = { + jwt_destruct, jwt_get_request_metadata, jwt_cancel_get_request_metadata}; + +grpc_call_credentials * +grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + grpc_exec_ctx *exec_ctx, grpc_auth_json_key key, + gpr_timespec token_lifetime) { + grpc_service_account_jwt_access_credentials *c; + if (!grpc_auth_json_key_is_valid(&key)) { + gpr_log(GPR_ERROR, "Invalid input for jwt credentials creation"); + return NULL; + } + c = (grpc_service_account_jwt_access_credentials *)gpr_zalloc( + sizeof(grpc_service_account_jwt_access_credentials)); + c->base.type = GRPC_CALL_CREDENTIALS_TYPE_JWT; + gpr_ref_init(&c->base.refcount, 1); + c->base.vtable = &jwt_vtable; + c->key = key; + gpr_timespec max_token_lifetime = grpc_max_auth_token_lifetime(); + if (gpr_time_cmp(token_lifetime, max_token_lifetime) > 0) { + gpr_log(GPR_INFO, + "Cropping token lifetime to maximum allowed value (%d secs).", + (int)max_token_lifetime.tv_sec); + token_lifetime = grpc_max_auth_token_lifetime(); + } + c->jwt_lifetime = token_lifetime; + gpr_mu_init(&c->cache_mu); + jwt_reset_cache(exec_ctx, c); + return &c->base; +} + +static char *redact_private_key(const char *json_key) { + char *json_copy = gpr_strdup(json_key); + grpc_json *json = grpc_json_parse_string(json_copy); + if (!json) { + gpr_free(json_copy); + return gpr_strdup(""); + } + const char *redacted = ""; + grpc_json *current = json->child; + while (current) { + if (current->type == GRPC_JSON_STRING && + strcmp(current->key, "private_key") == 0) { + current->value = (char *)redacted; + break; + } + current = current->next; + } + char *clean_json = grpc_json_dump_to_string(json, 2); + gpr_free(json_copy); + grpc_json_destroy(json); + return clean_json; +} + +grpc_call_credentials *grpc_service_account_jwt_access_credentials_create( + const char *json_key, gpr_timespec token_lifetime, void *reserved) { + if (GRPC_TRACER_ON(grpc_api_trace)) { + char *clean_json = redact_private_key(json_key); + gpr_log(GPR_INFO, + "grpc_service_account_jwt_access_credentials_create(" + "json_key=%s, " + "token_lifetime=" + "gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + clean_json, token_lifetime.tv_sec, token_lifetime.tv_nsec, + (int)token_lifetime.clock_type, reserved); + gpr_free(clean_json); + } + GPR_ASSERT(reserved == NULL); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call_credentials *creds = + grpc_service_account_jwt_access_credentials_create_from_auth_json_key( + &exec_ctx, grpc_auth_json_key_create_from_string(json_key), + token_lifetime); + grpc_exec_ctx_finish(&exec_ctx); + return creds; +} diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.c b/src/core/lib/security/credentials/jwt/jwt_verifier.c deleted file mode 100644 index 656d0920cd..0000000000 --- a/src/core/lib/security/credentials/jwt/jwt_verifier.c +++ /dev/null @@ -1,939 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/jwt/jwt_verifier.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/http/httpcli.h" -#include "src/core/lib/iomgr/polling_entity.h" -#include "src/core/lib/slice/b64.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" -#include "src/core/tsi/ssl_types.h" - -/* --- Utils. --- */ - -const char *grpc_jwt_verifier_status_to_string( - grpc_jwt_verifier_status status) { - switch (status) { - case GRPC_JWT_VERIFIER_OK: - return "OK"; - case GRPC_JWT_VERIFIER_BAD_SIGNATURE: - return "BAD_SIGNATURE"; - case GRPC_JWT_VERIFIER_BAD_FORMAT: - return "BAD_FORMAT"; - case GRPC_JWT_VERIFIER_BAD_AUDIENCE: - return "BAD_AUDIENCE"; - case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR: - return "KEY_RETRIEVAL_ERROR"; - case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE: - return "TIME_CONSTRAINT_FAILURE"; - case GRPC_JWT_VERIFIER_GENERIC_ERROR: - return "GENERIC_ERROR"; - default: - return "UNKNOWN"; - } -} - -static const EVP_MD *evp_md_from_alg(const char *alg) { - if (strcmp(alg, "RS256") == 0) { - return EVP_sha256(); - } else if (strcmp(alg, "RS384") == 0) { - return EVP_sha384(); - } else if (strcmp(alg, "RS512") == 0) { - return EVP_sha512(); - } else { - return NULL; - } -} - -static grpc_json *parse_json_part_from_jwt(grpc_exec_ctx *exec_ctx, - const char *str, size_t len, - grpc_slice *buffer) { - grpc_json *json; - - *buffer = grpc_base64_decode_with_len(exec_ctx, str, len, 1); - if (GRPC_SLICE_IS_EMPTY(*buffer)) { - gpr_log(GPR_ERROR, "Invalid base64."); - return NULL; - } - json = grpc_json_parse_string_with_len((char *)GRPC_SLICE_START_PTR(*buffer), - GRPC_SLICE_LENGTH(*buffer)); - if (json == NULL) { - grpc_slice_unref_internal(exec_ctx, *buffer); - gpr_log(GPR_ERROR, "JSON parsing error."); - } - return json; -} - -static const char *validate_string_field(const grpc_json *json, - const char *key) { - if (json->type != GRPC_JSON_STRING) { - gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); - return NULL; - } - return json->value; -} - -static gpr_timespec validate_time_field(const grpc_json *json, - const char *key) { - gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME); - if (json->type != GRPC_JSON_NUMBER) { - gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); - return result; - } - result.tv_sec = strtol(json->value, NULL, 10); - return result; -} - -/* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */ - -typedef struct { - const char *alg; - const char *kid; - const char *typ; - /* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */ - grpc_slice buffer; -} jose_header; - -static void jose_header_destroy(grpc_exec_ctx *exec_ctx, jose_header *h) { - grpc_slice_unref_internal(exec_ctx, h->buffer); - gpr_free(h); -} - -/* Takes ownership of json and buffer. */ -static jose_header *jose_header_from_json(grpc_exec_ctx *exec_ctx, - grpc_json *json, grpc_slice buffer) { - grpc_json *cur; - jose_header *h = (jose_header *)gpr_zalloc(sizeof(jose_header)); - h->buffer = buffer; - for (cur = json->child; cur != NULL; cur = cur->next) { - if (strcmp(cur->key, "alg") == 0) { - /* We only support RSA-1.5 signatures for now. - Beware of this if we add HMAC support: - https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ - */ - if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) || - evp_md_from_alg(cur->value) == NULL) { - gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value); - goto error; - } - h->alg = cur->value; - } else if (strcmp(cur->key, "typ") == 0) { - h->typ = validate_string_field(cur, "typ"); - if (h->typ == NULL) goto error; - } else if (strcmp(cur->key, "kid") == 0) { - h->kid = validate_string_field(cur, "kid"); - if (h->kid == NULL) goto error; - } - } - if (h->alg == NULL) { - gpr_log(GPR_ERROR, "Missing alg field."); - goto error; - } - grpc_json_destroy(json); - h->buffer = buffer; - return h; - -error: - grpc_json_destroy(json); - jose_header_destroy(exec_ctx, h); - return NULL; -} - -/* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */ - -struct grpc_jwt_claims { - /* Well known properties already parsed. */ - const char *sub; - const char *iss; - const char *aud; - const char *jti; - gpr_timespec iat; - gpr_timespec exp; - gpr_timespec nbf; - - grpc_json *json; - grpc_slice buffer; -}; - -void grpc_jwt_claims_destroy(grpc_exec_ctx *exec_ctx, grpc_jwt_claims *claims) { - grpc_json_destroy(claims->json); - grpc_slice_unref_internal(exec_ctx, claims->buffer); - gpr_free(claims); -} - -const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims) { - if (claims == NULL) return NULL; - return claims->json; -} - -const char *grpc_jwt_claims_subject(const grpc_jwt_claims *claims) { - if (claims == NULL) return NULL; - return claims->sub; -} - -const char *grpc_jwt_claims_issuer(const grpc_jwt_claims *claims) { - if (claims == NULL) return NULL; - return claims->iss; -} - -const char *grpc_jwt_claims_id(const grpc_jwt_claims *claims) { - if (claims == NULL) return NULL; - return claims->jti; -} - -const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims) { - if (claims == NULL) return NULL; - return claims->aud; -} - -gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims) { - if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); - return claims->iat; -} - -gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims) { - if (claims == NULL) return gpr_inf_future(GPR_CLOCK_REALTIME); - return claims->exp; -} - -gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) { - if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); - return claims->nbf; -} - -/* Takes ownership of json and buffer even in case of failure. */ -grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_exec_ctx *exec_ctx, - grpc_json *json, grpc_slice buffer) { - grpc_json *cur; - grpc_jwt_claims *claims = - (grpc_jwt_claims *)gpr_malloc(sizeof(grpc_jwt_claims)); - memset(claims, 0, sizeof(grpc_jwt_claims)); - claims->json = json; - claims->buffer = buffer; - claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME); - claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME); - claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME); - - /* Per the spec, all fields are optional. */ - for (cur = json->child; cur != NULL; cur = cur->next) { - if (strcmp(cur->key, "sub") == 0) { - claims->sub = validate_string_field(cur, "sub"); - if (claims->sub == NULL) goto error; - } else if (strcmp(cur->key, "iss") == 0) { - claims->iss = validate_string_field(cur, "iss"); - if (claims->iss == NULL) goto error; - } else if (strcmp(cur->key, "aud") == 0) { - claims->aud = validate_string_field(cur, "aud"); - if (claims->aud == NULL) goto error; - } else if (strcmp(cur->key, "jti") == 0) { - claims->jti = validate_string_field(cur, "jti"); - if (claims->jti == NULL) goto error; - } else if (strcmp(cur->key, "iat") == 0) { - claims->iat = validate_time_field(cur, "iat"); - if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) - goto error; - } else if (strcmp(cur->key, "exp") == 0) { - claims->exp = validate_time_field(cur, "exp"); - if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) - goto error; - } else if (strcmp(cur->key, "nbf") == 0) { - claims->nbf = validate_time_field(cur, "nbf"); - if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) - goto error; - } - } - return claims; - -error: - grpc_jwt_claims_destroy(exec_ctx, claims); - return NULL; -} - -grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims, - const char *audience) { - gpr_timespec skewed_now; - int audience_ok; - - GPR_ASSERT(claims != NULL); - - skewed_now = - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); - if (gpr_time_cmp(skewed_now, claims->nbf) < 0) { - gpr_log(GPR_ERROR, "JWT is not valid yet."); - return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; - } - skewed_now = - gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); - if (gpr_time_cmp(skewed_now, claims->exp) > 0) { - gpr_log(GPR_ERROR, "JWT is expired."); - return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; - } - - /* This should be probably up to the upper layer to decide but let's harcode - the 99% use case here for email issuers, where the JWT must be self - issued. */ - if (grpc_jwt_issuer_email_domain(claims->iss) != NULL && - claims->sub != NULL && strcmp(claims->iss, claims->sub) != 0) { - gpr_log(GPR_ERROR, - "Email issuer (%s) cannot assert another subject (%s) than itself.", - claims->iss, claims->sub); - return GRPC_JWT_VERIFIER_BAD_SUBJECT; - } - - if (audience == NULL) { - audience_ok = claims->aud == NULL; - } else { - audience_ok = claims->aud != NULL && strcmp(audience, claims->aud) == 0; - } - if (!audience_ok) { - gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.", - audience == NULL ? "NULL" : audience, - claims->aud == NULL ? "NULL" : claims->aud); - return GRPC_JWT_VERIFIER_BAD_AUDIENCE; - } - return GRPC_JWT_VERIFIER_OK; -} - -/* --- verifier_cb_ctx object. --- */ - -typedef enum { - HTTP_RESPONSE_OPENID = 0, - HTTP_RESPONSE_KEYS, - HTTP_RESPONSE_COUNT /* must be last */ -} http_response_index; - -typedef struct { - grpc_jwt_verifier *verifier; - grpc_polling_entity pollent; - jose_header *header; - grpc_jwt_claims *claims; - char *audience; - grpc_slice signature; - grpc_slice signed_data; - void *user_data; - grpc_jwt_verification_done_cb user_cb; - grpc_http_response responses[HTTP_RESPONSE_COUNT]; -} verifier_cb_ctx; - -/* Takes ownership of the header, claims and signature. */ -static verifier_cb_ctx *verifier_cb_ctx_create( - grpc_jwt_verifier *verifier, grpc_pollset *pollset, jose_header *header, - grpc_jwt_claims *claims, const char *audience, grpc_slice signature, - const char *signed_jwt, size_t signed_jwt_len, void *user_data, - grpc_jwt_verification_done_cb cb) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - verifier_cb_ctx *ctx = (verifier_cb_ctx *)gpr_zalloc(sizeof(verifier_cb_ctx)); - ctx->verifier = verifier; - ctx->pollent = grpc_polling_entity_create_from_pollset(pollset); - ctx->header = header; - ctx->audience = gpr_strdup(audience); - ctx->claims = claims; - ctx->signature = signature; - ctx->signed_data = grpc_slice_from_copied_buffer(signed_jwt, signed_jwt_len); - ctx->user_data = user_data; - ctx->user_cb = cb; - grpc_exec_ctx_finish(&exec_ctx); - return ctx; -} - -void verifier_cb_ctx_destroy(grpc_exec_ctx *exec_ctx, verifier_cb_ctx *ctx) { - if (ctx->audience != NULL) gpr_free(ctx->audience); - if (ctx->claims != NULL) grpc_jwt_claims_destroy(exec_ctx, ctx->claims); - grpc_slice_unref_internal(exec_ctx, ctx->signature); - grpc_slice_unref_internal(exec_ctx, ctx->signed_data); - jose_header_destroy(exec_ctx, ctx->header); - for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) { - grpc_http_response_destroy(&ctx->responses[i]); - } - /* TODO: see what to do with claims... */ - gpr_free(ctx); -} - -/* --- grpc_jwt_verifier object. --- */ - -/* Clock skew defaults to one minute. */ -gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN}; - -/* Max delay defaults to one minute. */ -gpr_timespec grpc_jwt_verifier_max_delay = {60, 0, GPR_TIMESPAN}; - -typedef struct { - char *email_domain; - char *key_url_prefix; -} email_key_mapping; - -struct grpc_jwt_verifier { - email_key_mapping *mappings; - size_t num_mappings; /* Should be very few, linear search ok. */ - size_t allocated_mappings; - grpc_httpcli_context http_ctx; -}; - -static grpc_json *json_from_http(const grpc_httpcli_response *response) { - grpc_json *json = NULL; - - if (response == NULL) { - gpr_log(GPR_ERROR, "HTTP response is NULL."); - return NULL; - } - if (response->status != 200) { - gpr_log(GPR_ERROR, "Call to http server failed with error %d.", - response->status); - return NULL; - } - - json = grpc_json_parse_string_with_len(response->body, response->body_length); - if (json == NULL) { - gpr_log(GPR_ERROR, "Invalid JSON found in response."); - } - return json; -} - -static const grpc_json *find_property_by_name(const grpc_json *json, - const char *name) { - const grpc_json *cur; - for (cur = json->child; cur != NULL; cur = cur->next) { - if (strcmp(cur->key, name) == 0) return cur; - } - return NULL; -} - -static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) { - X509 *x509 = NULL; - EVP_PKEY *result = NULL; - BIO *bio = BIO_new(BIO_s_mem()); - size_t len = strlen(x509_str); - GPR_ASSERT(len < INT_MAX); - BIO_write(bio, x509_str, (int)len); - x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (x509 == NULL) { - gpr_log(GPR_ERROR, "Unable to parse x509 cert."); - goto end; - } - result = X509_get_pubkey(x509); - if (result == NULL) { - gpr_log(GPR_ERROR, "Cannot find public key in X509 cert."); - } - -end: - BIO_free(bio); - X509_free(x509); - return result; -} - -static BIGNUM *bignum_from_base64(grpc_exec_ctx *exec_ctx, const char *b64) { - BIGNUM *result = NULL; - grpc_slice bin; - - if (b64 == NULL) return NULL; - bin = grpc_base64_decode(exec_ctx, b64, 1); - if (GRPC_SLICE_IS_EMPTY(bin)) { - gpr_log(GPR_ERROR, "Invalid base64 for big num."); - return NULL; - } - result = BN_bin2bn(GRPC_SLICE_START_PTR(bin), - TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), NULL); - grpc_slice_unref_internal(exec_ctx, bin); - return result; -} - -#if OPENSSL_VERSION_NUMBER < 0x10100000L - -// Provide compatibility across OpenSSL 1.02 and 1.1. -static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { - /* If the fields n and e in r are NULL, the corresponding input - * parameters MUST be non-NULL for n and e. d may be - * left NULL (in case only the public key is used). - */ - if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) { - return 0; - } - - if (n != NULL) { - BN_free(r->n); - r->n = n; - } - if (e != NULL) { - BN_free(r->e); - r->e = e; - } - if (d != NULL) { - BN_free(r->d); - r->d = d; - } - - return 1; -} -#endif // OPENSSL_VERSION_NUMBER < 0x10100000L - -static EVP_PKEY *pkey_from_jwk(grpc_exec_ctx *exec_ctx, const grpc_json *json, - const char *kty) { - const grpc_json *key_prop; - RSA *rsa = NULL; - EVP_PKEY *result = NULL; - BIGNUM *tmp_n = NULL; - BIGNUM *tmp_e = NULL; - - GPR_ASSERT(kty != NULL && json != NULL); - if (strcmp(kty, "RSA") != 0) { - gpr_log(GPR_ERROR, "Unsupported key type %s.", kty); - goto end; - } - rsa = RSA_new(); - if (rsa == NULL) { - gpr_log(GPR_ERROR, "Could not create rsa key."); - goto end; - } - for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) { - if (strcmp(key_prop->key, "n") == 0) { - tmp_n = - bignum_from_base64(exec_ctx, validate_string_field(key_prop, "n")); - if (tmp_n == NULL) goto end; - } else if (strcmp(key_prop->key, "e") == 0) { - tmp_e = - bignum_from_base64(exec_ctx, validate_string_field(key_prop, "e")); - if (tmp_e == NULL) goto end; - } - } - if (tmp_e == NULL || tmp_n == NULL) { - gpr_log(GPR_ERROR, "Missing RSA public key field."); - goto end; - } - if (!RSA_set0_key(rsa, tmp_n, tmp_e, NULL)) { - gpr_log(GPR_ERROR, "Cannot set RSA key from inputs."); - goto end; - } - /* RSA_set0_key takes ownership on success. */ - tmp_n = NULL; - tmp_e = NULL; - result = EVP_PKEY_new(); - EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */ - -end: - RSA_free(rsa); - BN_free(tmp_n); - BN_free(tmp_e); - return result; -} - -static EVP_PKEY *find_verification_key(grpc_exec_ctx *exec_ctx, - const grpc_json *json, - const char *header_alg, - const char *header_kid) { - const grpc_json *jkey; - const grpc_json *jwk_keys; - /* Try to parse the json as a JWK set: - https://tools.ietf.org/html/rfc7517#section-5. */ - jwk_keys = find_property_by_name(json, "keys"); - if (jwk_keys == NULL) { - /* Use the google proprietary format which is: - { : , : , ... } */ - const grpc_json *cur = find_property_by_name(json, header_kid); - if (cur == NULL) return NULL; - return extract_pkey_from_x509(cur->value); - } - - if (jwk_keys->type != GRPC_JSON_ARRAY) { - gpr_log(GPR_ERROR, - "Unexpected value type of keys property in jwks key set."); - return NULL; - } - /* Key format is specified in: - https://tools.ietf.org/html/rfc7518#section-6. */ - for (jkey = jwk_keys->child; jkey != NULL; jkey = jkey->next) { - grpc_json *key_prop; - const char *alg = NULL; - const char *kid = NULL; - const char *kty = NULL; - - if (jkey->type != GRPC_JSON_OBJECT) continue; - for (key_prop = jkey->child; key_prop != NULL; key_prop = key_prop->next) { - if (strcmp(key_prop->key, "alg") == 0 && - key_prop->type == GRPC_JSON_STRING) { - alg = key_prop->value; - } else if (strcmp(key_prop->key, "kid") == 0 && - key_prop->type == GRPC_JSON_STRING) { - kid = key_prop->value; - } else if (strcmp(key_prop->key, "kty") == 0 && - key_prop->type == GRPC_JSON_STRING) { - kty = key_prop->value; - } - } - if (alg != NULL && kid != NULL && kty != NULL && - strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) { - return pkey_from_jwk(exec_ctx, jkey, kty); - } - } - gpr_log(GPR_ERROR, - "Could not find matching key in key set for kid=%s and alg=%s", - header_kid, header_alg); - return NULL; -} - -static int verify_jwt_signature(EVP_PKEY *key, const char *alg, - grpc_slice signature, grpc_slice signed_data) { - EVP_MD_CTX *md_ctx = EVP_MD_CTX_create(); - const EVP_MD *md = evp_md_from_alg(alg); - int result = 0; - - GPR_ASSERT(md != NULL); /* Checked before. */ - if (md_ctx == NULL) { - gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX."); - goto end; - } - if (EVP_DigestVerifyInit(md_ctx, NULL, md, NULL, key) != 1) { - gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed."); - goto end; - } - if (EVP_DigestVerifyUpdate(md_ctx, GRPC_SLICE_START_PTR(signed_data), - GRPC_SLICE_LENGTH(signed_data)) != 1) { - gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed."); - goto end; - } - if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature), - GRPC_SLICE_LENGTH(signature)) != 1) { - gpr_log(GPR_ERROR, "JWT signature verification failed."); - goto end; - } - result = 1; - -end: - EVP_MD_CTX_destroy(md_ctx); - return result; -} - -static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *error) { - verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; - grpc_json *json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]); - EVP_PKEY *verification_key = NULL; - grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR; - grpc_jwt_claims *claims = NULL; - - if (json == NULL) { - status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; - goto end; - } - verification_key = - find_verification_key(exec_ctx, json, ctx->header->alg, ctx->header->kid); - if (verification_key == NULL) { - gpr_log(GPR_ERROR, "Could not find verification key with kid %s.", - ctx->header->kid); - status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; - goto end; - } - - if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature, - ctx->signed_data)) { - status = GRPC_JWT_VERIFIER_BAD_SIGNATURE; - goto end; - } - - status = grpc_jwt_claims_check(ctx->claims, ctx->audience); - if (status == GRPC_JWT_VERIFIER_OK) { - /* Pass ownership. */ - claims = ctx->claims; - ctx->claims = NULL; - } - -end: - if (json != NULL) grpc_json_destroy(json); - EVP_PKEY_free(verification_key); - ctx->user_cb(exec_ctx, ctx->user_data, status, claims); - verifier_cb_ctx_destroy(exec_ctx, ctx); -} - -static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *error) { - const grpc_json *cur; - verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; - const grpc_http_response *response = &ctx->responses[HTTP_RESPONSE_OPENID]; - grpc_json *json = json_from_http(response); - grpc_httpcli_request req; - const char *jwks_uri; - grpc_resource_quota *resource_quota = NULL; - - /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */ - if (json == NULL) goto error; - cur = find_property_by_name(json, "jwks_uri"); - if (cur == NULL) { - gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config."); - goto error; - } - jwks_uri = validate_string_field(cur, "jwks_uri"); - if (jwks_uri == NULL) goto error; - if (strstr(jwks_uri, "https://") != jwks_uri) { - gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri); - goto error; - } - jwks_uri += 8; - req.handshaker = &grpc_httpcli_ssl; - req.host = gpr_strdup(jwks_uri); - req.http.path = (char *)strchr(jwks_uri, '/'); - if (req.http.path == NULL) { - req.http.path = (char *)""; - } else { - *(req.host + (req.http.path - jwks_uri)) = '\0'; - } - - /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host - channel. This would allow us to cancel an authentication query when under - extreme memory pressure. */ - resource_quota = grpc_resource_quota_create("jwt_verifier"); - grpc_httpcli_get( - exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), - GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx), - &ctx->responses[HTTP_RESPONSE_KEYS]); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - grpc_json_destroy(json); - gpr_free(req.host); - return; - -error: - if (json != NULL) grpc_json_destroy(json); - ctx->user_cb(exec_ctx, ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, - NULL); - verifier_cb_ctx_destroy(exec_ctx, ctx); -} - -static email_key_mapping *verifier_get_mapping(grpc_jwt_verifier *v, - const char *email_domain) { - size_t i; - if (v->mappings == NULL) return NULL; - for (i = 0; i < v->num_mappings; i++) { - if (strcmp(email_domain, v->mappings[i].email_domain) == 0) { - return &v->mappings[i]; - } - } - return NULL; -} - -static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain, - const char *key_url_prefix) { - email_key_mapping *mapping = verifier_get_mapping(v, email_domain); - GPR_ASSERT(v->num_mappings < v->allocated_mappings); - if (mapping != NULL) { - gpr_free(mapping->key_url_prefix); - mapping->key_url_prefix = gpr_strdup(key_url_prefix); - return; - } - v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain); - v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix); - v->num_mappings++; - GPR_ASSERT(v->num_mappings <= v->allocated_mappings); -} - -/* Very non-sophisticated way to detect an email address. Should be good - enough for now... */ -const char *grpc_jwt_issuer_email_domain(const char *issuer) { - const char *at_sign = strchr(issuer, '@'); - if (at_sign == NULL) return NULL; - const char *email_domain = at_sign + 1; - if (*email_domain == '\0') return NULL; - const char *dot = strrchr(email_domain, '.'); - if (dot == NULL || dot == email_domain) return email_domain; - GPR_ASSERT(dot > email_domain); - /* There may be a subdomain, we just want the domain. */ - dot = (const char *)gpr_memrchr((void *)email_domain, '.', - (size_t)(dot - email_domain)); - if (dot == NULL) return email_domain; - return dot + 1; -} - -/* Takes ownership of ctx. */ -static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx, - verifier_cb_ctx *ctx) { - const char *email_domain; - grpc_closure *http_cb; - char *path_prefix = NULL; - const char *iss; - grpc_httpcli_request req; - grpc_resource_quota *resource_quota = NULL; - memset(&req, 0, sizeof(grpc_httpcli_request)); - req.handshaker = &grpc_httpcli_ssl; - http_response_index rsp_idx; - - GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL); - iss = ctx->claims->iss; - if (ctx->header->kid == NULL) { - gpr_log(GPR_ERROR, "Missing kid in jose header."); - goto error; - } - if (iss == NULL) { - gpr_log(GPR_ERROR, "Missing iss in claims."); - goto error; - } - - /* This code relies on: - https://openid.net/specs/openid-connect-discovery-1_0.html - Nobody seems to implement the account/email/webfinger part 2. of the spec - so we will rely instead on email/url mappings if we detect such an issuer. - Part 4, on the other hand is implemented by both google and salesforce. */ - email_domain = grpc_jwt_issuer_email_domain(iss); - if (email_domain != NULL) { - email_key_mapping *mapping; - GPR_ASSERT(ctx->verifier != NULL); - mapping = verifier_get_mapping(ctx->verifier, email_domain); - if (mapping == NULL) { - gpr_log(GPR_ERROR, "Missing mapping for issuer email."); - goto error; - } - req.host = gpr_strdup(mapping->key_url_prefix); - path_prefix = strchr(req.host, '/'); - if (path_prefix == NULL) { - gpr_asprintf(&req.http.path, "/%s", iss); - } else { - *(path_prefix++) = '\0'; - gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss); - } - http_cb = - GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx); - rsp_idx = HTTP_RESPONSE_KEYS; - } else { - req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss); - path_prefix = strchr(req.host, '/'); - if (path_prefix == NULL) { - req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX); - } else { - *(path_prefix++) = 0; - gpr_asprintf(&req.http.path, "/%s%s", path_prefix, - GRPC_OPENID_CONFIG_URL_SUFFIX); - } - http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx, - grpc_schedule_on_exec_ctx); - rsp_idx = HTTP_RESPONSE_OPENID; - } - - /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host - channel. This would allow us to cancel an authentication query when under - extreme memory pressure. */ - resource_quota = grpc_resource_quota_create("jwt_verifier"); - grpc_httpcli_get( - exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), - http_cb, &ctx->responses[rsp_idx]); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - gpr_free(req.host); - gpr_free(req.http.path); - return; - -error: - ctx->user_cb(exec_ctx, ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, - NULL); - verifier_cb_ctx_destroy(exec_ctx, ctx); -} - -void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx, - grpc_jwt_verifier *verifier, - grpc_pollset *pollset, const char *jwt, - const char *audience, - grpc_jwt_verification_done_cb cb, - void *user_data) { - const char *dot = NULL; - grpc_json *json; - jose_header *header = NULL; - grpc_jwt_claims *claims = NULL; - grpc_slice header_buffer; - grpc_slice claims_buffer; - grpc_slice signature; - size_t signed_jwt_len; - const char *cur = jwt; - - GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL); - dot = strchr(cur, '.'); - if (dot == NULL) goto error; - json = parse_json_part_from_jwt(exec_ctx, cur, (size_t)(dot - cur), - &header_buffer); - if (json == NULL) goto error; - header = jose_header_from_json(exec_ctx, json, header_buffer); - if (header == NULL) goto error; - - cur = dot + 1; - dot = strchr(cur, '.'); - if (dot == NULL) goto error; - json = parse_json_part_from_jwt(exec_ctx, cur, (size_t)(dot - cur), - &claims_buffer); - if (json == NULL) goto error; - claims = grpc_jwt_claims_from_json(exec_ctx, json, claims_buffer); - if (claims == NULL) goto error; - - signed_jwt_len = (size_t)(dot - jwt); - cur = dot + 1; - signature = grpc_base64_decode(exec_ctx, cur, 1); - if (GRPC_SLICE_IS_EMPTY(signature)) goto error; - retrieve_key_and_verify( - exec_ctx, - verifier_cb_ctx_create(verifier, pollset, header, claims, audience, - signature, jwt, signed_jwt_len, user_data, cb)); - return; - -error: - if (header != NULL) jose_header_destroy(exec_ctx, header); - if (claims != NULL) grpc_jwt_claims_destroy(exec_ctx, claims); - cb(exec_ctx, user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL); -} - -grpc_jwt_verifier *grpc_jwt_verifier_create( - const grpc_jwt_verifier_email_domain_key_url_mapping *mappings, - size_t num_mappings) { - grpc_jwt_verifier *v = - (grpc_jwt_verifier *)gpr_zalloc(sizeof(grpc_jwt_verifier)); - grpc_httpcli_context_init(&v->http_ctx); - - /* We know at least of one mapping. */ - v->allocated_mappings = 1 + num_mappings; - v->mappings = (email_key_mapping *)gpr_malloc(v->allocated_mappings * - sizeof(email_key_mapping)); - verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN, - GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX); - /* User-Provided mappings. */ - if (mappings != NULL) { - size_t i; - for (i = 0; i < num_mappings; i++) { - verifier_put_mapping(v, mappings[i].email_domain, - mappings[i].key_url_prefix); - } - } - return v; -} - -void grpc_jwt_verifier_destroy(grpc_exec_ctx *exec_ctx, grpc_jwt_verifier *v) { - size_t i; - if (v == NULL) return; - grpc_httpcli_context_destroy(exec_ctx, &v->http_ctx); - if (v->mappings != NULL) { - for (i = 0; i < v->num_mappings; i++) { - gpr_free(v->mappings[i].email_domain); - gpr_free(v->mappings[i].key_url_prefix); - } - gpr_free(v->mappings); - } - gpr_free(v); -} diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.cc b/src/core/lib/security/credentials/jwt/jwt_verifier.cc new file mode 100644 index 0000000000..656d0920cd --- /dev/null +++ b/src/core/lib/security/credentials/jwt/jwt_verifier.cc @@ -0,0 +1,939 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/jwt/jwt_verifier.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/http/httpcli.h" +#include "src/core/lib/iomgr/polling_entity.h" +#include "src/core/lib/slice/b64.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" +#include "src/core/tsi/ssl_types.h" + +/* --- Utils. --- */ + +const char *grpc_jwt_verifier_status_to_string( + grpc_jwt_verifier_status status) { + switch (status) { + case GRPC_JWT_VERIFIER_OK: + return "OK"; + case GRPC_JWT_VERIFIER_BAD_SIGNATURE: + return "BAD_SIGNATURE"; + case GRPC_JWT_VERIFIER_BAD_FORMAT: + return "BAD_FORMAT"; + case GRPC_JWT_VERIFIER_BAD_AUDIENCE: + return "BAD_AUDIENCE"; + case GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR: + return "KEY_RETRIEVAL_ERROR"; + case GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE: + return "TIME_CONSTRAINT_FAILURE"; + case GRPC_JWT_VERIFIER_GENERIC_ERROR: + return "GENERIC_ERROR"; + default: + return "UNKNOWN"; + } +} + +static const EVP_MD *evp_md_from_alg(const char *alg) { + if (strcmp(alg, "RS256") == 0) { + return EVP_sha256(); + } else if (strcmp(alg, "RS384") == 0) { + return EVP_sha384(); + } else if (strcmp(alg, "RS512") == 0) { + return EVP_sha512(); + } else { + return NULL; + } +} + +static grpc_json *parse_json_part_from_jwt(grpc_exec_ctx *exec_ctx, + const char *str, size_t len, + grpc_slice *buffer) { + grpc_json *json; + + *buffer = grpc_base64_decode_with_len(exec_ctx, str, len, 1); + if (GRPC_SLICE_IS_EMPTY(*buffer)) { + gpr_log(GPR_ERROR, "Invalid base64."); + return NULL; + } + json = grpc_json_parse_string_with_len((char *)GRPC_SLICE_START_PTR(*buffer), + GRPC_SLICE_LENGTH(*buffer)); + if (json == NULL) { + grpc_slice_unref_internal(exec_ctx, *buffer); + gpr_log(GPR_ERROR, "JSON parsing error."); + } + return json; +} + +static const char *validate_string_field(const grpc_json *json, + const char *key) { + if (json->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); + return NULL; + } + return json->value; +} + +static gpr_timespec validate_time_field(const grpc_json *json, + const char *key) { + gpr_timespec result = gpr_time_0(GPR_CLOCK_REALTIME); + if (json->type != GRPC_JSON_NUMBER) { + gpr_log(GPR_ERROR, "Invalid %s field [%s]", key, json->value); + return result; + } + result.tv_sec = strtol(json->value, NULL, 10); + return result; +} + +/* --- JOSE header. see http://tools.ietf.org/html/rfc7515#section-4 --- */ + +typedef struct { + const char *alg; + const char *kid; + const char *typ; + /* TODO(jboeuf): Add others as needed (jku, jwk, x5u, x5c and so on...). */ + grpc_slice buffer; +} jose_header; + +static void jose_header_destroy(grpc_exec_ctx *exec_ctx, jose_header *h) { + grpc_slice_unref_internal(exec_ctx, h->buffer); + gpr_free(h); +} + +/* Takes ownership of json and buffer. */ +static jose_header *jose_header_from_json(grpc_exec_ctx *exec_ctx, + grpc_json *json, grpc_slice buffer) { + grpc_json *cur; + jose_header *h = (jose_header *)gpr_zalloc(sizeof(jose_header)); + h->buffer = buffer; + for (cur = json->child; cur != NULL; cur = cur->next) { + if (strcmp(cur->key, "alg") == 0) { + /* We only support RSA-1.5 signatures for now. + Beware of this if we add HMAC support: + https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ + */ + if (cur->type != GRPC_JSON_STRING || strncmp(cur->value, "RS", 2) || + evp_md_from_alg(cur->value) == NULL) { + gpr_log(GPR_ERROR, "Invalid alg field [%s]", cur->value); + goto error; + } + h->alg = cur->value; + } else if (strcmp(cur->key, "typ") == 0) { + h->typ = validate_string_field(cur, "typ"); + if (h->typ == NULL) goto error; + } else if (strcmp(cur->key, "kid") == 0) { + h->kid = validate_string_field(cur, "kid"); + if (h->kid == NULL) goto error; + } + } + if (h->alg == NULL) { + gpr_log(GPR_ERROR, "Missing alg field."); + goto error; + } + grpc_json_destroy(json); + h->buffer = buffer; + return h; + +error: + grpc_json_destroy(json); + jose_header_destroy(exec_ctx, h); + return NULL; +} + +/* --- JWT claims. see http://tools.ietf.org/html/rfc7519#section-4.1 */ + +struct grpc_jwt_claims { + /* Well known properties already parsed. */ + const char *sub; + const char *iss; + const char *aud; + const char *jti; + gpr_timespec iat; + gpr_timespec exp; + gpr_timespec nbf; + + grpc_json *json; + grpc_slice buffer; +}; + +void grpc_jwt_claims_destroy(grpc_exec_ctx *exec_ctx, grpc_jwt_claims *claims) { + grpc_json_destroy(claims->json); + grpc_slice_unref_internal(exec_ctx, claims->buffer); + gpr_free(claims); +} + +const grpc_json *grpc_jwt_claims_json(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->json; +} + +const char *grpc_jwt_claims_subject(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->sub; +} + +const char *grpc_jwt_claims_issuer(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->iss; +} + +const char *grpc_jwt_claims_id(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->jti; +} + +const char *grpc_jwt_claims_audience(const grpc_jwt_claims *claims) { + if (claims == NULL) return NULL; + return claims->aud; +} + +gpr_timespec grpc_jwt_claims_issued_at(const grpc_jwt_claims *claims) { + if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); + return claims->iat; +} + +gpr_timespec grpc_jwt_claims_expires_at(const grpc_jwt_claims *claims) { + if (claims == NULL) return gpr_inf_future(GPR_CLOCK_REALTIME); + return claims->exp; +} + +gpr_timespec grpc_jwt_claims_not_before(const grpc_jwt_claims *claims) { + if (claims == NULL) return gpr_inf_past(GPR_CLOCK_REALTIME); + return claims->nbf; +} + +/* Takes ownership of json and buffer even in case of failure. */ +grpc_jwt_claims *grpc_jwt_claims_from_json(grpc_exec_ctx *exec_ctx, + grpc_json *json, grpc_slice buffer) { + grpc_json *cur; + grpc_jwt_claims *claims = + (grpc_jwt_claims *)gpr_malloc(sizeof(grpc_jwt_claims)); + memset(claims, 0, sizeof(grpc_jwt_claims)); + claims->json = json; + claims->buffer = buffer; + claims->iat = gpr_inf_past(GPR_CLOCK_REALTIME); + claims->nbf = gpr_inf_past(GPR_CLOCK_REALTIME); + claims->exp = gpr_inf_future(GPR_CLOCK_REALTIME); + + /* Per the spec, all fields are optional. */ + for (cur = json->child; cur != NULL; cur = cur->next) { + if (strcmp(cur->key, "sub") == 0) { + claims->sub = validate_string_field(cur, "sub"); + if (claims->sub == NULL) goto error; + } else if (strcmp(cur->key, "iss") == 0) { + claims->iss = validate_string_field(cur, "iss"); + if (claims->iss == NULL) goto error; + } else if (strcmp(cur->key, "aud") == 0) { + claims->aud = validate_string_field(cur, "aud"); + if (claims->aud == NULL) goto error; + } else if (strcmp(cur->key, "jti") == 0) { + claims->jti = validate_string_field(cur, "jti"); + if (claims->jti == NULL) goto error; + } else if (strcmp(cur->key, "iat") == 0) { + claims->iat = validate_time_field(cur, "iat"); + if (gpr_time_cmp(claims->iat, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; + } else if (strcmp(cur->key, "exp") == 0) { + claims->exp = validate_time_field(cur, "exp"); + if (gpr_time_cmp(claims->exp, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; + } else if (strcmp(cur->key, "nbf") == 0) { + claims->nbf = validate_time_field(cur, "nbf"); + if (gpr_time_cmp(claims->nbf, gpr_time_0(GPR_CLOCK_REALTIME)) == 0) + goto error; + } + } + return claims; + +error: + grpc_jwt_claims_destroy(exec_ctx, claims); + return NULL; +} + +grpc_jwt_verifier_status grpc_jwt_claims_check(const grpc_jwt_claims *claims, + const char *audience) { + gpr_timespec skewed_now; + int audience_ok; + + GPR_ASSERT(claims != NULL); + + skewed_now = + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); + if (gpr_time_cmp(skewed_now, claims->nbf) < 0) { + gpr_log(GPR_ERROR, "JWT is not valid yet."); + return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; + } + skewed_now = + gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_clock_skew); + if (gpr_time_cmp(skewed_now, claims->exp) > 0) { + gpr_log(GPR_ERROR, "JWT is expired."); + return GRPC_JWT_VERIFIER_TIME_CONSTRAINT_FAILURE; + } + + /* This should be probably up to the upper layer to decide but let's harcode + the 99% use case here for email issuers, where the JWT must be self + issued. */ + if (grpc_jwt_issuer_email_domain(claims->iss) != NULL && + claims->sub != NULL && strcmp(claims->iss, claims->sub) != 0) { + gpr_log(GPR_ERROR, + "Email issuer (%s) cannot assert another subject (%s) than itself.", + claims->iss, claims->sub); + return GRPC_JWT_VERIFIER_BAD_SUBJECT; + } + + if (audience == NULL) { + audience_ok = claims->aud == NULL; + } else { + audience_ok = claims->aud != NULL && strcmp(audience, claims->aud) == 0; + } + if (!audience_ok) { + gpr_log(GPR_ERROR, "Audience mismatch: expected %s and found %s.", + audience == NULL ? "NULL" : audience, + claims->aud == NULL ? "NULL" : claims->aud); + return GRPC_JWT_VERIFIER_BAD_AUDIENCE; + } + return GRPC_JWT_VERIFIER_OK; +} + +/* --- verifier_cb_ctx object. --- */ + +typedef enum { + HTTP_RESPONSE_OPENID = 0, + HTTP_RESPONSE_KEYS, + HTTP_RESPONSE_COUNT /* must be last */ +} http_response_index; + +typedef struct { + grpc_jwt_verifier *verifier; + grpc_polling_entity pollent; + jose_header *header; + grpc_jwt_claims *claims; + char *audience; + grpc_slice signature; + grpc_slice signed_data; + void *user_data; + grpc_jwt_verification_done_cb user_cb; + grpc_http_response responses[HTTP_RESPONSE_COUNT]; +} verifier_cb_ctx; + +/* Takes ownership of the header, claims and signature. */ +static verifier_cb_ctx *verifier_cb_ctx_create( + grpc_jwt_verifier *verifier, grpc_pollset *pollset, jose_header *header, + grpc_jwt_claims *claims, const char *audience, grpc_slice signature, + const char *signed_jwt, size_t signed_jwt_len, void *user_data, + grpc_jwt_verification_done_cb cb) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + verifier_cb_ctx *ctx = (verifier_cb_ctx *)gpr_zalloc(sizeof(verifier_cb_ctx)); + ctx->verifier = verifier; + ctx->pollent = grpc_polling_entity_create_from_pollset(pollset); + ctx->header = header; + ctx->audience = gpr_strdup(audience); + ctx->claims = claims; + ctx->signature = signature; + ctx->signed_data = grpc_slice_from_copied_buffer(signed_jwt, signed_jwt_len); + ctx->user_data = user_data; + ctx->user_cb = cb; + grpc_exec_ctx_finish(&exec_ctx); + return ctx; +} + +void verifier_cb_ctx_destroy(grpc_exec_ctx *exec_ctx, verifier_cb_ctx *ctx) { + if (ctx->audience != NULL) gpr_free(ctx->audience); + if (ctx->claims != NULL) grpc_jwt_claims_destroy(exec_ctx, ctx->claims); + grpc_slice_unref_internal(exec_ctx, ctx->signature); + grpc_slice_unref_internal(exec_ctx, ctx->signed_data); + jose_header_destroy(exec_ctx, ctx->header); + for (size_t i = 0; i < HTTP_RESPONSE_COUNT; i++) { + grpc_http_response_destroy(&ctx->responses[i]); + } + /* TODO: see what to do with claims... */ + gpr_free(ctx); +} + +/* --- grpc_jwt_verifier object. --- */ + +/* Clock skew defaults to one minute. */ +gpr_timespec grpc_jwt_verifier_clock_skew = {60, 0, GPR_TIMESPAN}; + +/* Max delay defaults to one minute. */ +gpr_timespec grpc_jwt_verifier_max_delay = {60, 0, GPR_TIMESPAN}; + +typedef struct { + char *email_domain; + char *key_url_prefix; +} email_key_mapping; + +struct grpc_jwt_verifier { + email_key_mapping *mappings; + size_t num_mappings; /* Should be very few, linear search ok. */ + size_t allocated_mappings; + grpc_httpcli_context http_ctx; +}; + +static grpc_json *json_from_http(const grpc_httpcli_response *response) { + grpc_json *json = NULL; + + if (response == NULL) { + gpr_log(GPR_ERROR, "HTTP response is NULL."); + return NULL; + } + if (response->status != 200) { + gpr_log(GPR_ERROR, "Call to http server failed with error %d.", + response->status); + return NULL; + } + + json = grpc_json_parse_string_with_len(response->body, response->body_length); + if (json == NULL) { + gpr_log(GPR_ERROR, "Invalid JSON found in response."); + } + return json; +} + +static const grpc_json *find_property_by_name(const grpc_json *json, + const char *name) { + const grpc_json *cur; + for (cur = json->child; cur != NULL; cur = cur->next) { + if (strcmp(cur->key, name) == 0) return cur; + } + return NULL; +} + +static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) { + X509 *x509 = NULL; + EVP_PKEY *result = NULL; + BIO *bio = BIO_new(BIO_s_mem()); + size_t len = strlen(x509_str); + GPR_ASSERT(len < INT_MAX); + BIO_write(bio, x509_str, (int)len); + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509 == NULL) { + gpr_log(GPR_ERROR, "Unable to parse x509 cert."); + goto end; + } + result = X509_get_pubkey(x509); + if (result == NULL) { + gpr_log(GPR_ERROR, "Cannot find public key in X509 cert."); + } + +end: + BIO_free(bio); + X509_free(x509); + return result; +} + +static BIGNUM *bignum_from_base64(grpc_exec_ctx *exec_ctx, const char *b64) { + BIGNUM *result = NULL; + grpc_slice bin; + + if (b64 == NULL) return NULL; + bin = grpc_base64_decode(exec_ctx, b64, 1); + if (GRPC_SLICE_IS_EMPTY(bin)) { + gpr_log(GPR_ERROR, "Invalid base64 for big num."); + return NULL; + } + result = BN_bin2bn(GRPC_SLICE_START_PTR(bin), + TSI_SIZE_AS_SIZE(GRPC_SLICE_LENGTH(bin)), NULL); + grpc_slice_unref_internal(exec_ctx, bin); + return result; +} + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + +// Provide compatibility across OpenSSL 1.02 and 1.1. +static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) { + /* If the fields n and e in r are NULL, the corresponding input + * parameters MUST be non-NULL for n and e. d may be + * left NULL (in case only the public key is used). + */ + if ((r->n == NULL && n == NULL) || (r->e == NULL && e == NULL)) { + return 0; + } + + if (n != NULL) { + BN_free(r->n); + r->n = n; + } + if (e != NULL) { + BN_free(r->e); + r->e = e; + } + if (d != NULL) { + BN_free(r->d); + r->d = d; + } + + return 1; +} +#endif // OPENSSL_VERSION_NUMBER < 0x10100000L + +static EVP_PKEY *pkey_from_jwk(grpc_exec_ctx *exec_ctx, const grpc_json *json, + const char *kty) { + const grpc_json *key_prop; + RSA *rsa = NULL; + EVP_PKEY *result = NULL; + BIGNUM *tmp_n = NULL; + BIGNUM *tmp_e = NULL; + + GPR_ASSERT(kty != NULL && json != NULL); + if (strcmp(kty, "RSA") != 0) { + gpr_log(GPR_ERROR, "Unsupported key type %s.", kty); + goto end; + } + rsa = RSA_new(); + if (rsa == NULL) { + gpr_log(GPR_ERROR, "Could not create rsa key."); + goto end; + } + for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) { + if (strcmp(key_prop->key, "n") == 0) { + tmp_n = + bignum_from_base64(exec_ctx, validate_string_field(key_prop, "n")); + if (tmp_n == NULL) goto end; + } else if (strcmp(key_prop->key, "e") == 0) { + tmp_e = + bignum_from_base64(exec_ctx, validate_string_field(key_prop, "e")); + if (tmp_e == NULL) goto end; + } + } + if (tmp_e == NULL || tmp_n == NULL) { + gpr_log(GPR_ERROR, "Missing RSA public key field."); + goto end; + } + if (!RSA_set0_key(rsa, tmp_n, tmp_e, NULL)) { + gpr_log(GPR_ERROR, "Cannot set RSA key from inputs."); + goto end; + } + /* RSA_set0_key takes ownership on success. */ + tmp_n = NULL; + tmp_e = NULL; + result = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */ + +end: + RSA_free(rsa); + BN_free(tmp_n); + BN_free(tmp_e); + return result; +} + +static EVP_PKEY *find_verification_key(grpc_exec_ctx *exec_ctx, + const grpc_json *json, + const char *header_alg, + const char *header_kid) { + const grpc_json *jkey; + const grpc_json *jwk_keys; + /* Try to parse the json as a JWK set: + https://tools.ietf.org/html/rfc7517#section-5. */ + jwk_keys = find_property_by_name(json, "keys"); + if (jwk_keys == NULL) { + /* Use the google proprietary format which is: + { : , : , ... } */ + const grpc_json *cur = find_property_by_name(json, header_kid); + if (cur == NULL) return NULL; + return extract_pkey_from_x509(cur->value); + } + + if (jwk_keys->type != GRPC_JSON_ARRAY) { + gpr_log(GPR_ERROR, + "Unexpected value type of keys property in jwks key set."); + return NULL; + } + /* Key format is specified in: + https://tools.ietf.org/html/rfc7518#section-6. */ + for (jkey = jwk_keys->child; jkey != NULL; jkey = jkey->next) { + grpc_json *key_prop; + const char *alg = NULL; + const char *kid = NULL; + const char *kty = NULL; + + if (jkey->type != GRPC_JSON_OBJECT) continue; + for (key_prop = jkey->child; key_prop != NULL; key_prop = key_prop->next) { + if (strcmp(key_prop->key, "alg") == 0 && + key_prop->type == GRPC_JSON_STRING) { + alg = key_prop->value; + } else if (strcmp(key_prop->key, "kid") == 0 && + key_prop->type == GRPC_JSON_STRING) { + kid = key_prop->value; + } else if (strcmp(key_prop->key, "kty") == 0 && + key_prop->type == GRPC_JSON_STRING) { + kty = key_prop->value; + } + } + if (alg != NULL && kid != NULL && kty != NULL && + strcmp(kid, header_kid) == 0 && strcmp(alg, header_alg) == 0) { + return pkey_from_jwk(exec_ctx, jkey, kty); + } + } + gpr_log(GPR_ERROR, + "Could not find matching key in key set for kid=%s and alg=%s", + header_kid, header_alg); + return NULL; +} + +static int verify_jwt_signature(EVP_PKEY *key, const char *alg, + grpc_slice signature, grpc_slice signed_data) { + EVP_MD_CTX *md_ctx = EVP_MD_CTX_create(); + const EVP_MD *md = evp_md_from_alg(alg); + int result = 0; + + GPR_ASSERT(md != NULL); /* Checked before. */ + if (md_ctx == NULL) { + gpr_log(GPR_ERROR, "Could not create EVP_MD_CTX."); + goto end; + } + if (EVP_DigestVerifyInit(md_ctx, NULL, md, NULL, key) != 1) { + gpr_log(GPR_ERROR, "EVP_DigestVerifyInit failed."); + goto end; + } + if (EVP_DigestVerifyUpdate(md_ctx, GRPC_SLICE_START_PTR(signed_data), + GRPC_SLICE_LENGTH(signed_data)) != 1) { + gpr_log(GPR_ERROR, "EVP_DigestVerifyUpdate failed."); + goto end; + } + if (EVP_DigestVerifyFinal(md_ctx, GRPC_SLICE_START_PTR(signature), + GRPC_SLICE_LENGTH(signature)) != 1) { + gpr_log(GPR_ERROR, "JWT signature verification failed."); + goto end; + } + result = 1; + +end: + EVP_MD_CTX_destroy(md_ctx); + return result; +} + +static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *error) { + verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; + grpc_json *json = json_from_http(&ctx->responses[HTTP_RESPONSE_KEYS]); + EVP_PKEY *verification_key = NULL; + grpc_jwt_verifier_status status = GRPC_JWT_VERIFIER_GENERIC_ERROR; + grpc_jwt_claims *claims = NULL; + + if (json == NULL) { + status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; + goto end; + } + verification_key = + find_verification_key(exec_ctx, json, ctx->header->alg, ctx->header->kid); + if (verification_key == NULL) { + gpr_log(GPR_ERROR, "Could not find verification key with kid %s.", + ctx->header->kid); + status = GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR; + goto end; + } + + if (!verify_jwt_signature(verification_key, ctx->header->alg, ctx->signature, + ctx->signed_data)) { + status = GRPC_JWT_VERIFIER_BAD_SIGNATURE; + goto end; + } + + status = grpc_jwt_claims_check(ctx->claims, ctx->audience); + if (status == GRPC_JWT_VERIFIER_OK) { + /* Pass ownership. */ + claims = ctx->claims; + ctx->claims = NULL; + } + +end: + if (json != NULL) grpc_json_destroy(json); + EVP_PKEY_free(verification_key); + ctx->user_cb(exec_ctx, ctx->user_data, status, claims); + verifier_cb_ctx_destroy(exec_ctx, ctx); +} + +static void on_openid_config_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *error) { + const grpc_json *cur; + verifier_cb_ctx *ctx = (verifier_cb_ctx *)user_data; + const grpc_http_response *response = &ctx->responses[HTTP_RESPONSE_OPENID]; + grpc_json *json = json_from_http(response); + grpc_httpcli_request req; + const char *jwks_uri; + grpc_resource_quota *resource_quota = NULL; + + /* TODO(jboeuf): Cache the jwks_uri in order to avoid this hop next time. */ + if (json == NULL) goto error; + cur = find_property_by_name(json, "jwks_uri"); + if (cur == NULL) { + gpr_log(GPR_ERROR, "Could not find jwks_uri in openid config."); + goto error; + } + jwks_uri = validate_string_field(cur, "jwks_uri"); + if (jwks_uri == NULL) goto error; + if (strstr(jwks_uri, "https://") != jwks_uri) { + gpr_log(GPR_ERROR, "Invalid non https jwks_uri: %s.", jwks_uri); + goto error; + } + jwks_uri += 8; + req.handshaker = &grpc_httpcli_ssl; + req.host = gpr_strdup(jwks_uri); + req.http.path = (char *)strchr(jwks_uri, '/'); + if (req.http.path == NULL) { + req.http.path = (char *)""; + } else { + *(req.host + (req.http.path - jwks_uri)) = '\0'; + } + + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + resource_quota = grpc_resource_quota_create("jwt_verifier"); + grpc_httpcli_get( + exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), + GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx), + &ctx->responses[HTTP_RESPONSE_KEYS]); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + grpc_json_destroy(json); + gpr_free(req.host); + return; + +error: + if (json != NULL) grpc_json_destroy(json); + ctx->user_cb(exec_ctx, ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, + NULL); + verifier_cb_ctx_destroy(exec_ctx, ctx); +} + +static email_key_mapping *verifier_get_mapping(grpc_jwt_verifier *v, + const char *email_domain) { + size_t i; + if (v->mappings == NULL) return NULL; + for (i = 0; i < v->num_mappings; i++) { + if (strcmp(email_domain, v->mappings[i].email_domain) == 0) { + return &v->mappings[i]; + } + } + return NULL; +} + +static void verifier_put_mapping(grpc_jwt_verifier *v, const char *email_domain, + const char *key_url_prefix) { + email_key_mapping *mapping = verifier_get_mapping(v, email_domain); + GPR_ASSERT(v->num_mappings < v->allocated_mappings); + if (mapping != NULL) { + gpr_free(mapping->key_url_prefix); + mapping->key_url_prefix = gpr_strdup(key_url_prefix); + return; + } + v->mappings[v->num_mappings].email_domain = gpr_strdup(email_domain); + v->mappings[v->num_mappings].key_url_prefix = gpr_strdup(key_url_prefix); + v->num_mappings++; + GPR_ASSERT(v->num_mappings <= v->allocated_mappings); +} + +/* Very non-sophisticated way to detect an email address. Should be good + enough for now... */ +const char *grpc_jwt_issuer_email_domain(const char *issuer) { + const char *at_sign = strchr(issuer, '@'); + if (at_sign == NULL) return NULL; + const char *email_domain = at_sign + 1; + if (*email_domain == '\0') return NULL; + const char *dot = strrchr(email_domain, '.'); + if (dot == NULL || dot == email_domain) return email_domain; + GPR_ASSERT(dot > email_domain); + /* There may be a subdomain, we just want the domain. */ + dot = (const char *)gpr_memrchr((void *)email_domain, '.', + (size_t)(dot - email_domain)); + if (dot == NULL) return email_domain; + return dot + 1; +} + +/* Takes ownership of ctx. */ +static void retrieve_key_and_verify(grpc_exec_ctx *exec_ctx, + verifier_cb_ctx *ctx) { + const char *email_domain; + grpc_closure *http_cb; + char *path_prefix = NULL; + const char *iss; + grpc_httpcli_request req; + grpc_resource_quota *resource_quota = NULL; + memset(&req, 0, sizeof(grpc_httpcli_request)); + req.handshaker = &grpc_httpcli_ssl; + http_response_index rsp_idx; + + GPR_ASSERT(ctx != NULL && ctx->header != NULL && ctx->claims != NULL); + iss = ctx->claims->iss; + if (ctx->header->kid == NULL) { + gpr_log(GPR_ERROR, "Missing kid in jose header."); + goto error; + } + if (iss == NULL) { + gpr_log(GPR_ERROR, "Missing iss in claims."); + goto error; + } + + /* This code relies on: + https://openid.net/specs/openid-connect-discovery-1_0.html + Nobody seems to implement the account/email/webfinger part 2. of the spec + so we will rely instead on email/url mappings if we detect such an issuer. + Part 4, on the other hand is implemented by both google and salesforce. */ + email_domain = grpc_jwt_issuer_email_domain(iss); + if (email_domain != NULL) { + email_key_mapping *mapping; + GPR_ASSERT(ctx->verifier != NULL); + mapping = verifier_get_mapping(ctx->verifier, email_domain); + if (mapping == NULL) { + gpr_log(GPR_ERROR, "Missing mapping for issuer email."); + goto error; + } + req.host = gpr_strdup(mapping->key_url_prefix); + path_prefix = strchr(req.host, '/'); + if (path_prefix == NULL) { + gpr_asprintf(&req.http.path, "/%s", iss); + } else { + *(path_prefix++) = '\0'; + gpr_asprintf(&req.http.path, "/%s/%s", path_prefix, iss); + } + http_cb = + GRPC_CLOSURE_CREATE(on_keys_retrieved, ctx, grpc_schedule_on_exec_ctx); + rsp_idx = HTTP_RESPONSE_KEYS; + } else { + req.host = gpr_strdup(strstr(iss, "https://") == iss ? iss + 8 : iss); + path_prefix = strchr(req.host, '/'); + if (path_prefix == NULL) { + req.http.path = gpr_strdup(GRPC_OPENID_CONFIG_URL_SUFFIX); + } else { + *(path_prefix++) = 0; + gpr_asprintf(&req.http.path, "/%s%s", path_prefix, + GRPC_OPENID_CONFIG_URL_SUFFIX); + } + http_cb = GRPC_CLOSURE_CREATE(on_openid_config_retrieved, ctx, + grpc_schedule_on_exec_ctx); + rsp_idx = HTTP_RESPONSE_OPENID; + } + + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + resource_quota = grpc_resource_quota_create("jwt_verifier"); + grpc_httpcli_get( + exec_ctx, &ctx->verifier->http_ctx, &ctx->pollent, resource_quota, &req, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), grpc_jwt_verifier_max_delay), + http_cb, &ctx->responses[rsp_idx]); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + gpr_free(req.host); + gpr_free(req.http.path); + return; + +error: + ctx->user_cb(exec_ctx, ctx->user_data, GRPC_JWT_VERIFIER_KEY_RETRIEVAL_ERROR, + NULL); + verifier_cb_ctx_destroy(exec_ctx, ctx); +} + +void grpc_jwt_verifier_verify(grpc_exec_ctx *exec_ctx, + grpc_jwt_verifier *verifier, + grpc_pollset *pollset, const char *jwt, + const char *audience, + grpc_jwt_verification_done_cb cb, + void *user_data) { + const char *dot = NULL; + grpc_json *json; + jose_header *header = NULL; + grpc_jwt_claims *claims = NULL; + grpc_slice header_buffer; + grpc_slice claims_buffer; + grpc_slice signature; + size_t signed_jwt_len; + const char *cur = jwt; + + GPR_ASSERT(verifier != NULL && jwt != NULL && audience != NULL && cb != NULL); + dot = strchr(cur, '.'); + if (dot == NULL) goto error; + json = parse_json_part_from_jwt(exec_ctx, cur, (size_t)(dot - cur), + &header_buffer); + if (json == NULL) goto error; + header = jose_header_from_json(exec_ctx, json, header_buffer); + if (header == NULL) goto error; + + cur = dot + 1; + dot = strchr(cur, '.'); + if (dot == NULL) goto error; + json = parse_json_part_from_jwt(exec_ctx, cur, (size_t)(dot - cur), + &claims_buffer); + if (json == NULL) goto error; + claims = grpc_jwt_claims_from_json(exec_ctx, json, claims_buffer); + if (claims == NULL) goto error; + + signed_jwt_len = (size_t)(dot - jwt); + cur = dot + 1; + signature = grpc_base64_decode(exec_ctx, cur, 1); + if (GRPC_SLICE_IS_EMPTY(signature)) goto error; + retrieve_key_and_verify( + exec_ctx, + verifier_cb_ctx_create(verifier, pollset, header, claims, audience, + signature, jwt, signed_jwt_len, user_data, cb)); + return; + +error: + if (header != NULL) jose_header_destroy(exec_ctx, header); + if (claims != NULL) grpc_jwt_claims_destroy(exec_ctx, claims); + cb(exec_ctx, user_data, GRPC_JWT_VERIFIER_BAD_FORMAT, NULL); +} + +grpc_jwt_verifier *grpc_jwt_verifier_create( + const grpc_jwt_verifier_email_domain_key_url_mapping *mappings, + size_t num_mappings) { + grpc_jwt_verifier *v = + (grpc_jwt_verifier *)gpr_zalloc(sizeof(grpc_jwt_verifier)); + grpc_httpcli_context_init(&v->http_ctx); + + /* We know at least of one mapping. */ + v->allocated_mappings = 1 + num_mappings; + v->mappings = (email_key_mapping *)gpr_malloc(v->allocated_mappings * + sizeof(email_key_mapping)); + verifier_put_mapping(v, GRPC_GOOGLE_SERVICE_ACCOUNTS_EMAIL_DOMAIN, + GRPC_GOOGLE_SERVICE_ACCOUNTS_KEY_URL_PREFIX); + /* User-Provided mappings. */ + if (mappings != NULL) { + size_t i; + for (i = 0; i < num_mappings; i++) { + verifier_put_mapping(v, mappings[i].email_domain, + mappings[i].key_url_prefix); + } + } + return v; +} + +void grpc_jwt_verifier_destroy(grpc_exec_ctx *exec_ctx, grpc_jwt_verifier *v) { + size_t i; + if (v == NULL) return; + grpc_httpcli_context_destroy(exec_ctx, &v->http_ctx); + if (v->mappings != NULL) { + for (i = 0; i < v->num_mappings; i++) { + gpr_free(v->mappings[i].email_domain); + gpr_free(v->mappings[i].key_url_prefix); + } + gpr_free(v->mappings); + } + gpr_free(v); +} diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c deleted file mode 100644 index 0a801bec82..0000000000 --- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" - -#include - -#include "src/core/lib/security/util/json_util.h" -#include "src/core/lib/surface/api_trace.h" - -#include -#include -#include - -// -// Auth Refresh Token. -// - -int grpc_auth_refresh_token_is_valid( - const grpc_auth_refresh_token *refresh_token) { - return (refresh_token != NULL) && - strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID); -} - -grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json( - const grpc_json *json) { - grpc_auth_refresh_token result; - const char *prop_value; - int success = 0; - - memset(&result, 0, sizeof(grpc_auth_refresh_token)); - result.type = GRPC_AUTH_JSON_TYPE_INVALID; - if (json == NULL) { - gpr_log(GPR_ERROR, "Invalid json."); - goto end; - } - - prop_value = grpc_json_get_string_property(json, "type"); - if (prop_value == NULL || - strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) { - goto end; - } - result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER; - - if (!grpc_copy_json_string_property(json, "client_secret", - &result.client_secret) || - !grpc_copy_json_string_property(json, "client_id", &result.client_id) || - !grpc_copy_json_string_property(json, "refresh_token", - &result.refresh_token)) { - goto end; - } - success = 1; - -end: - if (!success) grpc_auth_refresh_token_destruct(&result); - return result; -} - -grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( - const char *json_string) { - char *scratchpad = gpr_strdup(json_string); - grpc_json *json = grpc_json_parse_string(scratchpad); - grpc_auth_refresh_token result = - grpc_auth_refresh_token_create_from_json(json); - if (json != NULL) grpc_json_destroy(json); - gpr_free(scratchpad); - return result; -} - -void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) { - if (refresh_token == NULL) return; - refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID; - if (refresh_token->client_id != NULL) { - gpr_free(refresh_token->client_id); - refresh_token->client_id = NULL; - } - if (refresh_token->client_secret != NULL) { - gpr_free(refresh_token->client_secret); - refresh_token->client_secret = NULL; - } - if (refresh_token->refresh_token != NULL) { - gpr_free(refresh_token->refresh_token); - refresh_token->refresh_token = NULL; - } -} - -// -// Oauth2 Token Fetcher credentials. -// - -static void oauth2_token_fetcher_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_oauth2_token_fetcher_credentials *c = - (grpc_oauth2_token_fetcher_credentials *)creds; - GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); - gpr_mu_destroy(&c->mu); - grpc_pollset_set_destroy(exec_ctx, - grpc_polling_entity_pollset_set(&c->pollent)); - grpc_httpcli_context_destroy(exec_ctx, &c->httpcli_context); -} - -grpc_credentials_status -grpc_oauth2_token_fetcher_credentials_parse_server_response( - grpc_exec_ctx *exec_ctx, const grpc_http_response *response, - grpc_mdelem *token_md, gpr_timespec *token_lifetime) { - char *null_terminated_body = NULL; - char *new_access_token = NULL; - grpc_credentials_status status = GRPC_CREDENTIALS_OK; - grpc_json *json = NULL; - - if (response == NULL) { - gpr_log(GPR_ERROR, "Received NULL response."); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } - - if (response->body_length > 0) { - null_terminated_body = (char *)gpr_malloc(response->body_length + 1); - null_terminated_body[response->body_length] = '\0'; - memcpy(null_terminated_body, response->body, response->body_length); - } - - if (response->status != 200) { - gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].", - response->status, - null_terminated_body != NULL ? null_terminated_body : ""); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } else { - grpc_json *access_token = NULL; - grpc_json *token_type = NULL; - grpc_json *expires_in = NULL; - grpc_json *ptr; - json = grpc_json_parse_string(null_terminated_body); - if (json == NULL) { - gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } - if (json->type != GRPC_JSON_OBJECT) { - gpr_log(GPR_ERROR, "Response should be a JSON object"); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } - for (ptr = json->child; ptr; ptr = ptr->next) { - if (strcmp(ptr->key, "access_token") == 0) { - access_token = ptr; - } else if (strcmp(ptr->key, "token_type") == 0) { - token_type = ptr; - } else if (strcmp(ptr->key, "expires_in") == 0) { - expires_in = ptr; - } - } - if (access_token == NULL || access_token->type != GRPC_JSON_STRING) { - gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON."); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } - if (token_type == NULL || token_type->type != GRPC_JSON_STRING) { - gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON."); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } - if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) { - gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON."); - status = GRPC_CREDENTIALS_ERROR; - goto end; - } - gpr_asprintf(&new_access_token, "%s %s", token_type->value, - access_token->value); - token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10); - token_lifetime->tv_nsec = 0; - token_lifetime->clock_type = GPR_TIMESPAN; - if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(exec_ctx, *token_md); - *token_md = grpc_mdelem_from_slices( - exec_ctx, - grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), - grpc_slice_from_copied_string(new_access_token)); - status = GRPC_CREDENTIALS_OK; - } - -end: - if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) { - GRPC_MDELEM_UNREF(exec_ctx, *token_md); - *token_md = GRPC_MDNULL; - } - if (null_terminated_body != NULL) gpr_free(null_terminated_body); - if (new_access_token != NULL) gpr_free(new_access_token); - if (json != NULL) grpc_json_destroy(json); - return status; -} - -static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_error *error) { - GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); - grpc_credentials_metadata_request *r = - (grpc_credentials_metadata_request *)user_data; - grpc_oauth2_token_fetcher_credentials *c = - (grpc_oauth2_token_fetcher_credentials *)r->creds; - grpc_mdelem access_token_md = GRPC_MDNULL; - gpr_timespec token_lifetime; - grpc_credentials_status status = - grpc_oauth2_token_fetcher_credentials_parse_server_response( - exec_ctx, &r->response, &access_token_md, &token_lifetime); - // Update cache and grab list of pending requests. - gpr_mu_lock(&c->mu); - c->token_fetch_pending = false; - c->access_token_md = GRPC_MDELEM_REF(access_token_md); - c->token_expiration = - status == GRPC_CREDENTIALS_OK - ? gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime) - : gpr_inf_past(GPR_CLOCK_REALTIME); - grpc_oauth2_pending_get_request_metadata *pending_request = - c->pending_requests; - c->pending_requests = NULL; - gpr_mu_unlock(&c->mu); - // Invoke callbacks for all pending requests. - while (pending_request != NULL) { - if (status == GRPC_CREDENTIALS_OK) { - grpc_credentials_mdelem_array_add(pending_request->md_array, - access_token_md); - } else { - error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Error occured when fetching oauth2 token.", &error, 1); - } - GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, error); - grpc_polling_entity_del_from_pollset_set( - exec_ctx, pending_request->pollent, - grpc_polling_entity_pollset_set(&c->pollent)); - grpc_oauth2_pending_get_request_metadata *prev = pending_request; - pending_request = pending_request->next; - gpr_free(prev); - } - GRPC_MDELEM_UNREF(exec_ctx, access_token_md); - grpc_call_credentials_unref(exec_ctx, r->creds); - grpc_credentials_metadata_request_destroy(exec_ctx, r); -} - -static bool oauth2_token_fetcher_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_oauth2_token_fetcher_credentials *c = - (grpc_oauth2_token_fetcher_credentials *)creds; - // Check if we can use the cached token. - gpr_timespec refresh_threshold = gpr_time_from_seconds( - GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); - grpc_mdelem cached_access_token_md = GRPC_MDNULL; - gpr_mu_lock(&c->mu); - if (!GRPC_MDISNULL(c->access_token_md) && - (gpr_time_cmp( - gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)), - refresh_threshold) > 0)) { - cached_access_token_md = GRPC_MDELEM_REF(c->access_token_md); - } - if (!GRPC_MDISNULL(cached_access_token_md)) { - gpr_mu_unlock(&c->mu); - grpc_credentials_mdelem_array_add(md_array, cached_access_token_md); - GRPC_MDELEM_UNREF(exec_ctx, cached_access_token_md); - return true; - } - // Couldn't get the token from the cache. - // Add request to c->pending_requests and start a new fetch if needed. - grpc_oauth2_pending_get_request_metadata *pending_request = - (grpc_oauth2_pending_get_request_metadata *)gpr_malloc( - sizeof(*pending_request)); - pending_request->md_array = md_array; - pending_request->on_request_metadata = on_request_metadata; - pending_request->pollent = pollent; - grpc_polling_entity_add_to_pollset_set( - exec_ctx, pollent, grpc_polling_entity_pollset_set(&c->pollent)); - pending_request->next = c->pending_requests; - c->pending_requests = pending_request; - bool start_fetch = false; - if (!c->token_fetch_pending) { - c->token_fetch_pending = true; - start_fetch = true; - } - gpr_mu_unlock(&c->mu); - if (start_fetch) { - grpc_call_credentials_ref(creds); - c->fetch_func( - exec_ctx, grpc_credentials_metadata_request_create(creds), - &c->httpcli_context, &c->pollent, on_oauth2_token_fetcher_http_response, - gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), refresh_threshold)); - } - return false; -} - -static void oauth2_token_fetcher_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - grpc_oauth2_token_fetcher_credentials *c = - (grpc_oauth2_token_fetcher_credentials *)creds; - gpr_mu_lock(&c->mu); - grpc_oauth2_pending_get_request_metadata *prev = NULL; - grpc_oauth2_pending_get_request_metadata *pending_request = - c->pending_requests; - while (pending_request != NULL) { - if (pending_request->md_array == md_array) { - // Remove matching pending request from the list. - if (prev != NULL) { - prev->next = pending_request->next; - } else { - c->pending_requests = pending_request->next; - } - // Invoke the callback immediately with an error. - GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, - GRPC_ERROR_REF(error)); - gpr_free(pending_request); - break; - } - prev = pending_request; - pending_request = pending_request->next; - } - gpr_mu_unlock(&c->mu); - GRPC_ERROR_UNREF(error); -} - -static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, - grpc_fetch_oauth2_func fetch_func) { - memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials)); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; - gpr_ref_init(&c->base.refcount, 1); - gpr_mu_init(&c->mu); - c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); - c->fetch_func = fetch_func; - c->pollent = - grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()); - grpc_httpcli_context_init(&c->httpcli_context); -} - -// -// Google Compute Engine credentials. -// - -static grpc_call_credentials_vtable compute_engine_vtable = { - oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata, - oauth2_token_fetcher_cancel_get_request_metadata}; - -static void compute_engine_fetch_oauth2( - grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, - grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent, - grpc_iomgr_cb_func response_cb, gpr_timespec deadline) { - grpc_http_header header = {(char *)"Metadata-Flavor", (char *)"Google"}; - grpc_httpcli_request request; - memset(&request, 0, sizeof(grpc_httpcli_request)); - request.host = (char *)GRPC_COMPUTE_ENGINE_METADATA_HOST; - request.http.path = (char *)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH; - request.http.hdr_count = 1; - request.http.hdrs = &header; - /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host - channel. This would allow us to cancel an authentication query when under - extreme memory pressure. */ - grpc_resource_quota *resource_quota = - grpc_resource_quota_create("oauth2_credentials"); - grpc_httpcli_get( - exec_ctx, httpcli_context, pollent, resource_quota, &request, deadline, - GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), - &metadata_req->response); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); -} - -grpc_call_credentials *grpc_google_compute_engine_credentials_create( - void *reserved) { - grpc_oauth2_token_fetcher_credentials *c = - (grpc_oauth2_token_fetcher_credentials *)gpr_malloc( - sizeof(grpc_oauth2_token_fetcher_credentials)); - GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1, - (reserved)); - GPR_ASSERT(reserved == NULL); - init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2); - c->base.vtable = &compute_engine_vtable; - return &c->base; -} - -// -// Google Refresh Token credentials. -// - -static void refresh_token_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_google_refresh_token_credentials *c = - (grpc_google_refresh_token_credentials *)creds; - grpc_auth_refresh_token_destruct(&c->refresh_token); - oauth2_token_fetcher_destruct(exec_ctx, &c->base.base); -} - -static grpc_call_credentials_vtable refresh_token_vtable = { - refresh_token_destruct, oauth2_token_fetcher_get_request_metadata, - oauth2_token_fetcher_cancel_get_request_metadata}; - -static void refresh_token_fetch_oauth2( - grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, - grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent, - grpc_iomgr_cb_func response_cb, gpr_timespec deadline) { - grpc_google_refresh_token_credentials *c = - (grpc_google_refresh_token_credentials *)metadata_req->creds; - grpc_http_header header = {(char *)"Content-Type", - (char *)"application/x-www-form-urlencoded"}; - grpc_httpcli_request request; - char *body = NULL; - gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, - c->refresh_token.client_id, c->refresh_token.client_secret, - c->refresh_token.refresh_token); - memset(&request, 0, sizeof(grpc_httpcli_request)); - request.host = (char *)GRPC_GOOGLE_OAUTH2_SERVICE_HOST; - request.http.path = (char *)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH; - request.http.hdr_count = 1; - request.http.hdrs = &header; - request.handshaker = &grpc_httpcli_ssl; - /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host - channel. This would allow us to cancel an authentication query when under - extreme memory pressure. */ - grpc_resource_quota *resource_quota = - grpc_resource_quota_create("oauth2_credentials_refresh"); - grpc_httpcli_post( - exec_ctx, httpcli_context, pollent, resource_quota, &request, body, - strlen(body), deadline, - GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), - &metadata_req->response); - grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - gpr_free(body); -} - -grpc_call_credentials * -grpc_refresh_token_credentials_create_from_auth_refresh_token( - grpc_auth_refresh_token refresh_token) { - grpc_google_refresh_token_credentials *c; - if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { - gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation"); - return NULL; - } - c = (grpc_google_refresh_token_credentials *)gpr_zalloc( - sizeof(grpc_google_refresh_token_credentials)); - init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2); - c->base.base.vtable = &refresh_token_vtable; - c->refresh_token = refresh_token; - return &c->base.base; -} - -static char *create_loggable_refresh_token(grpc_auth_refresh_token *token) { - if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) { - return gpr_strdup(""); - } - char *loggable_token = NULL; - gpr_asprintf(&loggable_token, - "{\n type: %s\n client_id: %s\n client_secret: " - "\n refresh_token: \n}", - token->type, token->client_id); - return loggable_token; -} - -grpc_call_credentials *grpc_google_refresh_token_credentials_create( - const char *json_refresh_token, void *reserved) { - grpc_auth_refresh_token token = - grpc_auth_refresh_token_create_from_string(json_refresh_token); - if (GRPC_TRACER_ON(grpc_api_trace)) { - char *loggable_token = create_loggable_refresh_token(&token); - gpr_log(GPR_INFO, - "grpc_refresh_token_credentials_create(json_refresh_token=%s, " - "reserved=%p)", - loggable_token, reserved); - gpr_free(loggable_token); - } - GPR_ASSERT(reserved == NULL); - return grpc_refresh_token_credentials_create_from_auth_refresh_token(token); -} - -// -// Oauth2 Access Token credentials. -// - -static void access_token_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); -} - -static bool access_token_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - grpc_credentials_mdelem_array_add(md_array, c->access_token_md); - return true; -} - -static void access_token_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - GRPC_ERROR_UNREF(error); -} - -static grpc_call_credentials_vtable access_token_vtable = { - access_token_destruct, access_token_get_request_metadata, - access_token_cancel_get_request_metadata}; - -grpc_call_credentials *grpc_access_token_credentials_create( - const char *access_token, void *reserved) { - grpc_access_token_credentials *c = - (grpc_access_token_credentials *)gpr_zalloc( - sizeof(grpc_access_token_credentials)); - GRPC_API_TRACE( - "grpc_access_token_credentials_create(access_token=, " - "reserved=%p)", - 1, (reserved)); - GPR_ASSERT(reserved == NULL); - c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; - c->base.vtable = &access_token_vtable; - gpr_ref_init(&c->base.refcount, 1); - char *token_md_value; - gpr_asprintf(&token_md_value, "Bearer %s", access_token); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - c->access_token_md = grpc_mdelem_from_slices( - &exec_ctx, grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), - grpc_slice_from_copied_string(token_md_value)); - grpc_exec_ctx_finish(&exec_ctx); - gpr_free(token_md_value); - return &c->base; -} diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc new file mode 100644 index 0000000000..0a801bec82 --- /dev/null +++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.cc @@ -0,0 +1,539 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/oauth2/oauth2_credentials.h" + +#include + +#include "src/core/lib/security/util/json_util.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include + +// +// Auth Refresh Token. +// + +int grpc_auth_refresh_token_is_valid( + const grpc_auth_refresh_token *refresh_token) { + return (refresh_token != NULL) && + strcmp(refresh_token->type, GRPC_AUTH_JSON_TYPE_INVALID); +} + +grpc_auth_refresh_token grpc_auth_refresh_token_create_from_json( + const grpc_json *json) { + grpc_auth_refresh_token result; + const char *prop_value; + int success = 0; + + memset(&result, 0, sizeof(grpc_auth_refresh_token)); + result.type = GRPC_AUTH_JSON_TYPE_INVALID; + if (json == NULL) { + gpr_log(GPR_ERROR, "Invalid json."); + goto end; + } + + prop_value = grpc_json_get_string_property(json, "type"); + if (prop_value == NULL || + strcmp(prop_value, GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER)) { + goto end; + } + result.type = GRPC_AUTH_JSON_TYPE_AUTHORIZED_USER; + + if (!grpc_copy_json_string_property(json, "client_secret", + &result.client_secret) || + !grpc_copy_json_string_property(json, "client_id", &result.client_id) || + !grpc_copy_json_string_property(json, "refresh_token", + &result.refresh_token)) { + goto end; + } + success = 1; + +end: + if (!success) grpc_auth_refresh_token_destruct(&result); + return result; +} + +grpc_auth_refresh_token grpc_auth_refresh_token_create_from_string( + const char *json_string) { + char *scratchpad = gpr_strdup(json_string); + grpc_json *json = grpc_json_parse_string(scratchpad); + grpc_auth_refresh_token result = + grpc_auth_refresh_token_create_from_json(json); + if (json != NULL) grpc_json_destroy(json); + gpr_free(scratchpad); + return result; +} + +void grpc_auth_refresh_token_destruct(grpc_auth_refresh_token *refresh_token) { + if (refresh_token == NULL) return; + refresh_token->type = GRPC_AUTH_JSON_TYPE_INVALID; + if (refresh_token->client_id != NULL) { + gpr_free(refresh_token->client_id); + refresh_token->client_id = NULL; + } + if (refresh_token->client_secret != NULL) { + gpr_free(refresh_token->client_secret); + refresh_token->client_secret = NULL; + } + if (refresh_token->refresh_token != NULL) { + gpr_free(refresh_token->refresh_token); + refresh_token->refresh_token = NULL; + } +} + +// +// Oauth2 Token Fetcher credentials. +// + +static void oauth2_token_fetcher_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)creds; + GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); + gpr_mu_destroy(&c->mu); + grpc_pollset_set_destroy(exec_ctx, + grpc_polling_entity_pollset_set(&c->pollent)); + grpc_httpcli_context_destroy(exec_ctx, &c->httpcli_context); +} + +grpc_credentials_status +grpc_oauth2_token_fetcher_credentials_parse_server_response( + grpc_exec_ctx *exec_ctx, const grpc_http_response *response, + grpc_mdelem *token_md, gpr_timespec *token_lifetime) { + char *null_terminated_body = NULL; + char *new_access_token = NULL; + grpc_credentials_status status = GRPC_CREDENTIALS_OK; + grpc_json *json = NULL; + + if (response == NULL) { + gpr_log(GPR_ERROR, "Received NULL response."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + + if (response->body_length > 0) { + null_terminated_body = (char *)gpr_malloc(response->body_length + 1); + null_terminated_body[response->body_length] = '\0'; + memcpy(null_terminated_body, response->body, response->body_length); + } + + if (response->status != 200) { + gpr_log(GPR_ERROR, "Call to http server ended with error %d [%s].", + response->status, + null_terminated_body != NULL ? null_terminated_body : ""); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } else { + grpc_json *access_token = NULL; + grpc_json *token_type = NULL; + grpc_json *expires_in = NULL; + grpc_json *ptr; + json = grpc_json_parse_string(null_terminated_body); + if (json == NULL) { + gpr_log(GPR_ERROR, "Could not parse JSON from %s", null_terminated_body); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + if (json->type != GRPC_JSON_OBJECT) { + gpr_log(GPR_ERROR, "Response should be a JSON object"); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + for (ptr = json->child; ptr; ptr = ptr->next) { + if (strcmp(ptr->key, "access_token") == 0) { + access_token = ptr; + } else if (strcmp(ptr->key, "token_type") == 0) { + token_type = ptr; + } else if (strcmp(ptr->key, "expires_in") == 0) { + expires_in = ptr; + } + } + if (access_token == NULL || access_token->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Missing or invalid access_token in JSON."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + if (token_type == NULL || token_type->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Missing or invalid token_type in JSON."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + if (expires_in == NULL || expires_in->type != GRPC_JSON_NUMBER) { + gpr_log(GPR_ERROR, "Missing or invalid expires_in in JSON."); + status = GRPC_CREDENTIALS_ERROR; + goto end; + } + gpr_asprintf(&new_access_token, "%s %s", token_type->value, + access_token->value); + token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10); + token_lifetime->tv_nsec = 0; + token_lifetime->clock_type = GPR_TIMESPAN; + if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(exec_ctx, *token_md); + *token_md = grpc_mdelem_from_slices( + exec_ctx, + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(new_access_token)); + status = GRPC_CREDENTIALS_OK; + } + +end: + if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) { + GRPC_MDELEM_UNREF(exec_ctx, *token_md); + *token_md = GRPC_MDNULL; + } + if (null_terminated_body != NULL) gpr_free(null_terminated_body); + if (new_access_token != NULL) gpr_free(new_access_token); + if (json != NULL) grpc_json_destroy(json); + return status; +} + +static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx, + void *user_data, + grpc_error *error) { + GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); + grpc_credentials_metadata_request *r = + (grpc_credentials_metadata_request *)user_data; + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)r->creds; + grpc_mdelem access_token_md = GRPC_MDNULL; + gpr_timespec token_lifetime; + grpc_credentials_status status = + grpc_oauth2_token_fetcher_credentials_parse_server_response( + exec_ctx, &r->response, &access_token_md, &token_lifetime); + // Update cache and grab list of pending requests. + gpr_mu_lock(&c->mu); + c->token_fetch_pending = false; + c->access_token_md = GRPC_MDELEM_REF(access_token_md); + c->token_expiration = + status == GRPC_CREDENTIALS_OK + ? gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime) + : gpr_inf_past(GPR_CLOCK_REALTIME); + grpc_oauth2_pending_get_request_metadata *pending_request = + c->pending_requests; + c->pending_requests = NULL; + gpr_mu_unlock(&c->mu); + // Invoke callbacks for all pending requests. + while (pending_request != NULL) { + if (status == GRPC_CREDENTIALS_OK) { + grpc_credentials_mdelem_array_add(pending_request->md_array, + access_token_md); + } else { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Error occured when fetching oauth2 token.", &error, 1); + } + GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, error); + grpc_polling_entity_del_from_pollset_set( + exec_ctx, pending_request->pollent, + grpc_polling_entity_pollset_set(&c->pollent)); + grpc_oauth2_pending_get_request_metadata *prev = pending_request; + pending_request = pending_request->next; + gpr_free(prev); + } + GRPC_MDELEM_UNREF(exec_ctx, access_token_md); + grpc_call_credentials_unref(exec_ctx, r->creds); + grpc_credentials_metadata_request_destroy(exec_ctx, r); +} + +static bool oauth2_token_fetcher_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_polling_entity *pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)creds; + // Check if we can use the cached token. + gpr_timespec refresh_threshold = gpr_time_from_seconds( + GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); + grpc_mdelem cached_access_token_md = GRPC_MDNULL; + gpr_mu_lock(&c->mu); + if (!GRPC_MDISNULL(c->access_token_md) && + (gpr_time_cmp( + gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { + cached_access_token_md = GRPC_MDELEM_REF(c->access_token_md); + } + if (!GRPC_MDISNULL(cached_access_token_md)) { + gpr_mu_unlock(&c->mu); + grpc_credentials_mdelem_array_add(md_array, cached_access_token_md); + GRPC_MDELEM_UNREF(exec_ctx, cached_access_token_md); + return true; + } + // Couldn't get the token from the cache. + // Add request to c->pending_requests and start a new fetch if needed. + grpc_oauth2_pending_get_request_metadata *pending_request = + (grpc_oauth2_pending_get_request_metadata *)gpr_malloc( + sizeof(*pending_request)); + pending_request->md_array = md_array; + pending_request->on_request_metadata = on_request_metadata; + pending_request->pollent = pollent; + grpc_polling_entity_add_to_pollset_set( + exec_ctx, pollent, grpc_polling_entity_pollset_set(&c->pollent)); + pending_request->next = c->pending_requests; + c->pending_requests = pending_request; + bool start_fetch = false; + if (!c->token_fetch_pending) { + c->token_fetch_pending = true; + start_fetch = true; + } + gpr_mu_unlock(&c->mu); + if (start_fetch) { + grpc_call_credentials_ref(creds); + c->fetch_func( + exec_ctx, grpc_credentials_metadata_request_create(creds), + &c->httpcli_context, &c->pollent, on_oauth2_token_fetcher_http_response, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), refresh_threshold)); + } + return false; +} + +static void oauth2_token_fetcher_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)creds; + gpr_mu_lock(&c->mu); + grpc_oauth2_pending_get_request_metadata *prev = NULL; + grpc_oauth2_pending_get_request_metadata *pending_request = + c->pending_requests; + while (pending_request != NULL) { + if (pending_request->md_array == md_array) { + // Remove matching pending request from the list. + if (prev != NULL) { + prev->next = pending_request->next; + } else { + c->pending_requests = pending_request->next; + } + // Invoke the callback immediately with an error. + GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, + GRPC_ERROR_REF(error)); + gpr_free(pending_request); + break; + } + prev = pending_request; + pending_request = pending_request->next; + } + gpr_mu_unlock(&c->mu); + GRPC_ERROR_UNREF(error); +} + +static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, + grpc_fetch_oauth2_func fetch_func) { + memset(c, 0, sizeof(grpc_oauth2_token_fetcher_credentials)); + c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; + gpr_ref_init(&c->base.refcount, 1); + gpr_mu_init(&c->mu); + c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); + c->fetch_func = fetch_func; + c->pollent = + grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()); + grpc_httpcli_context_init(&c->httpcli_context); +} + +// +// Google Compute Engine credentials. +// + +static grpc_call_credentials_vtable compute_engine_vtable = { + oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata, + oauth2_token_fetcher_cancel_get_request_metadata}; + +static void compute_engine_fetch_oauth2( + grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, + grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent, + grpc_iomgr_cb_func response_cb, gpr_timespec deadline) { + grpc_http_header header = {(char *)"Metadata-Flavor", (char *)"Google"}; + grpc_httpcli_request request; + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = (char *)GRPC_COMPUTE_ENGINE_METADATA_HOST; + request.http.path = (char *)GRPC_COMPUTE_ENGINE_METADATA_TOKEN_PATH; + request.http.hdr_count = 1; + request.http.hdrs = &header; + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + grpc_resource_quota *resource_quota = + grpc_resource_quota_create("oauth2_credentials"); + grpc_httpcli_get( + exec_ctx, httpcli_context, pollent, resource_quota, &request, deadline, + GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), + &metadata_req->response); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); +} + +grpc_call_credentials *grpc_google_compute_engine_credentials_create( + void *reserved) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)gpr_malloc( + sizeof(grpc_oauth2_token_fetcher_credentials)); + GRPC_API_TRACE("grpc_compute_engine_credentials_create(reserved=%p)", 1, + (reserved)); + GPR_ASSERT(reserved == NULL); + init_oauth2_token_fetcher(c, compute_engine_fetch_oauth2); + c->base.vtable = &compute_engine_vtable; + return &c->base; +} + +// +// Google Refresh Token credentials. +// + +static void refresh_token_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_google_refresh_token_credentials *c = + (grpc_google_refresh_token_credentials *)creds; + grpc_auth_refresh_token_destruct(&c->refresh_token); + oauth2_token_fetcher_destruct(exec_ctx, &c->base.base); +} + +static grpc_call_credentials_vtable refresh_token_vtable = { + refresh_token_destruct, oauth2_token_fetcher_get_request_metadata, + oauth2_token_fetcher_cancel_get_request_metadata}; + +static void refresh_token_fetch_oauth2( + grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, + grpc_httpcli_context *httpcli_context, grpc_polling_entity *pollent, + grpc_iomgr_cb_func response_cb, gpr_timespec deadline) { + grpc_google_refresh_token_credentials *c = + (grpc_google_refresh_token_credentials *)metadata_req->creds; + grpc_http_header header = {(char *)"Content-Type", + (char *)"application/x-www-form-urlencoded"}; + grpc_httpcli_request request; + char *body = NULL; + gpr_asprintf(&body, GRPC_REFRESH_TOKEN_POST_BODY_FORMAT_STRING, + c->refresh_token.client_id, c->refresh_token.client_secret, + c->refresh_token.refresh_token); + memset(&request, 0, sizeof(grpc_httpcli_request)); + request.host = (char *)GRPC_GOOGLE_OAUTH2_SERVICE_HOST; + request.http.path = (char *)GRPC_GOOGLE_OAUTH2_SERVICE_TOKEN_PATH; + request.http.hdr_count = 1; + request.http.hdrs = &header; + request.handshaker = &grpc_httpcli_ssl; + /* TODO(ctiller): Carry the resource_quota in ctx and share it with the host + channel. This would allow us to cancel an authentication query when under + extreme memory pressure. */ + grpc_resource_quota *resource_quota = + grpc_resource_quota_create("oauth2_credentials_refresh"); + grpc_httpcli_post( + exec_ctx, httpcli_context, pollent, resource_quota, &request, body, + strlen(body), deadline, + GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), + &metadata_req->response); + grpc_resource_quota_unref_internal(exec_ctx, resource_quota); + gpr_free(body); +} + +grpc_call_credentials * +grpc_refresh_token_credentials_create_from_auth_refresh_token( + grpc_auth_refresh_token refresh_token) { + grpc_google_refresh_token_credentials *c; + if (!grpc_auth_refresh_token_is_valid(&refresh_token)) { + gpr_log(GPR_ERROR, "Invalid input for refresh token credentials creation"); + return NULL; + } + c = (grpc_google_refresh_token_credentials *)gpr_zalloc( + sizeof(grpc_google_refresh_token_credentials)); + init_oauth2_token_fetcher(&c->base, refresh_token_fetch_oauth2); + c->base.base.vtable = &refresh_token_vtable; + c->refresh_token = refresh_token; + return &c->base.base; +} + +static char *create_loggable_refresh_token(grpc_auth_refresh_token *token) { + if (strcmp(token->type, GRPC_AUTH_JSON_TYPE_INVALID) == 0) { + return gpr_strdup(""); + } + char *loggable_token = NULL; + gpr_asprintf(&loggable_token, + "{\n type: %s\n client_id: %s\n client_secret: " + "\n refresh_token: \n}", + token->type, token->client_id); + return loggable_token; +} + +grpc_call_credentials *grpc_google_refresh_token_credentials_create( + const char *json_refresh_token, void *reserved) { + grpc_auth_refresh_token token = + grpc_auth_refresh_token_create_from_string(json_refresh_token); + if (GRPC_TRACER_ON(grpc_api_trace)) { + char *loggable_token = create_loggable_refresh_token(&token); + gpr_log(GPR_INFO, + "grpc_refresh_token_credentials_create(json_refresh_token=%s, " + "reserved=%p)", + loggable_token, reserved); + gpr_free(loggable_token); + } + GPR_ASSERT(reserved == NULL); + return grpc_refresh_token_credentials_create_from_auth_refresh_token(token); +} + +// +// Oauth2 Access Token credentials. +// + +static void access_token_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; + GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); +} + +static bool access_token_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_polling_entity *pollent, grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; + grpc_credentials_mdelem_array_add(md_array, c->access_token_md); + return true; +} + +static void access_token_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable access_token_vtable = { + access_token_destruct, access_token_get_request_metadata, + access_token_cancel_get_request_metadata}; + +grpc_call_credentials *grpc_access_token_credentials_create( + const char *access_token, void *reserved) { + grpc_access_token_credentials *c = + (grpc_access_token_credentials *)gpr_zalloc( + sizeof(grpc_access_token_credentials)); + GRPC_API_TRACE( + "grpc_access_token_credentials_create(access_token=, " + "reserved=%p)", + 1, (reserved)); + GPR_ASSERT(reserved == NULL); + c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; + c->base.vtable = &access_token_vtable; + gpr_ref_init(&c->base.refcount, 1); + char *token_md_value; + gpr_asprintf(&token_md_value, "Bearer %s", access_token); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + c->access_token_md = grpc_mdelem_from_slices( + &exec_ctx, grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(token_md_value)); + grpc_exec_ctx_finish(&exec_ctx); + gpr_free(token_md_value); + return &c->base; +} diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c deleted file mode 100644 index 8106a730fe..0000000000 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.c +++ /dev/null @@ -1,272 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/plugin/plugin_credentials.h" - -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/validate_metadata.h" - -grpc_tracer_flag grpc_plugin_credentials_trace = - GRPC_TRACER_INITIALIZER(false, "plugin_credentials"); - -static void plugin_destruct(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds) { - grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; - gpr_mu_destroy(&c->mu); - if (c->plugin.state != NULL && c->plugin.destroy != NULL) { - c->plugin.destroy(c->plugin.state); - } -} - -static void pending_request_remove_locked( - grpc_plugin_credentials *c, - grpc_plugin_credentials_pending_request *pending_request) { - if (pending_request->prev == NULL) { - c->pending_requests = pending_request->next; - } else { - pending_request->prev->next = pending_request->next; - } - if (pending_request->next != NULL) { - pending_request->next->prev = pending_request->prev; - } -} - -// Checks if the request has been cancelled. -// If not, removes it from the pending list, so that it cannot be -// cancelled out from under us. -// When this returns, r->cancelled indicates whether the request was -// cancelled before completion. -static void pending_request_complete( - grpc_exec_ctx *exec_ctx, grpc_plugin_credentials_pending_request *r) { - gpr_mu_lock(&r->creds->mu); - if (!r->cancelled) pending_request_remove_locked(r->creds, r); - gpr_mu_unlock(&r->creds->mu); - // Ref to credentials not needed anymore. - grpc_call_credentials_unref(exec_ctx, &r->creds->base); -} - -static grpc_error *process_plugin_result( - grpc_exec_ctx *exec_ctx, grpc_plugin_credentials_pending_request *r, - const grpc_metadata *md, size_t num_md, grpc_status_code status, - const char *error_details) { - grpc_error *error = GRPC_ERROR_NONE; - if (status != GRPC_STATUS_OK) { - char *msg; - gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s", - error_details); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - } else { - bool seen_illegal_header = false; - for (size_t i = 0; i < num_md; ++i) { - if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin", - grpc_validate_header_key_is_legal(md[i].key))) { - seen_illegal_header = true; - break; - } else if (!grpc_is_binary_header(md[i].key) && - !GRPC_LOG_IF_ERROR( - "validate_metadata_from_plugin", - grpc_validate_header_nonbin_value_is_legal(md[i].value))) { - gpr_log(GPR_ERROR, "Plugin added invalid metadata value."); - seen_illegal_header = true; - break; - } - } - if (seen_illegal_header) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata"); - } else { - for (size_t i = 0; i < num_md; ++i) { - grpc_mdelem mdelem = grpc_mdelem_from_slices( - exec_ctx, grpc_slice_ref_internal(md[i].key), - grpc_slice_ref_internal(md[i].value)); - grpc_credentials_mdelem_array_add(r->md_array, mdelem); - GRPC_MDELEM_UNREF(exec_ctx, mdelem); - } - } - } - return error; -} - -static void plugin_md_request_metadata_ready(void *request, - const grpc_metadata *md, - size_t num_md, - grpc_status_code status, - const char *error_details) { - /* called from application code */ - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER( - GRPC_EXEC_CTX_FLAG_IS_FINISHED | GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP, - NULL, NULL); - grpc_plugin_credentials_pending_request *r = - (grpc_plugin_credentials_pending_request *)request; - if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, - "plugin_credentials[%p]: request %p: plugin returned " - "asynchronously", - r->creds, r); - } - // Remove request from pending list if not previously cancelled. - pending_request_complete(&exec_ctx, r); - // If it has not been cancelled, process it. - if (!r->cancelled) { - grpc_error *error = - process_plugin_result(&exec_ctx, r, md, num_md, status, error_details); - GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, error); - } else if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, - "plugin_credentials[%p]: request %p: plugin was previously " - "cancelled", - r->creds, r); - } - gpr_free(r); - grpc_exec_ctx_finish(&exec_ctx); -} - -static bool plugin_get_request_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_credentials *creds, - grpc_polling_entity *pollent, - grpc_auth_metadata_context context, - grpc_credentials_mdelem_array *md_array, - grpc_closure *on_request_metadata, - grpc_error **error) { - grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; - bool retval = true; // Synchronous return. - if (c->plugin.get_metadata != NULL) { - // Create pending_request object. - grpc_plugin_credentials_pending_request *pending_request = - (grpc_plugin_credentials_pending_request *)gpr_zalloc( - sizeof(*pending_request)); - pending_request->creds = c; - pending_request->md_array = md_array; - pending_request->on_request_metadata = on_request_metadata; - // Add it to the pending list. - gpr_mu_lock(&c->mu); - if (c->pending_requests != NULL) { - c->pending_requests->prev = pending_request; - } - pending_request->next = c->pending_requests; - c->pending_requests = pending_request; - gpr_mu_unlock(&c->mu); - // Invoke the plugin. The callback holds a ref to us. - if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin", - c, pending_request); - } - grpc_call_credentials_ref(creds); - grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX]; - size_t num_creds_md = 0; - grpc_status_code status = GRPC_STATUS_OK; - const char *error_details = NULL; - if (!c->plugin.get_metadata(c->plugin.state, context, - plugin_md_request_metadata_ready, - pending_request, creds_md, &num_creds_md, - &status, &error_details)) { - if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, - "plugin_credentials[%p]: request %p: plugin will return " - "asynchronously", - c, pending_request); - } - return false; // Asynchronous return. - } - // Returned synchronously. - // Remove request from pending list if not previously cancelled. - pending_request_complete(exec_ctx, pending_request); - // If the request was cancelled, the error will have been returned - // asynchronously by plugin_cancel_get_request_metadata(), so return - // false. Otherwise, process the result. - if (pending_request->cancelled) { - if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, - "plugin_credentials[%p]: request %p was cancelled, error " - "will be returned asynchronously", - c, pending_request); - } - retval = false; - } else { - if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, - "plugin_credentials[%p]: request %p: plugin returned " - "synchronously", - c, pending_request); - } - *error = process_plugin_result(exec_ctx, pending_request, creds_md, - num_creds_md, status, error_details); - } - // Clean up. - for (size_t i = 0; i < num_creds_md; ++i) { - grpc_slice_unref_internal(exec_ctx, creds_md[i].key); - grpc_slice_unref_internal(exec_ctx, creds_md[i].value); - } - gpr_free((void *)error_details); - gpr_free(pending_request); - } - return retval; -} - -static void plugin_cancel_get_request_metadata( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, - grpc_credentials_mdelem_array *md_array, grpc_error *error) { - grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; - gpr_mu_lock(&c->mu); - for (grpc_plugin_credentials_pending_request *pending_request = - c->pending_requests; - pending_request != NULL; pending_request = pending_request->next) { - if (pending_request->md_array == md_array) { - if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { - gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", c, - pending_request); - } - pending_request->cancelled = true; - GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, - GRPC_ERROR_REF(error)); - pending_request_remove_locked(c, pending_request); - break; - } - } - gpr_mu_unlock(&c->mu); - GRPC_ERROR_UNREF(error); -} - -static grpc_call_credentials_vtable plugin_vtable = { - plugin_destruct, plugin_get_request_metadata, - plugin_cancel_get_request_metadata}; - -grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( - grpc_metadata_credentials_plugin plugin, void *reserved) { - grpc_plugin_credentials *c = - (grpc_plugin_credentials *)gpr_zalloc(sizeof(*c)); - GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1, - (reserved)); - GPR_ASSERT(reserved == NULL); - c->base.type = plugin.type; - c->base.vtable = &plugin_vtable; - gpr_ref_init(&c->base.refcount, 1); - c->plugin = plugin; - gpr_mu_init(&c->mu); - return &c->base; -} diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.cc b/src/core/lib/security/credentials/plugin/plugin_credentials.cc new file mode 100644 index 0000000000..8106a730fe --- /dev/null +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.cc @@ -0,0 +1,272 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/plugin/plugin_credentials.h" + +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/validate_metadata.h" + +grpc_tracer_flag grpc_plugin_credentials_trace = + GRPC_TRACER_INITIALIZER(false, "plugin_credentials"); + +static void plugin_destruct(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds) { + grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + gpr_mu_destroy(&c->mu); + if (c->plugin.state != NULL && c->plugin.destroy != NULL) { + c->plugin.destroy(c->plugin.state); + } +} + +static void pending_request_remove_locked( + grpc_plugin_credentials *c, + grpc_plugin_credentials_pending_request *pending_request) { + if (pending_request->prev == NULL) { + c->pending_requests = pending_request->next; + } else { + pending_request->prev->next = pending_request->next; + } + if (pending_request->next != NULL) { + pending_request->next->prev = pending_request->prev; + } +} + +// Checks if the request has been cancelled. +// If not, removes it from the pending list, so that it cannot be +// cancelled out from under us. +// When this returns, r->cancelled indicates whether the request was +// cancelled before completion. +static void pending_request_complete( + grpc_exec_ctx *exec_ctx, grpc_plugin_credentials_pending_request *r) { + gpr_mu_lock(&r->creds->mu); + if (!r->cancelled) pending_request_remove_locked(r->creds, r); + gpr_mu_unlock(&r->creds->mu); + // Ref to credentials not needed anymore. + grpc_call_credentials_unref(exec_ctx, &r->creds->base); +} + +static grpc_error *process_plugin_result( + grpc_exec_ctx *exec_ctx, grpc_plugin_credentials_pending_request *r, + const grpc_metadata *md, size_t num_md, grpc_status_code status, + const char *error_details) { + grpc_error *error = GRPC_ERROR_NONE; + if (status != GRPC_STATUS_OK) { + char *msg; + gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s", + error_details); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + } else { + bool seen_illegal_header = false; + for (size_t i = 0; i < num_md; ++i) { + if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin", + grpc_validate_header_key_is_legal(md[i].key))) { + seen_illegal_header = true; + break; + } else if (!grpc_is_binary_header(md[i].key) && + !GRPC_LOG_IF_ERROR( + "validate_metadata_from_plugin", + grpc_validate_header_nonbin_value_is_legal(md[i].value))) { + gpr_log(GPR_ERROR, "Plugin added invalid metadata value."); + seen_illegal_header = true; + break; + } + } + if (seen_illegal_header) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata"); + } else { + for (size_t i = 0; i < num_md; ++i) { + grpc_mdelem mdelem = grpc_mdelem_from_slices( + exec_ctx, grpc_slice_ref_internal(md[i].key), + grpc_slice_ref_internal(md[i].value)); + grpc_credentials_mdelem_array_add(r->md_array, mdelem); + GRPC_MDELEM_UNREF(exec_ctx, mdelem); + } + } + } + return error; +} + +static void plugin_md_request_metadata_ready(void *request, + const grpc_metadata *md, + size_t num_md, + grpc_status_code status, + const char *error_details) { + /* called from application code */ + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER( + GRPC_EXEC_CTX_FLAG_IS_FINISHED | GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP, + NULL, NULL); + grpc_plugin_credentials_pending_request *r = + (grpc_plugin_credentials_pending_request *)request; + if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin returned " + "asynchronously", + r->creds, r); + } + // Remove request from pending list if not previously cancelled. + pending_request_complete(&exec_ctx, r); + // If it has not been cancelled, process it. + if (!r->cancelled) { + grpc_error *error = + process_plugin_result(&exec_ctx, r, md, num_md, status, error_details); + GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, error); + } else if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin was previously " + "cancelled", + r->creds, r); + } + gpr_free(r); + grpc_exec_ctx_finish(&exec_ctx); +} + +static bool plugin_get_request_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *creds, + grpc_polling_entity *pollent, + grpc_auth_metadata_context context, + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error) { + grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + bool retval = true; // Synchronous return. + if (c->plugin.get_metadata != NULL) { + // Create pending_request object. + grpc_plugin_credentials_pending_request *pending_request = + (grpc_plugin_credentials_pending_request *)gpr_zalloc( + sizeof(*pending_request)); + pending_request->creds = c; + pending_request->md_array = md_array; + pending_request->on_request_metadata = on_request_metadata; + // Add it to the pending list. + gpr_mu_lock(&c->mu); + if (c->pending_requests != NULL) { + c->pending_requests->prev = pending_request; + } + pending_request->next = c->pending_requests; + c->pending_requests = pending_request; + gpr_mu_unlock(&c->mu); + // Invoke the plugin. The callback holds a ref to us. + if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin", + c, pending_request); + } + grpc_call_credentials_ref(creds); + grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX]; + size_t num_creds_md = 0; + grpc_status_code status = GRPC_STATUS_OK; + const char *error_details = NULL; + if (!c->plugin.get_metadata(c->plugin.state, context, + plugin_md_request_metadata_ready, + pending_request, creds_md, &num_creds_md, + &status, &error_details)) { + if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin will return " + "asynchronously", + c, pending_request); + } + return false; // Asynchronous return. + } + // Returned synchronously. + // Remove request from pending list if not previously cancelled. + pending_request_complete(exec_ctx, pending_request); + // If the request was cancelled, the error will have been returned + // asynchronously by plugin_cancel_get_request_metadata(), so return + // false. Otherwise, process the result. + if (pending_request->cancelled) { + if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p was cancelled, error " + "will be returned asynchronously", + c, pending_request); + } + retval = false; + } else { + if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, + "plugin_credentials[%p]: request %p: plugin returned " + "synchronously", + c, pending_request); + } + *error = process_plugin_result(exec_ctx, pending_request, creds_md, + num_creds_md, status, error_details); + } + // Clean up. + for (size_t i = 0; i < num_creds_md; ++i) { + grpc_slice_unref_internal(exec_ctx, creds_md[i].key); + grpc_slice_unref_internal(exec_ctx, creds_md[i].value); + } + gpr_free((void *)error_details); + gpr_free(pending_request); + } + return retval; +} + +static void plugin_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + gpr_mu_lock(&c->mu); + for (grpc_plugin_credentials_pending_request *pending_request = + c->pending_requests; + pending_request != NULL; pending_request = pending_request->next) { + if (pending_request->md_array == md_array) { + if (GRPC_TRACER_ON(grpc_plugin_credentials_trace)) { + gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", c, + pending_request); + } + pending_request->cancelled = true; + GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, + GRPC_ERROR_REF(error)); + pending_request_remove_locked(c, pending_request); + break; + } + } + gpr_mu_unlock(&c->mu); + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable plugin_vtable = { + plugin_destruct, plugin_get_request_metadata, + plugin_cancel_get_request_metadata}; + +grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( + grpc_metadata_credentials_plugin plugin, void *reserved) { + grpc_plugin_credentials *c = + (grpc_plugin_credentials *)gpr_zalloc(sizeof(*c)); + GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1, + (reserved)); + GPR_ASSERT(reserved == NULL); + c->base.type = plugin.type; + c->base.vtable = &plugin_vtable; + gpr_ref_init(&c->base.refcount, 1); + c->plugin = plugin; + gpr_mu_init(&c->mu); + return &c->base; +} diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.c b/src/core/lib/security/credentials/ssl/ssl_credentials.c deleted file mode 100644 index 9df69a2a3d..0000000000 --- a/src/core/lib/security/credentials/ssl/ssl_credentials.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/surface/api_trace.h" - -#include -#include -#include - -// -// SSL Channel Credentials. -// - -static void ssl_config_pem_key_cert_pair_destroy( - tsi_ssl_pem_key_cert_pair *kp) { - if (kp == NULL) return; - gpr_free((void *)kp->private_key); - gpr_free((void *)kp->cert_chain); -} - -static void ssl_destruct(grpc_exec_ctx *exec_ctx, - grpc_channel_credentials *creds) { - grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds; - gpr_free(c->config.pem_root_certs); - ssl_config_pem_key_cert_pair_destroy(&c->config.pem_key_cert_pair); -} - -static grpc_security_status ssl_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds, - grpc_call_credentials *call_creds, const char *target, - const grpc_channel_args *args, grpc_channel_security_connector **sc, - grpc_channel_args **new_args) { - grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds; - grpc_security_status status = GRPC_SECURITY_OK; - const char *overridden_target_name = NULL; - for (size_t i = 0; args && i < args->num_args; i++) { - grpc_arg *arg = &args->args[i]; - if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 && - arg->type == GRPC_ARG_STRING) { - overridden_target_name = arg->value.string; - break; - } - } - status = grpc_ssl_channel_security_connector_create( - exec_ctx, call_creds, &c->config, target, overridden_target_name, sc); - if (status != GRPC_SECURITY_OK) { - return status; - } - grpc_arg new_arg = grpc_channel_arg_string_create( - (char *)GRPC_ARG_HTTP2_SCHEME, (char *)"https"); - *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1); - return status; -} - -static grpc_channel_credentials_vtable ssl_vtable = { - ssl_destruct, ssl_create_security_connector, NULL}; - -static void ssl_build_config(const char *pem_root_certs, - grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, - grpc_ssl_config *config) { - if (pem_root_certs != NULL) { - config->pem_root_certs = gpr_strdup(pem_root_certs); - } - if (pem_key_cert_pair != NULL) { - GPR_ASSERT(pem_key_cert_pair->private_key != NULL); - GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL); - config->pem_key_cert_pair.cert_chain = - gpr_strdup(pem_key_cert_pair->cert_chain); - config->pem_key_cert_pair.private_key = - gpr_strdup(pem_key_cert_pair->private_key); - } -} - -grpc_channel_credentials *grpc_ssl_credentials_create( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, - void *reserved) { - grpc_ssl_credentials *c = - (grpc_ssl_credentials *)gpr_zalloc(sizeof(grpc_ssl_credentials)); - GRPC_API_TRACE( - "grpc_ssl_credentials_create(pem_root_certs=%s, " - "pem_key_cert_pair=%p, " - "reserved=%p)", - 3, (pem_root_certs, pem_key_cert_pair, reserved)); - GPR_ASSERT(reserved == NULL); - c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL; - c->base.vtable = &ssl_vtable; - gpr_ref_init(&c->base.refcount, 1); - ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config); - return &c->base; -} - -// -// SSL Server Credentials. -// - -static void ssl_server_destruct(grpc_exec_ctx *exec_ctx, - grpc_server_credentials *creds) { - grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds; - size_t i; - for (i = 0; i < c->config.num_key_cert_pairs; i++) { - ssl_config_pem_key_cert_pair_destroy(&c->config.pem_key_cert_pairs[i]); - } - gpr_free(c->config.pem_key_cert_pairs); - gpr_free(c->config.pem_root_certs); -} - -static grpc_security_status ssl_server_create_security_connector( - grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds, - grpc_server_security_connector **sc) { - grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds; - return grpc_ssl_server_security_connector_create(exec_ctx, &c->config, sc); -} - -static grpc_server_credentials_vtable ssl_server_vtable = { - ssl_server_destruct, ssl_server_create_security_connector}; - -static void ssl_build_server_config( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, - grpc_ssl_client_certificate_request_type client_certificate_request, - grpc_ssl_server_config *config) { - size_t i; - config->client_certificate_request = client_certificate_request; - if (pem_root_certs != NULL) { - config->pem_root_certs = gpr_strdup(pem_root_certs); - } - if (num_key_cert_pairs > 0) { - GPR_ASSERT(pem_key_cert_pairs != NULL); - config->pem_key_cert_pairs = (tsi_ssl_pem_key_cert_pair *)gpr_zalloc( - num_key_cert_pairs * sizeof(tsi_ssl_pem_key_cert_pair)); - } - config->num_key_cert_pairs = num_key_cert_pairs; - for (i = 0; i < num_key_cert_pairs; i++) { - GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL); - GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL); - config->pem_key_cert_pairs[i].cert_chain = - gpr_strdup(pem_key_cert_pairs[i].cert_chain); - config->pem_key_cert_pairs[i].private_key = - gpr_strdup(pem_key_cert_pairs[i].private_key); - } -} - -grpc_server_credentials *grpc_ssl_server_credentials_create( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, int force_client_auth, void *reserved) { - return grpc_ssl_server_credentials_create_ex( - pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs, - force_client_auth - ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY - : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, - reserved); -} - -grpc_server_credentials *grpc_ssl_server_credentials_create_ex( - const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, - grpc_ssl_client_certificate_request_type client_certificate_request, - void *reserved) { - grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)gpr_zalloc( - sizeof(grpc_ssl_server_credentials)); - GRPC_API_TRACE( - "grpc_ssl_server_credentials_create_ex(" - "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, " - "client_certificate_request=%d, reserved=%p)", - 5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs, - client_certificate_request, reserved)); - GPR_ASSERT(reserved == NULL); - c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL; - gpr_ref_init(&c->base.refcount, 1); - c->base.vtable = &ssl_server_vtable; - ssl_build_server_config(pem_root_certs, pem_key_cert_pairs, - num_key_cert_pairs, client_certificate_request, - &c->config); - return &c->base; -} diff --git a/src/core/lib/security/credentials/ssl/ssl_credentials.cc b/src/core/lib/security/credentials/ssl/ssl_credentials.cc new file mode 100644 index 0000000000..9df69a2a3d --- /dev/null +++ b/src/core/lib/security/credentials/ssl/ssl_credentials.cc @@ -0,0 +1,195 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/credentials/ssl/ssl_credentials.h" + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/surface/api_trace.h" + +#include +#include +#include + +// +// SSL Channel Credentials. +// + +static void ssl_config_pem_key_cert_pair_destroy( + tsi_ssl_pem_key_cert_pair *kp) { + if (kp == NULL) return; + gpr_free((void *)kp->private_key); + gpr_free((void *)kp->cert_chain); +} + +static void ssl_destruct(grpc_exec_ctx *exec_ctx, + grpc_channel_credentials *creds) { + grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds; + gpr_free(c->config.pem_root_certs); + ssl_config_pem_key_cert_pair_destroy(&c->config.pem_key_cert_pair); +} + +static grpc_security_status ssl_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_channel_credentials *creds, + grpc_call_credentials *call_creds, const char *target, + const grpc_channel_args *args, grpc_channel_security_connector **sc, + grpc_channel_args **new_args) { + grpc_ssl_credentials *c = (grpc_ssl_credentials *)creds; + grpc_security_status status = GRPC_SECURITY_OK; + const char *overridden_target_name = NULL; + for (size_t i = 0; args && i < args->num_args; i++) { + grpc_arg *arg = &args->args[i]; + if (strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) == 0 && + arg->type == GRPC_ARG_STRING) { + overridden_target_name = arg->value.string; + break; + } + } + status = grpc_ssl_channel_security_connector_create( + exec_ctx, call_creds, &c->config, target, overridden_target_name, sc); + if (status != GRPC_SECURITY_OK) { + return status; + } + grpc_arg new_arg = grpc_channel_arg_string_create( + (char *)GRPC_ARG_HTTP2_SCHEME, (char *)"https"); + *new_args = grpc_channel_args_copy_and_add(args, &new_arg, 1); + return status; +} + +static grpc_channel_credentials_vtable ssl_vtable = { + ssl_destruct, ssl_create_security_connector, NULL}; + +static void ssl_build_config(const char *pem_root_certs, + grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, + grpc_ssl_config *config) { + if (pem_root_certs != NULL) { + config->pem_root_certs = gpr_strdup(pem_root_certs); + } + if (pem_key_cert_pair != NULL) { + GPR_ASSERT(pem_key_cert_pair->private_key != NULL); + GPR_ASSERT(pem_key_cert_pair->cert_chain != NULL); + config->pem_key_cert_pair.cert_chain = + gpr_strdup(pem_key_cert_pair->cert_chain); + config->pem_key_cert_pair.private_key = + gpr_strdup(pem_key_cert_pair->private_key); + } +} + +grpc_channel_credentials *grpc_ssl_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pair, + void *reserved) { + grpc_ssl_credentials *c = + (grpc_ssl_credentials *)gpr_zalloc(sizeof(grpc_ssl_credentials)); + GRPC_API_TRACE( + "grpc_ssl_credentials_create(pem_root_certs=%s, " + "pem_key_cert_pair=%p, " + "reserved=%p)", + 3, (pem_root_certs, pem_key_cert_pair, reserved)); + GPR_ASSERT(reserved == NULL); + c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL; + c->base.vtable = &ssl_vtable; + gpr_ref_init(&c->base.refcount, 1); + ssl_build_config(pem_root_certs, pem_key_cert_pair, &c->config); + return &c->base; +} + +// +// SSL Server Credentials. +// + +static void ssl_server_destruct(grpc_exec_ctx *exec_ctx, + grpc_server_credentials *creds) { + grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds; + size_t i; + for (i = 0; i < c->config.num_key_cert_pairs; i++) { + ssl_config_pem_key_cert_pair_destroy(&c->config.pem_key_cert_pairs[i]); + } + gpr_free(c->config.pem_key_cert_pairs); + gpr_free(c->config.pem_root_certs); +} + +static grpc_security_status ssl_server_create_security_connector( + grpc_exec_ctx *exec_ctx, grpc_server_credentials *creds, + grpc_server_security_connector **sc) { + grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)creds; + return grpc_ssl_server_security_connector_create(exec_ctx, &c->config, sc); +} + +static grpc_server_credentials_vtable ssl_server_vtable = { + ssl_server_destruct, ssl_server_create_security_connector}; + +static void ssl_build_server_config( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, + grpc_ssl_client_certificate_request_type client_certificate_request, + grpc_ssl_server_config *config) { + size_t i; + config->client_certificate_request = client_certificate_request; + if (pem_root_certs != NULL) { + config->pem_root_certs = gpr_strdup(pem_root_certs); + } + if (num_key_cert_pairs > 0) { + GPR_ASSERT(pem_key_cert_pairs != NULL); + config->pem_key_cert_pairs = (tsi_ssl_pem_key_cert_pair *)gpr_zalloc( + num_key_cert_pairs * sizeof(tsi_ssl_pem_key_cert_pair)); + } + config->num_key_cert_pairs = num_key_cert_pairs; + for (i = 0; i < num_key_cert_pairs; i++) { + GPR_ASSERT(pem_key_cert_pairs[i].private_key != NULL); + GPR_ASSERT(pem_key_cert_pairs[i].cert_chain != NULL); + config->pem_key_cert_pairs[i].cert_chain = + gpr_strdup(pem_key_cert_pairs[i].cert_chain); + config->pem_key_cert_pairs[i].private_key = + gpr_strdup(pem_key_cert_pairs[i].private_key); + } +} + +grpc_server_credentials *grpc_ssl_server_credentials_create( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, int force_client_auth, void *reserved) { + return grpc_ssl_server_credentials_create_ex( + pem_root_certs, pem_key_cert_pairs, num_key_cert_pairs, + force_client_auth + ? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + : GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, + reserved); +} + +grpc_server_credentials *grpc_ssl_server_credentials_create_ex( + const char *pem_root_certs, grpc_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, + grpc_ssl_client_certificate_request_type client_certificate_request, + void *reserved) { + grpc_ssl_server_credentials *c = (grpc_ssl_server_credentials *)gpr_zalloc( + sizeof(grpc_ssl_server_credentials)); + GRPC_API_TRACE( + "grpc_ssl_server_credentials_create_ex(" + "pem_root_certs=%s, pem_key_cert_pairs=%p, num_key_cert_pairs=%lu, " + "client_certificate_request=%d, reserved=%p)", + 5, (pem_root_certs, pem_key_cert_pairs, (unsigned long)num_key_cert_pairs, + client_certificate_request, reserved)); + GPR_ASSERT(reserved == NULL); + c->base.type = GRPC_CHANNEL_CREDENTIALS_TYPE_SSL; + gpr_ref_init(&c->base.refcount, 1); + c->base.vtable = &ssl_server_vtable; + ssl_build_server_config(pem_root_certs, pem_key_cert_pairs, + num_key_cert_pairs, client_certificate_request, + &c->config); + return &c->base; +} diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c deleted file mode 100644 index a8464dbf9e..0000000000 --- a/src/core/lib/security/transport/client_auth_filter.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/transport/auth_filters.h" - -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/security/context/security_context.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/transport/security_connector.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/transport/static_metadata.h" - -#define MAX_CREDENTIALS_METADATA_COUNT 4 - -/* We can have a per-call credentials. */ -typedef struct { - grpc_call_stack *owning_call; - grpc_call_combiner *call_combiner; - grpc_call_credentials *creds; - bool have_host; - bool have_method; - grpc_slice host; - grpc_slice method; - /* pollset{_set} bound to this call; if we need to make external - network requests, they should be done under a pollset added to this - pollset_set so that work can progress when this call wants work to progress - */ - grpc_polling_entity *pollent; - grpc_credentials_mdelem_array md_array; - grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; - grpc_auth_metadata_context auth_md_context; - grpc_closure async_result_closure; - grpc_closure check_call_host_cancel_closure; - grpc_closure get_request_metadata_cancel_closure; -} call_data; - -/* We can have a per-channel credentials. */ -typedef struct { - grpc_channel_security_connector *security_connector; - grpc_auth_context *auth_context; -} channel_data; - -static void reset_auth_metadata_context( - grpc_auth_metadata_context *auth_md_context) { - if (auth_md_context->service_url != NULL) { - gpr_free((char *)auth_md_context->service_url); - auth_md_context->service_url = NULL; - } - if (auth_md_context->method_name != NULL) { - gpr_free((char *)auth_md_context->method_name); - auth_md_context->method_name = NULL; - } - GRPC_AUTH_CONTEXT_UNREF( - (grpc_auth_context *)auth_md_context->channel_auth_context, - "grpc_auth_metadata_context"); - auth_md_context->channel_auth_context = NULL; -} - -static void add_error(grpc_error **combined, grpc_error *error) { - if (error == GRPC_ERROR_NONE) return; - if (*combined == GRPC_ERROR_NONE) { - *combined = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Client auth metadata plugin error"); - } - *combined = grpc_error_add_child(*combined, error); -} - -static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *input_error) { - grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; - grpc_call_element *elem = - (grpc_call_element *)batch->handler_private.extra_arg; - call_data *calld = (call_data *)elem->call_data; - reset_auth_metadata_context(&calld->auth_md_context); - grpc_error *error = GRPC_ERROR_REF(input_error); - if (error == GRPC_ERROR_NONE) { - GPR_ASSERT(calld->md_array.size <= MAX_CREDENTIALS_METADATA_COUNT); - GPR_ASSERT(batch->send_initial_metadata); - grpc_metadata_batch *mdb = - batch->payload->send_initial_metadata.send_initial_metadata; - for (size_t i = 0; i < calld->md_array.size; ++i) { - add_error(&error, grpc_metadata_batch_add_tail( - exec_ctx, mdb, &calld->md_links[i], - GRPC_MDELEM_REF(calld->md_array.md[i]))); - } - } - if (error == GRPC_ERROR_NONE) { - grpc_call_next_op(exec_ctx, elem, batch); - } else { - error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNAUTHENTICATED); - grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, batch, error, - calld->call_combiner); - } -} - -void build_auth_metadata_context(grpc_security_connector *sc, - grpc_auth_context *auth_context, - call_data *calld) { - char *service = grpc_slice_to_c_string(calld->method); - char *last_slash = strrchr(service, '/'); - char *method_name = NULL; - char *service_url = NULL; - reset_auth_metadata_context(&calld->auth_md_context); - if (last_slash == NULL) { - gpr_log(GPR_ERROR, "No '/' found in fully qualified method name"); - service[0] = '\0'; - } else if (last_slash == service) { - /* No service part in fully qualified method name: will just be "/". */ - service[1] = '\0'; - } else { - *last_slash = '\0'; - method_name = gpr_strdup(last_slash + 1); - } - if (method_name == NULL) method_name = gpr_strdup(""); - char *host = grpc_slice_to_c_string(calld->host); - gpr_asprintf(&service_url, "%s://%s%s", - sc->url_scheme == NULL ? "" : sc->url_scheme, host, service); - calld->auth_md_context.service_url = service_url; - calld->auth_md_context.method_name = method_name; - calld->auth_md_context.channel_auth_context = - GRPC_AUTH_CONTEXT_REF(auth_context, "grpc_auth_metadata_context"); - gpr_free(service); - gpr_free(host); -} - -static void cancel_get_request_metadata(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - if (error != GRPC_ERROR_NONE) { - grpc_call_credentials_cancel_get_request_metadata( - exec_ctx, calld->creds, &calld->md_array, GRPC_ERROR_REF(error)); - } - GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, - "cancel_get_request_metadata"); -} - -static void send_security_metadata(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - grpc_client_security_context *ctx = - (grpc_client_security_context *)batch->payload - ->context[GRPC_CONTEXT_SECURITY] - .value; - grpc_call_credentials *channel_call_creds = - chand->security_connector->request_metadata_creds; - int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL); - - if (channel_call_creds == NULL && !call_creds_has_md) { - /* Skip sending metadata altogether. */ - grpc_call_next_op(exec_ctx, elem, batch); - return; - } - - if (channel_call_creds != NULL && call_creds_has_md) { - calld->creds = grpc_composite_call_credentials_create(channel_call_creds, - ctx->creds, NULL); - if (calld->creds == NULL) { - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, batch, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Incompatible credentials set on channel and call."), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED), - calld->call_combiner); - return; - } - } else { - calld->creds = grpc_call_credentials_ref( - call_creds_has_md ? ctx->creds : channel_call_creds); - } - - build_auth_metadata_context(&chand->security_connector->base, - chand->auth_context, calld); - - GPR_ASSERT(calld->pollent != NULL); - - GRPC_CLOSURE_INIT(&calld->async_result_closure, on_credentials_metadata, - batch, grpc_schedule_on_exec_ctx); - grpc_error *error = GRPC_ERROR_NONE; - if (grpc_call_credentials_get_request_metadata( - exec_ctx, calld->creds, calld->pollent, calld->auth_md_context, - &calld->md_array, &calld->async_result_closure, &error)) { - // Synchronous return; invoke on_credentials_metadata() directly. - on_credentials_metadata(exec_ctx, batch, error); - GRPC_ERROR_UNREF(error); - } else { - // Async return; register cancellation closure with call combiner. - GRPC_CALL_STACK_REF(calld->owning_call, "cancel_get_request_metadata"); - grpc_call_combiner_set_notify_on_cancel( - exec_ctx, calld->call_combiner, - GRPC_CLOSURE_INIT(&calld->get_request_metadata_cancel_closure, - cancel_get_request_metadata, elem, - grpc_schedule_on_exec_ctx)); - } -} - -static void on_host_checked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; - grpc_call_element *elem = - (grpc_call_element *)batch->handler_private.extra_arg; - call_data *calld = (call_data *)elem->call_data; - if (error == GRPC_ERROR_NONE) { - send_security_metadata(exec_ctx, elem, batch); - } else { - char *error_msg; - char *host = grpc_slice_to_c_string(calld->host); - gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", - host); - gpr_free(host); - grpc_transport_stream_op_batch_finish_with_failure( - exec_ctx, batch, - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg), - GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNAUTHENTICATED), - calld->call_combiner); - gpr_free(error_msg); - } -} - -static void cancel_check_call_host(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - if (error != GRPC_ERROR_NONE) { - grpc_channel_security_connector_cancel_check_call_host( - exec_ctx, chand->security_connector, &calld->async_result_closure, - GRPC_ERROR_REF(error)); - } - GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "cancel_check_call_host"); -} - -static void auth_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - GPR_TIMER_BEGIN("auth_start_transport_stream_op_batch", 0); - - /* grab pointers to our data from the call element */ - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - - if (!batch->cancel_stream) { - GPR_ASSERT(batch->payload->context != NULL); - if (batch->payload->context[GRPC_CONTEXT_SECURITY].value == NULL) { - batch->payload->context[GRPC_CONTEXT_SECURITY].value = - grpc_client_security_context_create(); - batch->payload->context[GRPC_CONTEXT_SECURITY].destroy = - grpc_client_security_context_destroy; - } - grpc_client_security_context *sec_ctx = - (grpc_client_security_context *)batch->payload - ->context[GRPC_CONTEXT_SECURITY] - .value; - GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter"); - sec_ctx->auth_context = - GRPC_AUTH_CONTEXT_REF(chand->auth_context, "client_auth_filter"); - } - - if (batch->send_initial_metadata) { - for (grpc_linked_mdelem *l = batch->payload->send_initial_metadata - .send_initial_metadata->list.head; - l != NULL; l = l->next) { - grpc_mdelem md = l->md; - /* Pointer comparison is OK for md_elems created from the same context. - */ - if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) { - if (calld->have_host) { - grpc_slice_unref_internal(exec_ctx, calld->host); - } - calld->host = grpc_slice_ref_internal(GRPC_MDVALUE(md)); - calld->have_host = true; - } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) { - if (calld->have_method) { - grpc_slice_unref_internal(exec_ctx, calld->method); - } - calld->method = grpc_slice_ref_internal(GRPC_MDVALUE(md)); - calld->have_method = true; - } - } - if (calld->have_host) { - batch->handler_private.extra_arg = elem; - GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch, - grpc_schedule_on_exec_ctx); - char *call_host = grpc_slice_to_c_string(calld->host); - grpc_error *error = GRPC_ERROR_NONE; - if (grpc_channel_security_connector_check_call_host( - exec_ctx, chand->security_connector, call_host, - chand->auth_context, &calld->async_result_closure, &error)) { - // Synchronous return; invoke on_host_checked() directly. - on_host_checked(exec_ctx, batch, error); - GRPC_ERROR_UNREF(error); - } else { - // Async return; register cancellation closure with call combiner. - GRPC_CALL_STACK_REF(calld->owning_call, "cancel_check_call_host"); - grpc_call_combiner_set_notify_on_cancel( - exec_ctx, calld->call_combiner, - GRPC_CLOSURE_INIT(&calld->check_call_host_cancel_closure, - cancel_check_call_host, elem, - grpc_schedule_on_exec_ctx)); - } - gpr_free(call_host); - GPR_TIMER_END("auth_start_transport_stream_op_batch", 0); - return; /* early exit */ - } - } - - /* pass control down the stack */ - grpc_call_next_op(exec_ctx, elem, batch); - GPR_TIMER_END("auth_start_transport_stream_op_batch", 0); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - calld->owning_call = args->call_stack; - calld->call_combiner = args->call_combiner; - return GRPC_ERROR_NONE; -} - -static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - grpc_polling_entity *pollent) { - call_data *calld = (call_data *)elem->call_data; - calld->pollent = pollent; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - call_data *calld = (call_data *)elem->call_data; - grpc_credentials_mdelem_array_destroy(exec_ctx, &calld->md_array); - grpc_call_credentials_unref(exec_ctx, calld->creds); - if (calld->have_host) { - grpc_slice_unref_internal(exec_ctx, calld->host); - } - if (calld->have_method) { - grpc_slice_unref_internal(exec_ctx, calld->method); - } - reset_auth_metadata_context(&calld->auth_md_context); -} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - grpc_security_connector *sc = - grpc_security_connector_find_in_args(args->channel_args); - if (sc == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Security connector missing from client auth filter args"); - } - grpc_auth_context *auth_context = - grpc_find_auth_context_in_args(args->channel_args); - if (auth_context == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Auth context missing from client auth filter args"); - } - - /* grab pointers to our data from the channel element */ - channel_data *chand = (channel_data *)elem->channel_data; - - /* The first and the last filters tend to be implemented differently to - handle the case that there's no 'next' filter to call on the up or down - path */ - GPR_ASSERT(!args->is_last); - - /* initialize members */ - chand->security_connector = - (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF( - sc, "client_auth_filter"); - chand->auth_context = - GRPC_AUTH_CONTEXT_REF(auth_context, "client_auth_filter"); - return GRPC_ERROR_NONE; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - /* grab pointers to our data from the channel element */ - channel_data *chand = (channel_data *)elem->channel_data; - grpc_channel_security_connector *sc = chand->security_connector; - if (sc != NULL) { - GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &sc->base, "client_auth_filter"); - } - GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "client_auth_filter"); -} - -const grpc_channel_filter grpc_client_auth_filter = { - auth_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "client-auth"}; diff --git a/src/core/lib/security/transport/client_auth_filter.cc b/src/core/lib/security/transport/client_auth_filter.cc new file mode 100644 index 0000000000..a8464dbf9e --- /dev/null +++ b/src/core/lib/security/transport/client_auth_filter.cc @@ -0,0 +1,432 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/transport/auth_filters.h" + +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/security_connector.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/transport/static_metadata.h" + +#define MAX_CREDENTIALS_METADATA_COUNT 4 + +/* We can have a per-call credentials. */ +typedef struct { + grpc_call_stack *owning_call; + grpc_call_combiner *call_combiner; + grpc_call_credentials *creds; + bool have_host; + bool have_method; + grpc_slice host; + grpc_slice method; + /* pollset{_set} bound to this call; if we need to make external + network requests, they should be done under a pollset added to this + pollset_set so that work can progress when this call wants work to progress + */ + grpc_polling_entity *pollent; + grpc_credentials_mdelem_array md_array; + grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; + grpc_auth_metadata_context auth_md_context; + grpc_closure async_result_closure; + grpc_closure check_call_host_cancel_closure; + grpc_closure get_request_metadata_cancel_closure; +} call_data; + +/* We can have a per-channel credentials. */ +typedef struct { + grpc_channel_security_connector *security_connector; + grpc_auth_context *auth_context; +} channel_data; + +static void reset_auth_metadata_context( + grpc_auth_metadata_context *auth_md_context) { + if (auth_md_context->service_url != NULL) { + gpr_free((char *)auth_md_context->service_url); + auth_md_context->service_url = NULL; + } + if (auth_md_context->method_name != NULL) { + gpr_free((char *)auth_md_context->method_name); + auth_md_context->method_name = NULL; + } + GRPC_AUTH_CONTEXT_UNREF( + (grpc_auth_context *)auth_md_context->channel_auth_context, + "grpc_auth_metadata_context"); + auth_md_context->channel_auth_context = NULL; +} + +static void add_error(grpc_error **combined, grpc_error *error) { + if (error == GRPC_ERROR_NONE) return; + if (*combined == GRPC_ERROR_NONE) { + *combined = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Client auth metadata plugin error"); + } + *combined = grpc_error_add_child(*combined, error); +} + +static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *input_error) { + grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; + grpc_call_element *elem = + (grpc_call_element *)batch->handler_private.extra_arg; + call_data *calld = (call_data *)elem->call_data; + reset_auth_metadata_context(&calld->auth_md_context); + grpc_error *error = GRPC_ERROR_REF(input_error); + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->md_array.size <= MAX_CREDENTIALS_METADATA_COUNT); + GPR_ASSERT(batch->send_initial_metadata); + grpc_metadata_batch *mdb = + batch->payload->send_initial_metadata.send_initial_metadata; + for (size_t i = 0; i < calld->md_array.size; ++i) { + add_error(&error, grpc_metadata_batch_add_tail( + exec_ctx, mdb, &calld->md_links[i], + GRPC_MDELEM_REF(calld->md_array.md[i]))); + } + } + if (error == GRPC_ERROR_NONE) { + grpc_call_next_op(exec_ctx, elem, batch); + } else { + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAUTHENTICATED); + grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, batch, error, + calld->call_combiner); + } +} + +void build_auth_metadata_context(grpc_security_connector *sc, + grpc_auth_context *auth_context, + call_data *calld) { + char *service = grpc_slice_to_c_string(calld->method); + char *last_slash = strrchr(service, '/'); + char *method_name = NULL; + char *service_url = NULL; + reset_auth_metadata_context(&calld->auth_md_context); + if (last_slash == NULL) { + gpr_log(GPR_ERROR, "No '/' found in fully qualified method name"); + service[0] = '\0'; + } else if (last_slash == service) { + /* No service part in fully qualified method name: will just be "/". */ + service[1] = '\0'; + } else { + *last_slash = '\0'; + method_name = gpr_strdup(last_slash + 1); + } + if (method_name == NULL) method_name = gpr_strdup(""); + char *host = grpc_slice_to_c_string(calld->host); + gpr_asprintf(&service_url, "%s://%s%s", + sc->url_scheme == NULL ? "" : sc->url_scheme, host, service); + calld->auth_md_context.service_url = service_url; + calld->auth_md_context.method_name = method_name; + calld->auth_md_context.channel_auth_context = + GRPC_AUTH_CONTEXT_REF(auth_context, "grpc_auth_metadata_context"); + gpr_free(service); + gpr_free(host); +} + +static void cancel_get_request_metadata(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + if (error != GRPC_ERROR_NONE) { + grpc_call_credentials_cancel_get_request_metadata( + exec_ctx, calld->creds, &calld->md_array, GRPC_ERROR_REF(error)); + } + GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, + "cancel_get_request_metadata"); +} + +static void send_security_metadata(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + grpc_client_security_context *ctx = + (grpc_client_security_context *)batch->payload + ->context[GRPC_CONTEXT_SECURITY] + .value; + grpc_call_credentials *channel_call_creds = + chand->security_connector->request_metadata_creds; + int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL); + + if (channel_call_creds == NULL && !call_creds_has_md) { + /* Skip sending metadata altogether. */ + grpc_call_next_op(exec_ctx, elem, batch); + return; + } + + if (channel_call_creds != NULL && call_creds_has_md) { + calld->creds = grpc_composite_call_credentials_create(channel_call_creds, + ctx->creds, NULL); + if (calld->creds == NULL) { + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, batch, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Incompatible credentials set on channel and call."), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); + return; + } + } else { + calld->creds = grpc_call_credentials_ref( + call_creds_has_md ? ctx->creds : channel_call_creds); + } + + build_auth_metadata_context(&chand->security_connector->base, + chand->auth_context, calld); + + GPR_ASSERT(calld->pollent != NULL); + + GRPC_CLOSURE_INIT(&calld->async_result_closure, on_credentials_metadata, + batch, grpc_schedule_on_exec_ctx); + grpc_error *error = GRPC_ERROR_NONE; + if (grpc_call_credentials_get_request_metadata( + exec_ctx, calld->creds, calld->pollent, calld->auth_md_context, + &calld->md_array, &calld->async_result_closure, &error)) { + // Synchronous return; invoke on_credentials_metadata() directly. + on_credentials_metadata(exec_ctx, batch, error); + GRPC_ERROR_UNREF(error); + } else { + // Async return; register cancellation closure with call combiner. + GRPC_CALL_STACK_REF(calld->owning_call, "cancel_get_request_metadata"); + grpc_call_combiner_set_notify_on_cancel( + exec_ctx, calld->call_combiner, + GRPC_CLOSURE_INIT(&calld->get_request_metadata_cancel_closure, + cancel_get_request_metadata, elem, + grpc_schedule_on_exec_ctx)); + } +} + +static void on_host_checked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; + grpc_call_element *elem = + (grpc_call_element *)batch->handler_private.extra_arg; + call_data *calld = (call_data *)elem->call_data; + if (error == GRPC_ERROR_NONE) { + send_security_metadata(exec_ctx, elem, batch); + } else { + char *error_msg; + char *host = grpc_slice_to_c_string(calld->host); + gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.", + host); + gpr_free(host); + grpc_transport_stream_op_batch_finish_with_failure( + exec_ctx, batch, + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg), + GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); + gpr_free(error_msg); + } +} + +static void cancel_check_call_host(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + if (error != GRPC_ERROR_NONE) { + grpc_channel_security_connector_cancel_check_call_host( + exec_ctx, chand->security_connector, &calld->async_result_closure, + GRPC_ERROR_REF(error)); + } + GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "cancel_check_call_host"); +} + +static void auth_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + GPR_TIMER_BEGIN("auth_start_transport_stream_op_batch", 0); + + /* grab pointers to our data from the call element */ + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + + if (!batch->cancel_stream) { + GPR_ASSERT(batch->payload->context != NULL); + if (batch->payload->context[GRPC_CONTEXT_SECURITY].value == NULL) { + batch->payload->context[GRPC_CONTEXT_SECURITY].value = + grpc_client_security_context_create(); + batch->payload->context[GRPC_CONTEXT_SECURITY].destroy = + grpc_client_security_context_destroy; + } + grpc_client_security_context *sec_ctx = + (grpc_client_security_context *)batch->payload + ->context[GRPC_CONTEXT_SECURITY] + .value; + GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter"); + sec_ctx->auth_context = + GRPC_AUTH_CONTEXT_REF(chand->auth_context, "client_auth_filter"); + } + + if (batch->send_initial_metadata) { + for (grpc_linked_mdelem *l = batch->payload->send_initial_metadata + .send_initial_metadata->list.head; + l != NULL; l = l->next) { + grpc_mdelem md = l->md; + /* Pointer comparison is OK for md_elems created from the same context. + */ + if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_AUTHORITY)) { + if (calld->have_host) { + grpc_slice_unref_internal(exec_ctx, calld->host); + } + calld->host = grpc_slice_ref_internal(GRPC_MDVALUE(md)); + calld->have_host = true; + } else if (grpc_slice_eq(GRPC_MDKEY(md), GRPC_MDSTR_PATH)) { + if (calld->have_method) { + grpc_slice_unref_internal(exec_ctx, calld->method); + } + calld->method = grpc_slice_ref_internal(GRPC_MDVALUE(md)); + calld->have_method = true; + } + } + if (calld->have_host) { + batch->handler_private.extra_arg = elem; + GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch, + grpc_schedule_on_exec_ctx); + char *call_host = grpc_slice_to_c_string(calld->host); + grpc_error *error = GRPC_ERROR_NONE; + if (grpc_channel_security_connector_check_call_host( + exec_ctx, chand->security_connector, call_host, + chand->auth_context, &calld->async_result_closure, &error)) { + // Synchronous return; invoke on_host_checked() directly. + on_host_checked(exec_ctx, batch, error); + GRPC_ERROR_UNREF(error); + } else { + // Async return; register cancellation closure with call combiner. + GRPC_CALL_STACK_REF(calld->owning_call, "cancel_check_call_host"); + grpc_call_combiner_set_notify_on_cancel( + exec_ctx, calld->call_combiner, + GRPC_CLOSURE_INIT(&calld->check_call_host_cancel_closure, + cancel_check_call_host, elem, + grpc_schedule_on_exec_ctx)); + } + gpr_free(call_host); + GPR_TIMER_END("auth_start_transport_stream_op_batch", 0); + return; /* early exit */ + } + } + + /* pass control down the stack */ + grpc_call_next_op(exec_ctx, elem, batch); + GPR_TIMER_END("auth_start_transport_stream_op_batch", 0); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + calld->owning_call = args->call_stack; + calld->call_combiner = args->call_combiner; + return GRPC_ERROR_NONE; +} + +static void set_pollset_or_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + grpc_polling_entity *pollent) { + call_data *calld = (call_data *)elem->call_data; + calld->pollent = pollent; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + call_data *calld = (call_data *)elem->call_data; + grpc_credentials_mdelem_array_destroy(exec_ctx, &calld->md_array); + grpc_call_credentials_unref(exec_ctx, calld->creds); + if (calld->have_host) { + grpc_slice_unref_internal(exec_ctx, calld->host); + } + if (calld->have_method) { + grpc_slice_unref_internal(exec_ctx, calld->method); + } + reset_auth_metadata_context(&calld->auth_md_context); +} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + grpc_security_connector *sc = + grpc_security_connector_find_in_args(args->channel_args); + if (sc == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Security connector missing from client auth filter args"); + } + grpc_auth_context *auth_context = + grpc_find_auth_context_in_args(args->channel_args); + if (auth_context == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Auth context missing from client auth filter args"); + } + + /* grab pointers to our data from the channel element */ + channel_data *chand = (channel_data *)elem->channel_data; + + /* The first and the last filters tend to be implemented differently to + handle the case that there's no 'next' filter to call on the up or down + path */ + GPR_ASSERT(!args->is_last); + + /* initialize members */ + chand->security_connector = + (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF( + sc, "client_auth_filter"); + chand->auth_context = + GRPC_AUTH_CONTEXT_REF(auth_context, "client_auth_filter"); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + /* grab pointers to our data from the channel element */ + channel_data *chand = (channel_data *)elem->channel_data; + grpc_channel_security_connector *sc = chand->security_connector; + if (sc != NULL) { + GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, &sc->base, "client_auth_filter"); + } + GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "client_auth_filter"); +} + +const grpc_channel_filter grpc_client_auth_filter = { + auth_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "client-auth"}; diff --git a/src/core/lib/security/transport/lb_targets_info.c b/src/core/lib/security/transport/lb_targets_info.c deleted file mode 100644 index 947fc1addf..0000000000 --- a/src/core/lib/security/transport/lb_targets_info.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/transport/lb_targets_info.h" - -/* Channel arg key for the mapping of LB server addresses to their names for - * secure naming purposes. */ -#define GRPC_ARG_LB_SECURE_NAMING_MAP "grpc.lb_secure_naming_map" - -static void *targets_info_copy(void *p) { - return grpc_slice_hash_table_ref((grpc_slice_hash_table *)p); -} -static void targets_info_destroy(grpc_exec_ctx *exec_ctx, void *p) { - grpc_slice_hash_table_unref(exec_ctx, (grpc_slice_hash_table *)p); -} -static int targets_info_cmp(void *a, void *b) { - return grpc_slice_hash_table_cmp((const grpc_slice_hash_table *)a, - (const grpc_slice_hash_table *)b); -} -static const grpc_arg_pointer_vtable server_to_balancer_names_vtable = { - targets_info_copy, targets_info_destroy, targets_info_cmp}; - -grpc_arg grpc_lb_targets_info_create_channel_arg( - grpc_slice_hash_table *targets_info) { - return grpc_channel_arg_pointer_create((char *)GRPC_ARG_LB_SECURE_NAMING_MAP, - targets_info, - &server_to_balancer_names_vtable); -} - -grpc_slice_hash_table *grpc_lb_targets_info_find_in_args( - const grpc_channel_args *args) { - const grpc_arg *targets_info_arg = - grpc_channel_args_find(args, GRPC_ARG_LB_SECURE_NAMING_MAP); - if (targets_info_arg != NULL) { - GPR_ASSERT(targets_info_arg->type == GRPC_ARG_POINTER); - return (grpc_slice_hash_table *)targets_info_arg->value.pointer.p; - } - return NULL; -} diff --git a/src/core/lib/security/transport/lb_targets_info.cc b/src/core/lib/security/transport/lb_targets_info.cc new file mode 100644 index 0000000000..947fc1addf --- /dev/null +++ b/src/core/lib/security/transport/lb_targets_info.cc @@ -0,0 +1,57 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/transport/lb_targets_info.h" + +/* Channel arg key for the mapping of LB server addresses to their names for + * secure naming purposes. */ +#define GRPC_ARG_LB_SECURE_NAMING_MAP "grpc.lb_secure_naming_map" + +static void *targets_info_copy(void *p) { + return grpc_slice_hash_table_ref((grpc_slice_hash_table *)p); +} +static void targets_info_destroy(grpc_exec_ctx *exec_ctx, void *p) { + grpc_slice_hash_table_unref(exec_ctx, (grpc_slice_hash_table *)p); +} +static int targets_info_cmp(void *a, void *b) { + return grpc_slice_hash_table_cmp((const grpc_slice_hash_table *)a, + (const grpc_slice_hash_table *)b); +} +static const grpc_arg_pointer_vtable server_to_balancer_names_vtable = { + targets_info_copy, targets_info_destroy, targets_info_cmp}; + +grpc_arg grpc_lb_targets_info_create_channel_arg( + grpc_slice_hash_table *targets_info) { + return grpc_channel_arg_pointer_create((char *)GRPC_ARG_LB_SECURE_NAMING_MAP, + targets_info, + &server_to_balancer_names_vtable); +} + +grpc_slice_hash_table *grpc_lb_targets_info_find_in_args( + const grpc_channel_args *args) { + const grpc_arg *targets_info_arg = + grpc_channel_args_find(args, GRPC_ARG_LB_SECURE_NAMING_MAP); + if (targets_info_arg != NULL) { + GPR_ASSERT(targets_info_arg->type == GRPC_ARG_POINTER); + return (grpc_slice_hash_table *)targets_info_arg->value.pointer.p; + } + return NULL; +} diff --git a/src/core/lib/security/transport/secure_endpoint.c b/src/core/lib/security/transport/secure_endpoint.c deleted file mode 100644 index ae5633b82c..0000000000 --- a/src/core/lib/security/transport/secure_endpoint.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when - using that endpoint. Because of various transitive includes in uv.h, - including windows.h on Windows, uv.h must be included before other system - headers. Therefore, sockaddr.h must always be included first */ -#include "src/core/lib/iomgr/sockaddr.h" - -#include -#include -#include -#include -#include -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/security/transport/secure_endpoint.h" -#include "src/core/lib/security/transport/tsi_error.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/tsi/transport_security_grpc.h" - -#define STAGING_BUFFER_SIZE 8192 - -typedef struct { - grpc_endpoint base; - grpc_endpoint *wrapped_ep; - struct tsi_frame_protector *protector; - struct tsi_zero_copy_grpc_protector *zero_copy_protector; - gpr_mu protector_mu; - /* saved upper level callbacks and user_data. */ - grpc_closure *read_cb; - grpc_closure *write_cb; - grpc_closure on_read; - grpc_slice_buffer *read_buffer; - grpc_slice_buffer source_buffer; - /* saved handshaker leftover data to unprotect. */ - grpc_slice_buffer leftover_bytes; - /* buffers for read and write */ - grpc_slice read_staging_buffer; - - grpc_slice write_staging_buffer; - grpc_slice_buffer output_buffer; - - gpr_refcount ref; -} secure_endpoint; - -grpc_tracer_flag grpc_trace_secure_endpoint = - GRPC_TRACER_INITIALIZER(false, "secure_endpoint"); - -static void destroy(grpc_exec_ctx *exec_ctx, secure_endpoint *secure_ep) { - secure_endpoint *ep = secure_ep; - grpc_endpoint_destroy(exec_ctx, ep->wrapped_ep); - tsi_frame_protector_destroy(ep->protector); - tsi_zero_copy_grpc_protector_destroy(exec_ctx, ep->zero_copy_protector); - grpc_slice_buffer_destroy_internal(exec_ctx, &ep->leftover_bytes); - grpc_slice_unref_internal(exec_ctx, ep->read_staging_buffer); - grpc_slice_unref_internal(exec_ctx, ep->write_staging_buffer); - grpc_slice_buffer_destroy_internal(exec_ctx, &ep->output_buffer); - grpc_slice_buffer_destroy_internal(exec_ctx, &ep->source_buffer); - gpr_mu_destroy(&ep->protector_mu); - gpr_free(ep); -} - -#ifndef NDEBUG -#define SECURE_ENDPOINT_UNREF(exec_ctx, ep, reason) \ - secure_endpoint_unref((exec_ctx), (ep), (reason), __FILE__, __LINE__) -#define SECURE_ENDPOINT_REF(ep, reason) \ - secure_endpoint_ref((ep), (reason), __FILE__, __LINE__) -static void secure_endpoint_unref(grpc_exec_ctx *exec_ctx, secure_endpoint *ep, - const char *reason, const char *file, - int line) { - if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { - gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val, - val - 1); - } - if (gpr_unref(&ep->ref)) { - destroy(exec_ctx, ep); - } -} - -static void secure_endpoint_ref(secure_endpoint *ep, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { - gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "SECENDP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val, - val + 1); - } - gpr_ref(&ep->ref); -} -#else -#define SECURE_ENDPOINT_UNREF(exec_ctx, ep, reason) \ - secure_endpoint_unref((exec_ctx), (ep)) -#define SECURE_ENDPOINT_REF(ep, reason) secure_endpoint_ref((ep)) -static void secure_endpoint_unref(grpc_exec_ctx *exec_ctx, - secure_endpoint *ep) { - if (gpr_unref(&ep->ref)) { - destroy(exec_ctx, ep); - } -} - -static void secure_endpoint_ref(secure_endpoint *ep) { gpr_ref(&ep->ref); } -#endif - -static void flush_read_staging_buffer(secure_endpoint *ep, uint8_t **cur, - uint8_t **end) { - grpc_slice_buffer_add(ep->read_buffer, ep->read_staging_buffer); - ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); - *cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer); - *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer); -} - -static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep, - grpc_error *error) { - if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { - size_t i; - for (i = 0; i < ep->read_buffer->count; i++) { - char *data = grpc_dump_slice(ep->read_buffer->slices[i], - GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "READ %p: %s", ep, data); - gpr_free(data); - } - } - ep->read_buffer = NULL; - GRPC_CLOSURE_SCHED(exec_ctx, ep->read_cb, error); - SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read"); -} - -static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_error *error) { - unsigned i; - uint8_t keep_looping = 0; - tsi_result result = TSI_OK; - secure_endpoint *ep = (secure_endpoint *)user_data; - uint8_t *cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer); - uint8_t *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer); - - if (error != GRPC_ERROR_NONE) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer); - call_read_cb(exec_ctx, ep, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Secure read failed", &error, 1)); - return; - } - - if (ep->zero_copy_protector != NULL) { - // Use zero-copy grpc protector to unprotect. - result = tsi_zero_copy_grpc_protector_unprotect( - exec_ctx, ep->zero_copy_protector, &ep->source_buffer, ep->read_buffer); - } else { - // Use frame protector to unprotect. - /* TODO(yangg) check error, maybe bail out early */ - for (i = 0; i < ep->source_buffer.count; i++) { - grpc_slice encrypted = ep->source_buffer.slices[i]; - uint8_t *message_bytes = GRPC_SLICE_START_PTR(encrypted); - size_t message_size = GRPC_SLICE_LENGTH(encrypted); - - while (message_size > 0 || keep_looping) { - size_t unprotected_buffer_size_written = (size_t)(end - cur); - size_t processed_message_size = message_size; - gpr_mu_lock(&ep->protector_mu); - result = tsi_frame_protector_unprotect( - ep->protector, message_bytes, &processed_message_size, cur, - &unprotected_buffer_size_written); - gpr_mu_unlock(&ep->protector_mu); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Decryption error: %s", - tsi_result_to_string(result)); - break; - } - message_bytes += processed_message_size; - message_size -= processed_message_size; - cur += unprotected_buffer_size_written; - - if (cur == end) { - flush_read_staging_buffer(ep, &cur, &end); - /* Force to enter the loop again to extract buffered bytes in - protector. The bytes could be buffered because of running out of - staging_buffer. If this happens at the end of all slices, doing - another unprotect avoids leaving data in the protector. */ - keep_looping = 1; - } else if (unprotected_buffer_size_written > 0) { - keep_looping = 1; - } else { - keep_looping = 0; - } - } - if (result != TSI_OK) break; - } - - if (cur != GRPC_SLICE_START_PTR(ep->read_staging_buffer)) { - grpc_slice_buffer_add( - ep->read_buffer, - grpc_slice_split_head( - &ep->read_staging_buffer, - (size_t)(cur - GRPC_SLICE_START_PTR(ep->read_staging_buffer)))); - } - } - - /* TODO(yangg) experiment with moving this block after read_cb to see if it - helps latency */ - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->source_buffer); - - if (result != TSI_OK) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer); - call_read_cb( - exec_ctx, ep, - grpc_set_tsi_error_result( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unwrap failed"), result)); - return; - } - - call_read_cb(exec_ctx, ep, GRPC_ERROR_NONE); -} - -static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, - grpc_slice_buffer *slices, grpc_closure *cb) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - ep->read_cb = cb; - ep->read_buffer = slices; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer); - - SECURE_ENDPOINT_REF(ep, "read"); - if (ep->leftover_bytes.count) { - grpc_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer); - GPR_ASSERT(ep->leftover_bytes.count == 0); - on_read(exec_ctx, ep, GRPC_ERROR_NONE); - return; - } - - grpc_endpoint_read(exec_ctx, ep->wrapped_ep, &ep->source_buffer, - &ep->on_read); -} - -static void flush_write_staging_buffer(secure_endpoint *ep, uint8_t **cur, - uint8_t **end) { - grpc_slice_buffer_add(&ep->output_buffer, ep->write_staging_buffer); - ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); - *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer); - *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer); -} - -static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, - grpc_slice_buffer *slices, grpc_closure *cb) { - GPR_TIMER_BEGIN("secure_endpoint.endpoint_write", 0); - - unsigned i; - tsi_result result = TSI_OK; - secure_endpoint *ep = (secure_endpoint *)secure_ep; - uint8_t *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer); - uint8_t *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer); - - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer); - - if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { - for (i = 0; i < slices->count; i++) { - char *data = - grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data); - gpr_free(data); - } - } - - if (ep->zero_copy_protector != NULL) { - // Use zero-copy grpc protector to protect. - result = tsi_zero_copy_grpc_protector_protect( - exec_ctx, ep->zero_copy_protector, slices, &ep->output_buffer); - } else { - // Use frame protector to protect. - for (i = 0; i < slices->count; i++) { - grpc_slice plain = slices->slices[i]; - uint8_t *message_bytes = GRPC_SLICE_START_PTR(plain); - size_t message_size = GRPC_SLICE_LENGTH(plain); - while (message_size > 0) { - size_t protected_buffer_size_to_send = (size_t)(end - cur); - size_t processed_message_size = message_size; - gpr_mu_lock(&ep->protector_mu); - result = tsi_frame_protector_protect(ep->protector, message_bytes, - &processed_message_size, cur, - &protected_buffer_size_to_send); - gpr_mu_unlock(&ep->protector_mu); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Encryption error: %s", - tsi_result_to_string(result)); - break; - } - message_bytes += processed_message_size; - message_size -= processed_message_size; - cur += protected_buffer_size_to_send; - - if (cur == end) { - flush_write_staging_buffer(ep, &cur, &end); - } - } - if (result != TSI_OK) break; - } - if (result == TSI_OK) { - size_t still_pending_size; - do { - size_t protected_buffer_size_to_send = (size_t)(end - cur); - gpr_mu_lock(&ep->protector_mu); - result = tsi_frame_protector_protect_flush( - ep->protector, cur, &protected_buffer_size_to_send, - &still_pending_size); - gpr_mu_unlock(&ep->protector_mu); - if (result != TSI_OK) break; - cur += protected_buffer_size_to_send; - if (cur == end) { - flush_write_staging_buffer(ep, &cur, &end); - } - } while (still_pending_size > 0); - if (cur != GRPC_SLICE_START_PTR(ep->write_staging_buffer)) { - grpc_slice_buffer_add( - &ep->output_buffer, - grpc_slice_split_head( - &ep->write_staging_buffer, - (size_t)(cur - - GRPC_SLICE_START_PTR(ep->write_staging_buffer)))); - } - } - } - - if (result != TSI_OK) { - /* TODO(yangg) do different things according to the error type? */ - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer); - GRPC_CLOSURE_SCHED( - exec_ctx, cb, - grpc_set_tsi_error_result( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Wrap failed"), result)); - GPR_TIMER_END("secure_endpoint.endpoint_write", 0); - return; - } - - grpc_endpoint_write(exec_ctx, ep->wrapped_ep, &ep->output_buffer, cb); - GPR_TIMER_END("secure_endpoint.endpoint_write", 0); -} - -static void endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, - grpc_error *why) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_shutdown(exec_ctx, ep->wrapped_ep, why); -} - -static void endpoint_destroy(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - SECURE_ENDPOINT_UNREF(exec_ctx, ep, "destroy"); -} - -static void endpoint_add_to_pollset(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep, - grpc_pollset *pollset) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_add_to_pollset(exec_ctx, ep->wrapped_ep, pollset); -} - -static void endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx, - grpc_endpoint *secure_ep, - grpc_pollset_set *pollset_set) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - grpc_endpoint_add_to_pollset_set(exec_ctx, ep->wrapped_ep, pollset_set); -} - -static char *endpoint_get_peer(grpc_endpoint *secure_ep) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - return grpc_endpoint_get_peer(ep->wrapped_ep); -} - -static int endpoint_get_fd(grpc_endpoint *secure_ep) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - return grpc_endpoint_get_fd(ep->wrapped_ep); -} - -static grpc_resource_user *endpoint_get_resource_user( - grpc_endpoint *secure_ep) { - secure_endpoint *ep = (secure_endpoint *)secure_ep; - return grpc_endpoint_get_resource_user(ep->wrapped_ep); -} - -static const grpc_endpoint_vtable vtable = {endpoint_read, - endpoint_write, - endpoint_add_to_pollset, - endpoint_add_to_pollset_set, - endpoint_shutdown, - endpoint_destroy, - endpoint_get_resource_user, - endpoint_get_peer, - endpoint_get_fd}; - -grpc_endpoint *grpc_secure_endpoint_create( - struct tsi_frame_protector *protector, - struct tsi_zero_copy_grpc_protector *zero_copy_protector, - grpc_endpoint *transport, grpc_slice *leftover_slices, - size_t leftover_nslices) { - size_t i; - secure_endpoint *ep = (secure_endpoint *)gpr_malloc(sizeof(secure_endpoint)); - ep->base.vtable = &vtable; - ep->wrapped_ep = transport; - ep->protector = protector; - ep->zero_copy_protector = zero_copy_protector; - grpc_slice_buffer_init(&ep->leftover_bytes); - for (i = 0; i < leftover_nslices; i++) { - grpc_slice_buffer_add(&ep->leftover_bytes, - grpc_slice_ref_internal(leftover_slices[i])); - } - ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); - ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); - grpc_slice_buffer_init(&ep->output_buffer); - grpc_slice_buffer_init(&ep->source_buffer); - ep->read_buffer = NULL; - GRPC_CLOSURE_INIT(&ep->on_read, on_read, ep, grpc_schedule_on_exec_ctx); - gpr_mu_init(&ep->protector_mu); - gpr_ref_init(&ep->ref, 1); - return &ep->base; -} diff --git a/src/core/lib/security/transport/secure_endpoint.cc b/src/core/lib/security/transport/secure_endpoint.cc new file mode 100644 index 0000000000..ae5633b82c --- /dev/null +++ b/src/core/lib/security/transport/secure_endpoint.cc @@ -0,0 +1,433 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* With the addition of a libuv endpoint, sockaddr.h now includes uv.h when + using that endpoint. Because of various transitive includes in uv.h, + including windows.h on Windows, uv.h must be included before other system + headers. Therefore, sockaddr.h must always be included first */ +#include "src/core/lib/iomgr/sockaddr.h" + +#include +#include +#include +#include +#include +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/tsi_error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/tsi/transport_security_grpc.h" + +#define STAGING_BUFFER_SIZE 8192 + +typedef struct { + grpc_endpoint base; + grpc_endpoint *wrapped_ep; + struct tsi_frame_protector *protector; + struct tsi_zero_copy_grpc_protector *zero_copy_protector; + gpr_mu protector_mu; + /* saved upper level callbacks and user_data. */ + grpc_closure *read_cb; + grpc_closure *write_cb; + grpc_closure on_read; + grpc_slice_buffer *read_buffer; + grpc_slice_buffer source_buffer; + /* saved handshaker leftover data to unprotect. */ + grpc_slice_buffer leftover_bytes; + /* buffers for read and write */ + grpc_slice read_staging_buffer; + + grpc_slice write_staging_buffer; + grpc_slice_buffer output_buffer; + + gpr_refcount ref; +} secure_endpoint; + +grpc_tracer_flag grpc_trace_secure_endpoint = + GRPC_TRACER_INITIALIZER(false, "secure_endpoint"); + +static void destroy(grpc_exec_ctx *exec_ctx, secure_endpoint *secure_ep) { + secure_endpoint *ep = secure_ep; + grpc_endpoint_destroy(exec_ctx, ep->wrapped_ep); + tsi_frame_protector_destroy(ep->protector); + tsi_zero_copy_grpc_protector_destroy(exec_ctx, ep->zero_copy_protector); + grpc_slice_buffer_destroy_internal(exec_ctx, &ep->leftover_bytes); + grpc_slice_unref_internal(exec_ctx, ep->read_staging_buffer); + grpc_slice_unref_internal(exec_ctx, ep->write_staging_buffer); + grpc_slice_buffer_destroy_internal(exec_ctx, &ep->output_buffer); + grpc_slice_buffer_destroy_internal(exec_ctx, &ep->source_buffer); + gpr_mu_destroy(&ep->protector_mu); + gpr_free(ep); +} + +#ifndef NDEBUG +#define SECURE_ENDPOINT_UNREF(exec_ctx, ep, reason) \ + secure_endpoint_unref((exec_ctx), (ep), (reason), __FILE__, __LINE__) +#define SECURE_ENDPOINT_REF(ep, reason) \ + secure_endpoint_ref((ep), (reason), __FILE__, __LINE__) +static void secure_endpoint_unref(grpc_exec_ctx *exec_ctx, secure_endpoint *ep, + const char *reason, const char *file, + int line) { + if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { + gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECENDP unref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val, + val - 1); + } + if (gpr_unref(&ep->ref)) { + destroy(exec_ctx, ep); + } +} + +static void secure_endpoint_ref(secure_endpoint *ep, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { + gpr_atm val = gpr_atm_no_barrier_load(&ep->ref.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECENDP ref %p : %s %" PRIdPTR " -> %" PRIdPTR, ep, reason, val, + val + 1); + } + gpr_ref(&ep->ref); +} +#else +#define SECURE_ENDPOINT_UNREF(exec_ctx, ep, reason) \ + secure_endpoint_unref((exec_ctx), (ep)) +#define SECURE_ENDPOINT_REF(ep, reason) secure_endpoint_ref((ep)) +static void secure_endpoint_unref(grpc_exec_ctx *exec_ctx, + secure_endpoint *ep) { + if (gpr_unref(&ep->ref)) { + destroy(exec_ctx, ep); + } +} + +static void secure_endpoint_ref(secure_endpoint *ep) { gpr_ref(&ep->ref); } +#endif + +static void flush_read_staging_buffer(secure_endpoint *ep, uint8_t **cur, + uint8_t **end) { + grpc_slice_buffer_add(ep->read_buffer, ep->read_staging_buffer); + ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + *cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer); + *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer); +} + +static void call_read_cb(grpc_exec_ctx *exec_ctx, secure_endpoint *ep, + grpc_error *error) { + if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { + size_t i; + for (i = 0; i < ep->read_buffer->count; i++) { + char *data = grpc_dump_slice(ep->read_buffer->slices[i], + GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "READ %p: %s", ep, data); + gpr_free(data); + } + } + ep->read_buffer = NULL; + GRPC_CLOSURE_SCHED(exec_ctx, ep->read_cb, error); + SECURE_ENDPOINT_UNREF(exec_ctx, ep, "read"); +} + +static void on_read(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_error *error) { + unsigned i; + uint8_t keep_looping = 0; + tsi_result result = TSI_OK; + secure_endpoint *ep = (secure_endpoint *)user_data; + uint8_t *cur = GRPC_SLICE_START_PTR(ep->read_staging_buffer); + uint8_t *end = GRPC_SLICE_END_PTR(ep->read_staging_buffer); + + if (error != GRPC_ERROR_NONE) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer); + call_read_cb(exec_ctx, ep, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Secure read failed", &error, 1)); + return; + } + + if (ep->zero_copy_protector != NULL) { + // Use zero-copy grpc protector to unprotect. + result = tsi_zero_copy_grpc_protector_unprotect( + exec_ctx, ep->zero_copy_protector, &ep->source_buffer, ep->read_buffer); + } else { + // Use frame protector to unprotect. + /* TODO(yangg) check error, maybe bail out early */ + for (i = 0; i < ep->source_buffer.count; i++) { + grpc_slice encrypted = ep->source_buffer.slices[i]; + uint8_t *message_bytes = GRPC_SLICE_START_PTR(encrypted); + size_t message_size = GRPC_SLICE_LENGTH(encrypted); + + while (message_size > 0 || keep_looping) { + size_t unprotected_buffer_size_written = (size_t)(end - cur); + size_t processed_message_size = message_size; + gpr_mu_lock(&ep->protector_mu); + result = tsi_frame_protector_unprotect( + ep->protector, message_bytes, &processed_message_size, cur, + &unprotected_buffer_size_written); + gpr_mu_unlock(&ep->protector_mu); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Decryption error: %s", + tsi_result_to_string(result)); + break; + } + message_bytes += processed_message_size; + message_size -= processed_message_size; + cur += unprotected_buffer_size_written; + + if (cur == end) { + flush_read_staging_buffer(ep, &cur, &end); + /* Force to enter the loop again to extract buffered bytes in + protector. The bytes could be buffered because of running out of + staging_buffer. If this happens at the end of all slices, doing + another unprotect avoids leaving data in the protector. */ + keep_looping = 1; + } else if (unprotected_buffer_size_written > 0) { + keep_looping = 1; + } else { + keep_looping = 0; + } + } + if (result != TSI_OK) break; + } + + if (cur != GRPC_SLICE_START_PTR(ep->read_staging_buffer)) { + grpc_slice_buffer_add( + ep->read_buffer, + grpc_slice_split_head( + &ep->read_staging_buffer, + (size_t)(cur - GRPC_SLICE_START_PTR(ep->read_staging_buffer)))); + } + } + + /* TODO(yangg) experiment with moving this block after read_cb to see if it + helps latency */ + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->source_buffer); + + if (result != TSI_OK) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer); + call_read_cb( + exec_ctx, ep, + grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unwrap failed"), result)); + return; + } + + call_read_cb(exec_ctx, ep, GRPC_ERROR_NONE); +} + +static void endpoint_read(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, + grpc_slice_buffer *slices, grpc_closure *cb) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + ep->read_cb = cb; + ep->read_buffer = slices; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, ep->read_buffer); + + SECURE_ENDPOINT_REF(ep, "read"); + if (ep->leftover_bytes.count) { + grpc_slice_buffer_swap(&ep->leftover_bytes, &ep->source_buffer); + GPR_ASSERT(ep->leftover_bytes.count == 0); + on_read(exec_ctx, ep, GRPC_ERROR_NONE); + return; + } + + grpc_endpoint_read(exec_ctx, ep->wrapped_ep, &ep->source_buffer, + &ep->on_read); +} + +static void flush_write_staging_buffer(secure_endpoint *ep, uint8_t **cur, + uint8_t **end) { + grpc_slice_buffer_add(&ep->output_buffer, ep->write_staging_buffer); + ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer); + *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer); +} + +static void endpoint_write(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, + grpc_slice_buffer *slices, grpc_closure *cb) { + GPR_TIMER_BEGIN("secure_endpoint.endpoint_write", 0); + + unsigned i; + tsi_result result = TSI_OK; + secure_endpoint *ep = (secure_endpoint *)secure_ep; + uint8_t *cur = GRPC_SLICE_START_PTR(ep->write_staging_buffer); + uint8_t *end = GRPC_SLICE_END_PTR(ep->write_staging_buffer); + + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer); + + if (GRPC_TRACER_ON(grpc_trace_secure_endpoint)) { + for (i = 0; i < slices->count; i++) { + char *data = + grpc_dump_slice(slices->slices[i], GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "WRITE %p: %s", ep, data); + gpr_free(data); + } + } + + if (ep->zero_copy_protector != NULL) { + // Use zero-copy grpc protector to protect. + result = tsi_zero_copy_grpc_protector_protect( + exec_ctx, ep->zero_copy_protector, slices, &ep->output_buffer); + } else { + // Use frame protector to protect. + for (i = 0; i < slices->count; i++) { + grpc_slice plain = slices->slices[i]; + uint8_t *message_bytes = GRPC_SLICE_START_PTR(plain); + size_t message_size = GRPC_SLICE_LENGTH(plain); + while (message_size > 0) { + size_t protected_buffer_size_to_send = (size_t)(end - cur); + size_t processed_message_size = message_size; + gpr_mu_lock(&ep->protector_mu); + result = tsi_frame_protector_protect(ep->protector, message_bytes, + &processed_message_size, cur, + &protected_buffer_size_to_send); + gpr_mu_unlock(&ep->protector_mu); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Encryption error: %s", + tsi_result_to_string(result)); + break; + } + message_bytes += processed_message_size; + message_size -= processed_message_size; + cur += protected_buffer_size_to_send; + + if (cur == end) { + flush_write_staging_buffer(ep, &cur, &end); + } + } + if (result != TSI_OK) break; + } + if (result == TSI_OK) { + size_t still_pending_size; + do { + size_t protected_buffer_size_to_send = (size_t)(end - cur); + gpr_mu_lock(&ep->protector_mu); + result = tsi_frame_protector_protect_flush( + ep->protector, cur, &protected_buffer_size_to_send, + &still_pending_size); + gpr_mu_unlock(&ep->protector_mu); + if (result != TSI_OK) break; + cur += protected_buffer_size_to_send; + if (cur == end) { + flush_write_staging_buffer(ep, &cur, &end); + } + } while (still_pending_size > 0); + if (cur != GRPC_SLICE_START_PTR(ep->write_staging_buffer)) { + grpc_slice_buffer_add( + &ep->output_buffer, + grpc_slice_split_head( + &ep->write_staging_buffer, + (size_t)(cur - + GRPC_SLICE_START_PTR(ep->write_staging_buffer)))); + } + } + } + + if (result != TSI_OK) { + /* TODO(yangg) do different things according to the error type? */ + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &ep->output_buffer); + GRPC_CLOSURE_SCHED( + exec_ctx, cb, + grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Wrap failed"), result)); + GPR_TIMER_END("secure_endpoint.endpoint_write", 0); + return; + } + + grpc_endpoint_write(exec_ctx, ep->wrapped_ep, &ep->output_buffer, cb); + GPR_TIMER_END("secure_endpoint.endpoint_write", 0); +} + +static void endpoint_shutdown(grpc_exec_ctx *exec_ctx, grpc_endpoint *secure_ep, + grpc_error *why) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + grpc_endpoint_shutdown(exec_ctx, ep->wrapped_ep, why); +} + +static void endpoint_destroy(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + SECURE_ENDPOINT_UNREF(exec_ctx, ep, "destroy"); +} + +static void endpoint_add_to_pollset(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep, + grpc_pollset *pollset) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + grpc_endpoint_add_to_pollset(exec_ctx, ep->wrapped_ep, pollset); +} + +static void endpoint_add_to_pollset_set(grpc_exec_ctx *exec_ctx, + grpc_endpoint *secure_ep, + grpc_pollset_set *pollset_set) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + grpc_endpoint_add_to_pollset_set(exec_ctx, ep->wrapped_ep, pollset_set); +} + +static char *endpoint_get_peer(grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + return grpc_endpoint_get_peer(ep->wrapped_ep); +} + +static int endpoint_get_fd(grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + return grpc_endpoint_get_fd(ep->wrapped_ep); +} + +static grpc_resource_user *endpoint_get_resource_user( + grpc_endpoint *secure_ep) { + secure_endpoint *ep = (secure_endpoint *)secure_ep; + return grpc_endpoint_get_resource_user(ep->wrapped_ep); +} + +static const grpc_endpoint_vtable vtable = {endpoint_read, + endpoint_write, + endpoint_add_to_pollset, + endpoint_add_to_pollset_set, + endpoint_shutdown, + endpoint_destroy, + endpoint_get_resource_user, + endpoint_get_peer, + endpoint_get_fd}; + +grpc_endpoint *grpc_secure_endpoint_create( + struct tsi_frame_protector *protector, + struct tsi_zero_copy_grpc_protector *zero_copy_protector, + grpc_endpoint *transport, grpc_slice *leftover_slices, + size_t leftover_nslices) { + size_t i; + secure_endpoint *ep = (secure_endpoint *)gpr_malloc(sizeof(secure_endpoint)); + ep->base.vtable = &vtable; + ep->wrapped_ep = transport; + ep->protector = protector; + ep->zero_copy_protector = zero_copy_protector; + grpc_slice_buffer_init(&ep->leftover_bytes); + for (i = 0; i < leftover_nslices; i++) { + grpc_slice_buffer_add(&ep->leftover_bytes, + grpc_slice_ref_internal(leftover_slices[i])); + } + ep->write_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + ep->read_staging_buffer = GRPC_SLICE_MALLOC(STAGING_BUFFER_SIZE); + grpc_slice_buffer_init(&ep->output_buffer); + grpc_slice_buffer_init(&ep->source_buffer); + ep->read_buffer = NULL; + GRPC_CLOSURE_INIT(&ep->on_read, on_read, ep, grpc_schedule_on_exec_ctx); + gpr_mu_init(&ep->protector_mu); + gpr_ref_init(&ep->ref, 1); + return &ep->base; +} diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c deleted file mode 100644 index 51844fb91f..0000000000 --- a/src/core/lib/security/transport/security_connector.c +++ /dev/null @@ -1,921 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/transport/security_connector.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/ext/transport/chttp2/alpn/alpn.h" -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker.h" -#include "src/core/lib/iomgr/load_file.h" -#include "src/core/lib/security/context/security_context.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/credentials/fake/fake_credentials.h" -#include "src/core/lib/security/transport/lb_targets_info.h" -#include "src/core/lib/security/transport/secure_endpoint.h" -#include "src/core/lib/security/transport/security_handshaker.h" -#include "src/core/lib/support/env.h" -#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" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_security_connector_refcount = - GRPC_TRACER_INITIALIZER(false, "security_connector_refcount"); -#endif - -/* -- Constants. -- */ - -#ifndef INSTALL_PREFIX -static const char *installed_roots_path = "/usr/share/grpc/roots.pem"; -#else -static const char *installed_roots_path = - INSTALL_PREFIX "/share/grpc/roots.pem"; -#endif - -/* -- Overridden default roots. -- */ - -static grpc_ssl_roots_override_callback ssl_roots_override_cb = NULL; - -void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) { - ssl_roots_override_cb = cb; -} - -/* -- Cipher suites. -- */ - -/* Defines the cipher suites that we accept by default. All these cipher suites - are compliant with HTTP2. */ -#define GRPC_SSL_CIPHER_SUITES \ - "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384" - -static gpr_once cipher_suites_once = GPR_ONCE_INIT; -static const char *cipher_suites = NULL; - -static void init_cipher_suites(void) { - char *overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES"); - cipher_suites = overridden != NULL ? overridden : GRPC_SSL_CIPHER_SUITES; -} - -static const char *ssl_cipher_suites(void) { - gpr_once_init(&cipher_suites_once, init_cipher_suites); - return cipher_suites; -} - -/* -- Common methods. -- */ - -/* Returns the first property with that name. */ -const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, - const char *name) { - size_t i; - if (peer == NULL) return NULL; - for (i = 0; i < peer->property_count; i++) { - const tsi_peer_property *property = &peer->properties[i]; - if (name == NULL && property->name == NULL) { - return property; - } - if (name != NULL && property->name != NULL && - strcmp(property->name, name) == 0) { - return property; - } - } - return NULL; -} - -void grpc_channel_security_connector_add_handshakers( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector, - grpc_handshake_manager *handshake_mgr) { - if (connector != NULL) { - connector->add_handshakers(exec_ctx, connector, handshake_mgr); - } -} - -void grpc_server_security_connector_add_handshakers( - grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector, - grpc_handshake_manager *handshake_mgr) { - if (connector != NULL) { - connector->add_handshakers(exec_ctx, connector, handshake_mgr); - } -} - -void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - if (sc == NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "cannot check peer -- no security connector")); - tsi_peer_destruct(&peer); - } else { - sc->vtable->check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); - } -} - -bool grpc_channel_security_connector_check_call_host( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - const char *host, grpc_auth_context *auth_context, - grpc_closure *on_call_host_checked, grpc_error **error) { - if (sc == NULL || sc->check_call_host == NULL) { - *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "cannot check call host -- no security connector"); - return true; - } - return sc->check_call_host(exec_ctx, sc, host, auth_context, - on_call_host_checked, error); -} - -void grpc_channel_security_connector_cancel_check_call_host( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - grpc_closure *on_call_host_checked, grpc_error *error) { - if (sc == NULL || sc->cancel_check_call_host == NULL) { - GRPC_ERROR_UNREF(error); - return; - } - sc->cancel_check_call_host(exec_ctx, sc, on_call_host_checked, error); -} - -#ifndef NDEBUG -grpc_security_connector *grpc_security_connector_ref( - grpc_security_connector *sc, const char *file, int line, - const char *reason) { - if (sc == NULL) return NULL; - if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "SECURITY_CONNECTOR:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", sc, - val, val + 1, reason); - } -#else -grpc_security_connector *grpc_security_connector_ref( - grpc_security_connector *sc) { - if (sc == NULL) return NULL; -#endif - gpr_ref(&sc->refcount); - return sc; -} - -#ifndef NDEBUG -void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, - const char *file, int line, - const char *reason) { - if (sc == NULL) return; - if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "SECURITY_CONNECTOR:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", sc, - val, val - 1, reason); - } -#else -void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc) { - if (sc == NULL) return; -#endif - if (gpr_unref(&sc->refcount)) sc->vtable->destroy(exec_ctx, sc); -} - -static void connector_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { - GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, (grpc_security_connector *)p, - "connector_pointer_arg_destroy"); -} - -static void *connector_pointer_arg_copy(void *p) { - return GRPC_SECURITY_CONNECTOR_REF((grpc_security_connector *)p, - "connector_pointer_arg_copy"); -} - -static int connector_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); } - -static const grpc_arg_pointer_vtable connector_pointer_vtable = { - connector_pointer_arg_copy, connector_pointer_arg_destroy, - connector_pointer_cmp}; - -grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) { - return grpc_channel_arg_pointer_create((char *)GRPC_ARG_SECURITY_CONNECTOR, - sc, &connector_pointer_vtable); -} - -grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg) { - if (strcmp(arg->key, GRPC_ARG_SECURITY_CONNECTOR)) return NULL; - if (arg->type != GRPC_ARG_POINTER) { - gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, - GRPC_ARG_SECURITY_CONNECTOR); - return NULL; - } - return (grpc_security_connector *)arg->value.pointer.p; -} - -grpc_security_connector *grpc_security_connector_find_in_args( - const grpc_channel_args *args) { - size_t i; - if (args == NULL) return NULL; - for (i = 0; i < args->num_args; i++) { - grpc_security_connector *sc = - grpc_security_connector_from_arg(&args->args[i]); - if (sc != NULL) return sc; - } - return NULL; -} - -/* -- Fake implementation. -- */ - -typedef struct { - grpc_channel_security_connector base; - char *target; - char *expected_targets; - bool is_lb_channel; -} grpc_fake_channel_security_connector; - -static void fake_channel_destroy(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc) { - grpc_fake_channel_security_connector *c = - (grpc_fake_channel_security_connector *)sc; - grpc_call_credentials_unref(exec_ctx, c->base.request_metadata_creds); - gpr_free(c->target); - gpr_free(c->expected_targets); - gpr_free(c); -} - -static void fake_server_destroy(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc) { - gpr_free(sc); -} - -static bool fake_check_target(const char *target_type, const char *target, - const char *set_str) { - GPR_ASSERT(target_type != NULL); - GPR_ASSERT(target != NULL); - char **set = NULL; - size_t set_size = 0; - gpr_string_split(set_str, ",", &set, &set_size); - bool found = false; - for (size_t i = 0; i < set_size; ++i) { - if (set[i] != NULL && strcmp(target, set[i]) == 0) found = true; - } - for (size_t i = 0; i < set_size; ++i) { - gpr_free(set[i]); - } - gpr_free(set); - return found; -} - -static void fake_secure_name_check(const char *target, - const char *expected_targets, - bool is_lb_channel) { - if (expected_targets == NULL) return; - char **lbs_and_backends = NULL; - size_t lbs_and_backends_size = 0; - bool success = false; - gpr_string_split(expected_targets, ";", &lbs_and_backends, - &lbs_and_backends_size); - if (lbs_and_backends_size > 2 || lbs_and_backends_size == 0) { - gpr_log(GPR_ERROR, "Invalid expected targets arg value: '%s'", - expected_targets); - goto done; - } - if (is_lb_channel) { - if (lbs_and_backends_size != 2) { - gpr_log(GPR_ERROR, - "Invalid expected targets arg value: '%s'. Expectations for LB " - "channels must be of the form 'be1,be2,be3,...;lb1,lb2,...", - expected_targets); - goto done; - } - if (!fake_check_target("LB", target, lbs_and_backends[1])) { - gpr_log(GPR_ERROR, "LB target '%s' not found in expected set '%s'", - target, lbs_and_backends[1]); - goto done; - } - success = true; - } else { - if (!fake_check_target("Backend", target, lbs_and_backends[0])) { - gpr_log(GPR_ERROR, "Backend target '%s' not found in expected set '%s'", - target, lbs_and_backends[0]); - goto done; - } - success = true; - } -done: - for (size_t i = 0; i < lbs_and_backends_size; ++i) { - gpr_free(lbs_and_backends[i]); - } - gpr_free(lbs_and_backends); - if (!success) abort(); -} - -static void fake_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - const char *prop_name; - grpc_error *error = GRPC_ERROR_NONE; - *auth_context = NULL; - if (peer.property_count != 1) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Fake peers should only have 1 property."); - goto end; - } - prop_name = peer.properties[0].name; - if (prop_name == NULL || - strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) { - char *msg; - gpr_asprintf(&msg, "Unexpected property in fake peer: %s.", - prop_name == NULL ? "" : prop_name); - error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - goto end; - } - if (strncmp(peer.properties[0].value.data, TSI_FAKE_CERTIFICATE_TYPE, - peer.properties[0].value.length)) { - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid value for cert type property."); - goto end; - } - *auth_context = grpc_auth_context_create(NULL); - grpc_auth_context_add_cstring_property( - *auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, - GRPC_FAKE_TRANSPORT_SECURITY_TYPE); -end: - GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); - tsi_peer_destruct(&peer); -} - -static void fake_channel_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - fake_check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); - grpc_fake_channel_security_connector *c = - (grpc_fake_channel_security_connector *)sc; - fake_secure_name_check(c->target, c->expected_targets, c->is_lb_channel); -} - -static void fake_server_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - fake_check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); -} - -static bool fake_channel_check_call_host(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, - const char *host, - grpc_auth_context *auth_context, - grpc_closure *on_call_host_checked, - grpc_error **error) { - return true; -} - -static void fake_channel_cancel_check_call_host( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - grpc_closure *on_call_host_checked, grpc_error *error) { - GRPC_ERROR_UNREF(error); -} - -static void fake_channel_add_handshakers( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - grpc_handshake_manager *handshake_mgr) { - grpc_handshake_manager_add( - handshake_mgr, - grpc_security_handshaker_create( - exec_ctx, tsi_create_fake_handshaker(true /* is_client */), - &sc->base)); -} - -static void fake_server_add_handshakers(grpc_exec_ctx *exec_ctx, - grpc_server_security_connector *sc, - grpc_handshake_manager *handshake_mgr) { - grpc_handshake_manager_add( - handshake_mgr, - grpc_security_handshaker_create( - exec_ctx, tsi_create_fake_handshaker(false /* is_client */), - &sc->base)); -} - -static grpc_security_connector_vtable fake_channel_vtable = { - fake_channel_destroy, fake_channel_check_peer}; - -static grpc_security_connector_vtable fake_server_vtable = { - fake_server_destroy, fake_server_check_peer}; - -grpc_channel_security_connector *grpc_fake_channel_security_connector_create( - grpc_call_credentials *request_metadata_creds, const char *target, - const grpc_channel_args *args) { - grpc_fake_channel_security_connector *c = - (grpc_fake_channel_security_connector *)gpr_zalloc(sizeof(*c)); - gpr_ref_init(&c->base.base.refcount, 1); - c->base.base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; - c->base.base.vtable = &fake_channel_vtable; - c->base.request_metadata_creds = - grpc_call_credentials_ref(request_metadata_creds); - c->base.check_call_host = fake_channel_check_call_host; - c->base.cancel_check_call_host = fake_channel_cancel_check_call_host; - c->base.add_handshakers = fake_channel_add_handshakers; - c->target = gpr_strdup(target); - const char *expected_targets = grpc_fake_transport_get_expected_targets(args); - c->expected_targets = gpr_strdup(expected_targets); - c->is_lb_channel = (grpc_lb_targets_info_find_in_args(args) != NULL); - return &c->base; -} - -grpc_server_security_connector *grpc_fake_server_security_connector_create( - void) { - grpc_server_security_connector *c = - (grpc_server_security_connector *)gpr_zalloc( - sizeof(grpc_server_security_connector)); - gpr_ref_init(&c->base.refcount, 1); - c->base.vtable = &fake_server_vtable; - c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; - c->add_handshakers = fake_server_add_handshakers; - return c; -} - -/* --- Ssl implementation. --- */ - -typedef struct { - grpc_channel_security_connector base; - tsi_ssl_client_handshaker_factory *client_handshaker_factory; - char *target_name; - char *overridden_target_name; -} grpc_ssl_channel_security_connector; - -typedef struct { - grpc_server_security_connector base; - tsi_ssl_server_handshaker_factory *server_handshaker_factory; -} grpc_ssl_server_security_connector; - -static void ssl_channel_destroy(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - grpc_call_credentials_unref(exec_ctx, c->base.request_metadata_creds); - tsi_ssl_client_handshaker_factory_unref(c->client_handshaker_factory); - c->client_handshaker_factory = NULL; - if (c->target_name != NULL) gpr_free(c->target_name); - if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name); - gpr_free(sc); -} - -static void ssl_server_destroy(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc) { - grpc_ssl_server_security_connector *c = - (grpc_ssl_server_security_connector *)sc; - tsi_ssl_server_handshaker_factory_unref(c->server_handshaker_factory); - c->server_handshaker_factory = NULL; - gpr_free(sc); -} - -static void ssl_channel_add_handshakers(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, - grpc_handshake_manager *handshake_mgr) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - // Instantiate TSI handshaker. - tsi_handshaker *tsi_hs = NULL; - tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker( - c->client_handshaker_factory, - c->overridden_target_name != NULL ? c->overridden_target_name - : c->target_name, - &tsi_hs); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", - tsi_result_to_string(result)); - return; - } - - // Create handshakers. - 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, - grpc_server_security_connector *sc, - grpc_handshake_manager *handshake_mgr) { - grpc_ssl_server_security_connector *c = - (grpc_ssl_server_security_connector *)sc; - // Instantiate TSI handshaker. - tsi_handshaker *tsi_hs = NULL; - tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker( - c->server_handshaker_factory, &tsi_hs); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", - tsi_result_to_string(result)); - return; - } - - // Create handshakers. - 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) { - char *allocated_name = NULL; - int r; - - if (strchr(peer_name, ':') != NULL) { - char *ignored_port; - gpr_split_host_port(peer_name, &allocated_name, &ignored_port); - gpr_free(ignored_port); - peer_name = allocated_name; - if (!peer_name) return 0; - } - r = tsi_ssl_peer_matches_name(peer, peer_name); - gpr_free(allocated_name); - return r; -} - -grpc_auth_context *tsi_ssl_peer_to_auth_context(const tsi_peer *peer) { - size_t i; - grpc_auth_context *ctx = NULL; - const char *peer_identity_property_name = NULL; - - /* The caller has checked the certificate type property. */ - GPR_ASSERT(peer->property_count >= 1); - ctx = grpc_auth_context_create(NULL); - grpc_auth_context_add_cstring_property( - ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, - GRPC_SSL_TRANSPORT_SECURITY_TYPE); - for (i = 0; i < peer->property_count; i++) { - const tsi_peer_property *prop = &peer->properties[i]; - if (prop->name == NULL) continue; - if (strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { - /* If there is no subject alt name, have the CN as the identity. */ - if (peer_identity_property_name == NULL) { - peer_identity_property_name = GRPC_X509_CN_PROPERTY_NAME; - } - grpc_auth_context_add_property(ctx, GRPC_X509_CN_PROPERTY_NAME, - prop->value.data, prop->value.length); - } else if (strcmp(prop->name, - TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { - peer_identity_property_name = GRPC_X509_SAN_PROPERTY_NAME; - grpc_auth_context_add_property(ctx, GRPC_X509_SAN_PROPERTY_NAME, - prop->value.data, prop->value.length); - } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) { - grpc_auth_context_add_property(ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME, - prop->value.data, prop->value.length); - } - } - if (peer_identity_property_name != NULL) { - GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( - ctx, peer_identity_property_name) == 1); - } - return ctx; -} - -static grpc_error *ssl_check_peer(grpc_security_connector *sc, - const char *peer_name, const tsi_peer *peer, - grpc_auth_context **auth_context) { - /* Check the ALPN. */ - const tsi_peer_property *p = - tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); - if (p == NULL) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Cannot check peer: missing selected ALPN property."); - } - if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Cannot check peer: invalid ALPN value."); - } - - /* Check the peer name if specified. */ - if (peer_name != NULL && !ssl_host_matches_name(peer, peer_name)) { - char *msg; - gpr_asprintf(&msg, "Peer name %s is not in peer certificate", peer_name); - grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); - gpr_free(msg); - return error; - } - *auth_context = tsi_ssl_peer_to_auth_context(peer); - return GRPC_ERROR_NONE; -} - -static void ssl_channel_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - grpc_error *error = ssl_check_peer(sc, c->overridden_target_name != NULL - ? c->overridden_target_name - : c->target_name, - &peer, auth_context); - GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); - tsi_peer_destruct(&peer); -} - -static void ssl_server_check_peer(grpc_exec_ctx *exec_ctx, - grpc_security_connector *sc, tsi_peer peer, - grpc_auth_context **auth_context, - grpc_closure *on_peer_checked) { - grpc_error *error = ssl_check_peer(sc, NULL, &peer, auth_context); - tsi_peer_destruct(&peer); - GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); -} - -static void add_shallow_auth_property_to_peer(tsi_peer *peer, - const grpc_auth_property *prop, - const char *tsi_prop_name) { - tsi_peer_property *tsi_prop = &peer->properties[peer->property_count++]; - tsi_prop->name = (char *)tsi_prop_name; - tsi_prop->value.data = prop->value; - tsi_prop->value.length = prop->value_length; -} - -tsi_peer tsi_shallow_peer_from_ssl_auth_context( - const grpc_auth_context *auth_context) { - size_t max_num_props = 0; - grpc_auth_property_iterator it; - const grpc_auth_property *prop; - tsi_peer peer; - memset(&peer, 0, sizeof(peer)); - - it = grpc_auth_context_property_iterator(auth_context); - while (grpc_auth_property_iterator_next(&it) != NULL) max_num_props++; - - if (max_num_props > 0) { - peer.properties = (tsi_peer_property *)gpr_malloc( - max_num_props * sizeof(tsi_peer_property)); - it = grpc_auth_context_property_iterator(auth_context); - while ((prop = grpc_auth_property_iterator_next(&it)) != NULL) { - if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) { - add_shallow_auth_property_to_peer( - &peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY); - } else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) { - add_shallow_auth_property_to_peer( - &peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); - } else if (strcmp(prop->name, GRPC_X509_PEM_CERT_PROPERTY_NAME) == 0) { - add_shallow_auth_property_to_peer(&peer, prop, - TSI_X509_PEM_CERT_PROPERTY); - } - } - } - return peer; -} - -void tsi_shallow_peer_destruct(tsi_peer *peer) { - if (peer->properties != NULL) gpr_free(peer->properties); -} - -static bool ssl_channel_check_call_host(grpc_exec_ctx *exec_ctx, - grpc_channel_security_connector *sc, - const char *host, - grpc_auth_context *auth_context, - grpc_closure *on_call_host_checked, - grpc_error **error) { - grpc_ssl_channel_security_connector *c = - (grpc_ssl_channel_security_connector *)sc; - grpc_security_status status = GRPC_SECURITY_ERROR; - tsi_peer peer = tsi_shallow_peer_from_ssl_auth_context(auth_context); - if (ssl_host_matches_name(&peer, host)) status = GRPC_SECURITY_OK; - /* If the target name was overridden, then the original target_name was - 'checked' transitively during the previous peer check at the end of the - handshake. */ - if (c->overridden_target_name != NULL && strcmp(host, c->target_name) == 0) { - status = GRPC_SECURITY_OK; - } - if (status != GRPC_SECURITY_OK) { - *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "call host does not match SSL server name"); - } - tsi_shallow_peer_destruct(&peer); - return true; -} - -static void ssl_channel_cancel_check_call_host( - grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, - grpc_closure *on_call_host_checked, grpc_error *error) { - GRPC_ERROR_UNREF(error); -} - -static grpc_security_connector_vtable ssl_channel_vtable = { - ssl_channel_destroy, ssl_channel_check_peer}; - -static grpc_security_connector_vtable ssl_server_vtable = { - ssl_server_destroy, ssl_server_check_peer}; - -/* returns a NULL terminated slice. */ -static grpc_slice compute_default_pem_root_certs_once(void) { - grpc_slice result = grpc_empty_slice(); - - /* First try to load the roots from the environment. */ - char *default_root_certs_path = - gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR); - if (default_root_certs_path != NULL) { - GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(default_root_certs_path, 1, &result)); - gpr_free(default_root_certs_path); - } - - /* Try overridden roots if needed. */ - grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL; - if (GRPC_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != NULL) { - char *pem_root_certs = NULL; - ovrd_res = ssl_roots_override_cb(&pem_root_certs); - if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) { - GPR_ASSERT(pem_root_certs != NULL); - result = grpc_slice_from_copied_buffer( - pem_root_certs, - strlen(pem_root_certs) + 1); // NULL terminator. - } - gpr_free(pem_root_certs); - } - - /* Fall back to installed certs if needed. */ - if (GRPC_SLICE_IS_EMPTY(result) && - ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) { - GRPC_LOG_IF_ERROR("load_file", - grpc_load_file(installed_roots_path, 1, &result)); - } - return result; -} - -static grpc_slice default_pem_root_certs; - -static void init_default_pem_root_certs(void) { - default_pem_root_certs = compute_default_pem_root_certs_once(); -} - -grpc_slice grpc_get_default_ssl_roots_for_testing(void) { - return compute_default_pem_root_certs_once(); -} - -static tsi_client_certificate_request_type -get_tsi_client_certificate_request_type( - grpc_ssl_client_certificate_request_type grpc_request_type) { - switch (grpc_request_type) { - case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE: - return TSI_DONT_REQUEST_CLIENT_CERTIFICATE; - - case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: - return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; - - case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: - return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY; - - case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: - return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; - - case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: - return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY; - - default: - // Is this a sane default - return TSI_DONT_REQUEST_CLIENT_CERTIFICATE; - } -} - -const char *grpc_get_default_ssl_roots(void) { - /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in - loading all the roots once for the lifetime of the process. */ - static gpr_once once = GPR_ONCE_INIT; - gpr_once_init(&once, init_default_pem_root_certs); - return GRPC_SLICE_IS_EMPTY(default_pem_root_certs) - ? NULL - : (const char *)GRPC_SLICE_START_PTR(default_pem_root_certs); -} - -grpc_security_status grpc_ssl_channel_security_connector_create( - grpc_exec_ctx *exec_ctx, grpc_call_credentials *request_metadata_creds, - const grpc_ssl_config *config, const char *target_name, - const char *overridden_target_name, grpc_channel_security_connector **sc) { - size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); - const char **alpn_protocol_strings = - (const char **)gpr_malloc(sizeof(const char *) * num_alpn_protocols); - tsi_result result = TSI_OK; - grpc_ssl_channel_security_connector *c; - size_t i; - const char *pem_root_certs; - char *port; - bool has_key_cert_pair; - for (i = 0; i < num_alpn_protocols; i++) { - alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i); - } - - if (config == NULL || target_name == NULL) { - gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); - goto error; - } - if (config->pem_root_certs == NULL) { - pem_root_certs = grpc_get_default_ssl_roots(); - if (pem_root_certs == NULL) { - gpr_log(GPR_ERROR, "Could not get default pem root certs."); - goto error; - } - } else { - pem_root_certs = config->pem_root_certs; - } - - c = (grpc_ssl_channel_security_connector *)gpr_zalloc( - sizeof(grpc_ssl_channel_security_connector)); - - gpr_ref_init(&c->base.base.refcount, 1); - c->base.base.vtable = &ssl_channel_vtable; - c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; - c->base.request_metadata_creds = - grpc_call_credentials_ref(request_metadata_creds); - c->base.check_call_host = ssl_channel_check_call_host; - c->base.cancel_check_call_host = ssl_channel_cancel_check_call_host; - c->base.add_handshakers = ssl_channel_add_handshakers; - gpr_split_host_port(target_name, &c->target_name, &port); - gpr_free(port); - if (overridden_target_name != NULL) { - c->overridden_target_name = gpr_strdup(overridden_target_name); - } - - has_key_cert_pair = config->pem_key_cert_pair.private_key != NULL && - config->pem_key_cert_pair.cert_chain != NULL; - result = tsi_create_ssl_client_handshaker_factory( - has_key_cert_pair ? &config->pem_key_cert_pair : NULL, pem_root_certs, - ssl_cipher_suites(), alpn_protocol_strings, (uint16_t)num_alpn_protocols, - &c->client_handshaker_factory); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", - tsi_result_to_string(result)); - ssl_channel_destroy(exec_ctx, &c->base.base); - *sc = NULL; - goto error; - } - *sc = &c->base; - gpr_free((void *)alpn_protocol_strings); - return GRPC_SECURITY_OK; - -error: - gpr_free((void *)alpn_protocol_strings); - return GRPC_SECURITY_ERROR; -} - -grpc_security_status grpc_ssl_server_security_connector_create( - grpc_exec_ctx *exec_ctx, const grpc_ssl_server_config *config, - grpc_server_security_connector **sc) { - size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); - const char **alpn_protocol_strings = - (const char **)gpr_malloc(sizeof(const char *) * num_alpn_protocols); - tsi_result result = TSI_OK; - grpc_ssl_server_security_connector *c; - size_t i; - - for (i = 0; i < num_alpn_protocols; i++) { - alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i); - } - - if (config == NULL || config->num_key_cert_pairs == 0) { - gpr_log(GPR_ERROR, "An SSL server needs a key and a cert."); - goto error; - } - c = (grpc_ssl_server_security_connector *)gpr_zalloc( - sizeof(grpc_ssl_server_security_connector)); - - gpr_ref_init(&c->base.base.refcount, 1); - c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; - c->base.base.vtable = &ssl_server_vtable; - result = tsi_create_ssl_server_handshaker_factory_ex( - config->pem_key_cert_pairs, config->num_key_cert_pairs, - config->pem_root_certs, get_tsi_client_certificate_request_type( - config->client_certificate_request), - ssl_cipher_suites(), alpn_protocol_strings, (uint16_t)num_alpn_protocols, - &c->server_handshaker_factory); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", - tsi_result_to_string(result)); - ssl_server_destroy(exec_ctx, &c->base.base); - *sc = NULL; - goto error; - } - c->base.add_handshakers = ssl_server_add_handshakers; - *sc = &c->base; - gpr_free((void *)alpn_protocol_strings); - return GRPC_SECURITY_OK; - -error: - gpr_free((void *)alpn_protocol_strings); - return GRPC_SECURITY_ERROR; -} diff --git a/src/core/lib/security/transport/security_connector.cc b/src/core/lib/security/transport/security_connector.cc new file mode 100644 index 0000000000..51844fb91f --- /dev/null +++ b/src/core/lib/security/transport/security_connector.cc @@ -0,0 +1,921 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/transport/security_connector.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/ext/transport/chttp2/alpn/alpn.h" +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/iomgr/load_file.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/fake/fake_credentials.h" +#include "src/core/lib/security/transport/lb_targets_info.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/support/env.h" +#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" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_security_connector_refcount = + GRPC_TRACER_INITIALIZER(false, "security_connector_refcount"); +#endif + +/* -- Constants. -- */ + +#ifndef INSTALL_PREFIX +static const char *installed_roots_path = "/usr/share/grpc/roots.pem"; +#else +static const char *installed_roots_path = + INSTALL_PREFIX "/share/grpc/roots.pem"; +#endif + +/* -- Overridden default roots. -- */ + +static grpc_ssl_roots_override_callback ssl_roots_override_cb = NULL; + +void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) { + ssl_roots_override_cb = cb; +} + +/* -- Cipher suites. -- */ + +/* Defines the cipher suites that we accept by default. All these cipher suites + are compliant with HTTP2. */ +#define GRPC_SSL_CIPHER_SUITES \ + "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384" + +static gpr_once cipher_suites_once = GPR_ONCE_INIT; +static const char *cipher_suites = NULL; + +static void init_cipher_suites(void) { + char *overridden = gpr_getenv("GRPC_SSL_CIPHER_SUITES"); + cipher_suites = overridden != NULL ? overridden : GRPC_SSL_CIPHER_SUITES; +} + +static const char *ssl_cipher_suites(void) { + gpr_once_init(&cipher_suites_once, init_cipher_suites); + return cipher_suites; +} + +/* -- Common methods. -- */ + +/* Returns the first property with that name. */ +const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, + const char *name) { + size_t i; + if (peer == NULL) return NULL; + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property *property = &peer->properties[i]; + if (name == NULL && property->name == NULL) { + return property; + } + if (name != NULL && property->name != NULL && + strcmp(property->name, name) == 0) { + return property; + } + } + return NULL; +} + +void grpc_channel_security_connector_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *connector, + grpc_handshake_manager *handshake_mgr) { + if (connector != NULL) { + connector->add_handshakers(exec_ctx, connector, handshake_mgr); + } +} + +void grpc_server_security_connector_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_server_security_connector *connector, + grpc_handshake_manager *handshake_mgr) { + if (connector != NULL) { + connector->add_handshakers(exec_ctx, connector, handshake_mgr); + } +} + +void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + if (sc == NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "cannot check peer -- no security connector")); + tsi_peer_destruct(&peer); + } else { + sc->vtable->check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); + } +} + +bool grpc_channel_security_connector_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + const char *host, grpc_auth_context *auth_context, + grpc_closure *on_call_host_checked, grpc_error **error) { + if (sc == NULL || sc->check_call_host == NULL) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "cannot check call host -- no security connector"); + return true; + } + return sc->check_call_host(exec_ctx, sc, host, auth_context, + on_call_host_checked, error); +} + +void grpc_channel_security_connector_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error) { + if (sc == NULL || sc->cancel_check_call_host == NULL) { + GRPC_ERROR_UNREF(error); + return; + } + sc->cancel_check_call_host(exec_ctx, sc, on_call_host_checked, error); +} + +#ifndef NDEBUG +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *sc, const char *file, int line, + const char *reason) { + if (sc == NULL) return NULL; + if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECURITY_CONNECTOR:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", sc, + val, val + 1, reason); + } +#else +grpc_security_connector *grpc_security_connector_ref( + grpc_security_connector *sc) { + if (sc == NULL) return NULL; +#endif + gpr_ref(&sc->refcount); + return sc; +} + +#ifndef NDEBUG +void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, + const char *file, int line, + const char *reason) { + if (sc == NULL) return; + if (GRPC_TRACER_ON(grpc_trace_security_connector_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&sc->refcount.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "SECURITY_CONNECTOR:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", sc, + val, val - 1, reason); + } +#else +void grpc_security_connector_unref(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc) { + if (sc == NULL) return; +#endif + if (gpr_unref(&sc->refcount)) sc->vtable->destroy(exec_ctx, sc); +} + +static void connector_pointer_arg_destroy(grpc_exec_ctx *exec_ctx, void *p) { + GRPC_SECURITY_CONNECTOR_UNREF(exec_ctx, (grpc_security_connector *)p, + "connector_pointer_arg_destroy"); +} + +static void *connector_pointer_arg_copy(void *p) { + return GRPC_SECURITY_CONNECTOR_REF((grpc_security_connector *)p, + "connector_pointer_arg_copy"); +} + +static int connector_pointer_cmp(void *a, void *b) { return GPR_ICMP(a, b); } + +static const grpc_arg_pointer_vtable connector_pointer_vtable = { + connector_pointer_arg_copy, connector_pointer_arg_destroy, + connector_pointer_cmp}; + +grpc_arg grpc_security_connector_to_arg(grpc_security_connector *sc) { + return grpc_channel_arg_pointer_create((char *)GRPC_ARG_SECURITY_CONNECTOR, + sc, &connector_pointer_vtable); +} + +grpc_security_connector *grpc_security_connector_from_arg(const grpc_arg *arg) { + if (strcmp(arg->key, GRPC_ARG_SECURITY_CONNECTOR)) return NULL; + if (arg->type != GRPC_ARG_POINTER) { + gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, + GRPC_ARG_SECURITY_CONNECTOR); + return NULL; + } + return (grpc_security_connector *)arg->value.pointer.p; +} + +grpc_security_connector *grpc_security_connector_find_in_args( + const grpc_channel_args *args) { + size_t i; + if (args == NULL) return NULL; + for (i = 0; i < args->num_args; i++) { + grpc_security_connector *sc = + grpc_security_connector_from_arg(&args->args[i]); + if (sc != NULL) return sc; + } + return NULL; +} + +/* -- Fake implementation. -- */ + +typedef struct { + grpc_channel_security_connector base; + char *target; + char *expected_targets; + bool is_lb_channel; +} grpc_fake_channel_security_connector; + +static void fake_channel_destroy(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc) { + grpc_fake_channel_security_connector *c = + (grpc_fake_channel_security_connector *)sc; + grpc_call_credentials_unref(exec_ctx, c->base.request_metadata_creds); + gpr_free(c->target); + gpr_free(c->expected_targets); + gpr_free(c); +} + +static void fake_server_destroy(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc) { + gpr_free(sc); +} + +static bool fake_check_target(const char *target_type, const char *target, + const char *set_str) { + GPR_ASSERT(target_type != NULL); + GPR_ASSERT(target != NULL); + char **set = NULL; + size_t set_size = 0; + gpr_string_split(set_str, ",", &set, &set_size); + bool found = false; + for (size_t i = 0; i < set_size; ++i) { + if (set[i] != NULL && strcmp(target, set[i]) == 0) found = true; + } + for (size_t i = 0; i < set_size; ++i) { + gpr_free(set[i]); + } + gpr_free(set); + return found; +} + +static void fake_secure_name_check(const char *target, + const char *expected_targets, + bool is_lb_channel) { + if (expected_targets == NULL) return; + char **lbs_and_backends = NULL; + size_t lbs_and_backends_size = 0; + bool success = false; + gpr_string_split(expected_targets, ";", &lbs_and_backends, + &lbs_and_backends_size); + if (lbs_and_backends_size > 2 || lbs_and_backends_size == 0) { + gpr_log(GPR_ERROR, "Invalid expected targets arg value: '%s'", + expected_targets); + goto done; + } + if (is_lb_channel) { + if (lbs_and_backends_size != 2) { + gpr_log(GPR_ERROR, + "Invalid expected targets arg value: '%s'. Expectations for LB " + "channels must be of the form 'be1,be2,be3,...;lb1,lb2,...", + expected_targets); + goto done; + } + if (!fake_check_target("LB", target, lbs_and_backends[1])) { + gpr_log(GPR_ERROR, "LB target '%s' not found in expected set '%s'", + target, lbs_and_backends[1]); + goto done; + } + success = true; + } else { + if (!fake_check_target("Backend", target, lbs_and_backends[0])) { + gpr_log(GPR_ERROR, "Backend target '%s' not found in expected set '%s'", + target, lbs_and_backends[0]); + goto done; + } + success = true; + } +done: + for (size_t i = 0; i < lbs_and_backends_size; ++i) { + gpr_free(lbs_and_backends[i]); + } + gpr_free(lbs_and_backends); + if (!success) abort(); +} + +static void fake_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + const char *prop_name; + grpc_error *error = GRPC_ERROR_NONE; + *auth_context = NULL; + if (peer.property_count != 1) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Fake peers should only have 1 property."); + goto end; + } + prop_name = peer.properties[0].name; + if (prop_name == NULL || + strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) { + char *msg; + gpr_asprintf(&msg, "Unexpected property in fake peer: %s.", + prop_name == NULL ? "" : prop_name); + error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + goto end; + } + if (strncmp(peer.properties[0].value.data, TSI_FAKE_CERTIFICATE_TYPE, + peer.properties[0].value.length)) { + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid value for cert type property."); + goto end; + } + *auth_context = grpc_auth_context_create(NULL); + grpc_auth_context_add_cstring_property( + *auth_context, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_FAKE_TRANSPORT_SECURITY_TYPE); +end: + GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); + tsi_peer_destruct(&peer); +} + +static void fake_channel_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + fake_check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); + grpc_fake_channel_security_connector *c = + (grpc_fake_channel_security_connector *)sc; + fake_secure_name_check(c->target, c->expected_targets, c->is_lb_channel); +} + +static void fake_server_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + fake_check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); +} + +static bool fake_channel_check_call_host(grpc_exec_ctx *exec_ctx, + grpc_channel_security_connector *sc, + const char *host, + grpc_auth_context *auth_context, + grpc_closure *on_call_host_checked, + grpc_error **error) { + return true; +} + +static void fake_channel_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static void fake_channel_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_handshake_manager *handshake_mgr) { + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + exec_ctx, tsi_create_fake_handshaker(true /* is_client */), + &sc->base)); +} + +static void fake_server_add_handshakers(grpc_exec_ctx *exec_ctx, + grpc_server_security_connector *sc, + grpc_handshake_manager *handshake_mgr) { + grpc_handshake_manager_add( + handshake_mgr, + grpc_security_handshaker_create( + exec_ctx, tsi_create_fake_handshaker(false /* is_client */), + &sc->base)); +} + +static grpc_security_connector_vtable fake_channel_vtable = { + fake_channel_destroy, fake_channel_check_peer}; + +static grpc_security_connector_vtable fake_server_vtable = { + fake_server_destroy, fake_server_check_peer}; + +grpc_channel_security_connector *grpc_fake_channel_security_connector_create( + grpc_call_credentials *request_metadata_creds, const char *target, + const grpc_channel_args *args) { + grpc_fake_channel_security_connector *c = + (grpc_fake_channel_security_connector *)gpr_zalloc(sizeof(*c)); + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; + c->base.base.vtable = &fake_channel_vtable; + c->base.request_metadata_creds = + grpc_call_credentials_ref(request_metadata_creds); + c->base.check_call_host = fake_channel_check_call_host; + c->base.cancel_check_call_host = fake_channel_cancel_check_call_host; + c->base.add_handshakers = fake_channel_add_handshakers; + c->target = gpr_strdup(target); + const char *expected_targets = grpc_fake_transport_get_expected_targets(args); + c->expected_targets = gpr_strdup(expected_targets); + c->is_lb_channel = (grpc_lb_targets_info_find_in_args(args) != NULL); + return &c->base; +} + +grpc_server_security_connector *grpc_fake_server_security_connector_create( + void) { + grpc_server_security_connector *c = + (grpc_server_security_connector *)gpr_zalloc( + sizeof(grpc_server_security_connector)); + gpr_ref_init(&c->base.refcount, 1); + c->base.vtable = &fake_server_vtable; + c->base.url_scheme = GRPC_FAKE_SECURITY_URL_SCHEME; + c->add_handshakers = fake_server_add_handshakers; + return c; +} + +/* --- Ssl implementation. --- */ + +typedef struct { + grpc_channel_security_connector base; + tsi_ssl_client_handshaker_factory *client_handshaker_factory; + char *target_name; + char *overridden_target_name; +} grpc_ssl_channel_security_connector; + +typedef struct { + grpc_server_security_connector base; + tsi_ssl_server_handshaker_factory *server_handshaker_factory; +} grpc_ssl_server_security_connector; + +static void ssl_channel_destroy(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + grpc_call_credentials_unref(exec_ctx, c->base.request_metadata_creds); + tsi_ssl_client_handshaker_factory_unref(c->client_handshaker_factory); + c->client_handshaker_factory = NULL; + if (c->target_name != NULL) gpr_free(c->target_name); + if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name); + gpr_free(sc); +} + +static void ssl_server_destroy(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc) { + grpc_ssl_server_security_connector *c = + (grpc_ssl_server_security_connector *)sc; + tsi_ssl_server_handshaker_factory_unref(c->server_handshaker_factory); + c->server_handshaker_factory = NULL; + gpr_free(sc); +} + +static void ssl_channel_add_handshakers(grpc_exec_ctx *exec_ctx, + grpc_channel_security_connector *sc, + grpc_handshake_manager *handshake_mgr) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + // Instantiate TSI handshaker. + tsi_handshaker *tsi_hs = NULL; + tsi_result result = tsi_ssl_client_handshaker_factory_create_handshaker( + c->client_handshaker_factory, + c->overridden_target_name != NULL ? c->overridden_target_name + : c->target_name, + &tsi_hs); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + return; + } + + // Create handshakers. + 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, + grpc_server_security_connector *sc, + grpc_handshake_manager *handshake_mgr) { + grpc_ssl_server_security_connector *c = + (grpc_ssl_server_security_connector *)sc; + // Instantiate TSI handshaker. + tsi_handshaker *tsi_hs = NULL; + tsi_result result = tsi_ssl_server_handshaker_factory_create_handshaker( + c->server_handshaker_factory, &tsi_hs); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", + tsi_result_to_string(result)); + return; + } + + // Create handshakers. + 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) { + char *allocated_name = NULL; + int r; + + if (strchr(peer_name, ':') != NULL) { + char *ignored_port; + gpr_split_host_port(peer_name, &allocated_name, &ignored_port); + gpr_free(ignored_port); + peer_name = allocated_name; + if (!peer_name) return 0; + } + r = tsi_ssl_peer_matches_name(peer, peer_name); + gpr_free(allocated_name); + return r; +} + +grpc_auth_context *tsi_ssl_peer_to_auth_context(const tsi_peer *peer) { + size_t i; + grpc_auth_context *ctx = NULL; + const char *peer_identity_property_name = NULL; + + /* The caller has checked the certificate type property. */ + GPR_ASSERT(peer->property_count >= 1); + ctx = grpc_auth_context_create(NULL); + grpc_auth_context_add_cstring_property( + ctx, GRPC_TRANSPORT_SECURITY_TYPE_PROPERTY_NAME, + GRPC_SSL_TRANSPORT_SECURITY_TYPE); + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property *prop = &peer->properties[i]; + if (prop->name == NULL) continue; + if (strcmp(prop->name, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { + /* If there is no subject alt name, have the CN as the identity. */ + if (peer_identity_property_name == NULL) { + peer_identity_property_name = GRPC_X509_CN_PROPERTY_NAME; + } + grpc_auth_context_add_property(ctx, GRPC_X509_CN_PROPERTY_NAME, + prop->value.data, prop->value.length); + } else if (strcmp(prop->name, + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { + peer_identity_property_name = GRPC_X509_SAN_PROPERTY_NAME; + grpc_auth_context_add_property(ctx, GRPC_X509_SAN_PROPERTY_NAME, + prop->value.data, prop->value.length); + } else if (strcmp(prop->name, TSI_X509_PEM_CERT_PROPERTY) == 0) { + grpc_auth_context_add_property(ctx, GRPC_X509_PEM_CERT_PROPERTY_NAME, + prop->value.data, prop->value.length); + } + } + if (peer_identity_property_name != NULL) { + GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name( + ctx, peer_identity_property_name) == 1); + } + return ctx; +} + +static grpc_error *ssl_check_peer(grpc_security_connector *sc, + const char *peer_name, const tsi_peer *peer, + grpc_auth_context **auth_context) { + /* Check the ALPN. */ + const tsi_peer_property *p = + tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); + if (p == NULL) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: missing selected ALPN property."); + } + if (!grpc_chttp2_is_alpn_version_supported(p->value.data, p->value.length)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Cannot check peer: invalid ALPN value."); + } + + /* Check the peer name if specified. */ + if (peer_name != NULL && !ssl_host_matches_name(peer, peer_name)) { + char *msg; + gpr_asprintf(&msg, "Peer name %s is not in peer certificate", peer_name); + grpc_error *error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg); + gpr_free(msg); + return error; + } + *auth_context = tsi_ssl_peer_to_auth_context(peer); + return GRPC_ERROR_NONE; +} + +static void ssl_channel_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + grpc_error *error = ssl_check_peer(sc, c->overridden_target_name != NULL + ? c->overridden_target_name + : c->target_name, + &peer, auth_context); + GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); + tsi_peer_destruct(&peer); +} + +static void ssl_server_check_peer(grpc_exec_ctx *exec_ctx, + grpc_security_connector *sc, tsi_peer peer, + grpc_auth_context **auth_context, + grpc_closure *on_peer_checked) { + grpc_error *error = ssl_check_peer(sc, NULL, &peer, auth_context); + tsi_peer_destruct(&peer); + GRPC_CLOSURE_SCHED(exec_ctx, on_peer_checked, error); +} + +static void add_shallow_auth_property_to_peer(tsi_peer *peer, + const grpc_auth_property *prop, + const char *tsi_prop_name) { + tsi_peer_property *tsi_prop = &peer->properties[peer->property_count++]; + tsi_prop->name = (char *)tsi_prop_name; + tsi_prop->value.data = prop->value; + tsi_prop->value.length = prop->value_length; +} + +tsi_peer tsi_shallow_peer_from_ssl_auth_context( + const grpc_auth_context *auth_context) { + size_t max_num_props = 0; + grpc_auth_property_iterator it; + const grpc_auth_property *prop; + tsi_peer peer; + memset(&peer, 0, sizeof(peer)); + + it = grpc_auth_context_property_iterator(auth_context); + while (grpc_auth_property_iterator_next(&it) != NULL) max_num_props++; + + if (max_num_props > 0) { + peer.properties = (tsi_peer_property *)gpr_malloc( + max_num_props * sizeof(tsi_peer_property)); + it = grpc_auth_context_property_iterator(auth_context); + while ((prop = grpc_auth_property_iterator_next(&it)) != NULL) { + if (strcmp(prop->name, GRPC_X509_SAN_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer( + &peer, prop, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY); + } else if (strcmp(prop->name, GRPC_X509_CN_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer( + &peer, prop, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); + } else if (strcmp(prop->name, GRPC_X509_PEM_CERT_PROPERTY_NAME) == 0) { + add_shallow_auth_property_to_peer(&peer, prop, + TSI_X509_PEM_CERT_PROPERTY); + } + } + } + return peer; +} + +void tsi_shallow_peer_destruct(tsi_peer *peer) { + if (peer->properties != NULL) gpr_free(peer->properties); +} + +static bool ssl_channel_check_call_host(grpc_exec_ctx *exec_ctx, + grpc_channel_security_connector *sc, + const char *host, + grpc_auth_context *auth_context, + grpc_closure *on_call_host_checked, + grpc_error **error) { + grpc_ssl_channel_security_connector *c = + (grpc_ssl_channel_security_connector *)sc; + grpc_security_status status = GRPC_SECURITY_ERROR; + tsi_peer peer = tsi_shallow_peer_from_ssl_auth_context(auth_context); + if (ssl_host_matches_name(&peer, host)) status = GRPC_SECURITY_OK; + /* If the target name was overridden, then the original target_name was + 'checked' transitively during the previous peer check at the end of the + handshake. */ + if (c->overridden_target_name != NULL && strcmp(host, c->target_name) == 0) { + status = GRPC_SECURITY_OK; + } + if (status != GRPC_SECURITY_OK) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "call host does not match SSL server name"); + } + tsi_shallow_peer_destruct(&peer); + return true; +} + +static void ssl_channel_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static grpc_security_connector_vtable ssl_channel_vtable = { + ssl_channel_destroy, ssl_channel_check_peer}; + +static grpc_security_connector_vtable ssl_server_vtable = { + ssl_server_destroy, ssl_server_check_peer}; + +/* returns a NULL terminated slice. */ +static grpc_slice compute_default_pem_root_certs_once(void) { + grpc_slice result = grpc_empty_slice(); + + /* First try to load the roots from the environment. */ + char *default_root_certs_path = + gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR); + if (default_root_certs_path != NULL) { + GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(default_root_certs_path, 1, &result)); + gpr_free(default_root_certs_path); + } + + /* Try overridden roots if needed. */ + grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL; + if (GRPC_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != NULL) { + char *pem_root_certs = NULL; + ovrd_res = ssl_roots_override_cb(&pem_root_certs); + if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) { + GPR_ASSERT(pem_root_certs != NULL); + result = grpc_slice_from_copied_buffer( + pem_root_certs, + strlen(pem_root_certs) + 1); // NULL terminator. + } + gpr_free(pem_root_certs); + } + + /* Fall back to installed certs if needed. */ + if (GRPC_SLICE_IS_EMPTY(result) && + ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) { + GRPC_LOG_IF_ERROR("load_file", + grpc_load_file(installed_roots_path, 1, &result)); + } + return result; +} + +static grpc_slice default_pem_root_certs; + +static void init_default_pem_root_certs(void) { + default_pem_root_certs = compute_default_pem_root_certs_once(); +} + +grpc_slice grpc_get_default_ssl_roots_for_testing(void) { + return compute_default_pem_root_certs_once(); +} + +static tsi_client_certificate_request_type +get_tsi_client_certificate_request_type( + grpc_ssl_client_certificate_request_type grpc_request_type) { + switch (grpc_request_type) { + case GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE: + return TSI_DONT_REQUEST_CLIENT_CERTIFICATE; + + case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + return TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; + + case GRPC_SSL_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: + return TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY; + + case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY; + + case GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: + return TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY; + + default: + // Is this a sane default + return TSI_DONT_REQUEST_CLIENT_CERTIFICATE; + } +} + +const char *grpc_get_default_ssl_roots(void) { + /* TODO(jboeuf@google.com): Maybe revisit the approach which consists in + loading all the roots once for the lifetime of the process. */ + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_default_pem_root_certs); + return GRPC_SLICE_IS_EMPTY(default_pem_root_certs) + ? NULL + : (const char *)GRPC_SLICE_START_PTR(default_pem_root_certs); +} + +grpc_security_status grpc_ssl_channel_security_connector_create( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *request_metadata_creds, + const grpc_ssl_config *config, const char *target_name, + const char *overridden_target_name, grpc_channel_security_connector **sc) { + size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); + const char **alpn_protocol_strings = + (const char **)gpr_malloc(sizeof(const char *) * num_alpn_protocols); + tsi_result result = TSI_OK; + grpc_ssl_channel_security_connector *c; + size_t i; + const char *pem_root_certs; + char *port; + bool has_key_cert_pair; + for (i = 0; i < num_alpn_protocols; i++) { + alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i); + } + + if (config == NULL || target_name == NULL) { + gpr_log(GPR_ERROR, "An ssl channel needs a config and a target name."); + goto error; + } + if (config->pem_root_certs == NULL) { + pem_root_certs = grpc_get_default_ssl_roots(); + if (pem_root_certs == NULL) { + gpr_log(GPR_ERROR, "Could not get default pem root certs."); + goto error; + } + } else { + pem_root_certs = config->pem_root_certs; + } + + c = (grpc_ssl_channel_security_connector *)gpr_zalloc( + sizeof(grpc_ssl_channel_security_connector)); + + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.vtable = &ssl_channel_vtable; + c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; + c->base.request_metadata_creds = + grpc_call_credentials_ref(request_metadata_creds); + c->base.check_call_host = ssl_channel_check_call_host; + c->base.cancel_check_call_host = ssl_channel_cancel_check_call_host; + c->base.add_handshakers = ssl_channel_add_handshakers; + gpr_split_host_port(target_name, &c->target_name, &port); + gpr_free(port); + if (overridden_target_name != NULL) { + c->overridden_target_name = gpr_strdup(overridden_target_name); + } + + has_key_cert_pair = config->pem_key_cert_pair.private_key != NULL && + config->pem_key_cert_pair.cert_chain != NULL; + result = tsi_create_ssl_client_handshaker_factory( + has_key_cert_pair ? &config->pem_key_cert_pair : NULL, pem_root_certs, + ssl_cipher_suites(), alpn_protocol_strings, (uint16_t)num_alpn_protocols, + &c->client_handshaker_factory); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + ssl_channel_destroy(exec_ctx, &c->base.base); + *sc = NULL; + goto error; + } + *sc = &c->base; + gpr_free((void *)alpn_protocol_strings); + return GRPC_SECURITY_OK; + +error: + gpr_free((void *)alpn_protocol_strings); + return GRPC_SECURITY_ERROR; +} + +grpc_security_status grpc_ssl_server_security_connector_create( + grpc_exec_ctx *exec_ctx, const grpc_ssl_server_config *config, + grpc_server_security_connector **sc) { + size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); + const char **alpn_protocol_strings = + (const char **)gpr_malloc(sizeof(const char *) * num_alpn_protocols); + tsi_result result = TSI_OK; + grpc_ssl_server_security_connector *c; + size_t i; + + for (i = 0; i < num_alpn_protocols; i++) { + alpn_protocol_strings[i] = grpc_chttp2_get_alpn_version_index(i); + } + + if (config == NULL || config->num_key_cert_pairs == 0) { + gpr_log(GPR_ERROR, "An SSL server needs a key and a cert."); + goto error; + } + c = (grpc_ssl_server_security_connector *)gpr_zalloc( + sizeof(grpc_ssl_server_security_connector)); + + gpr_ref_init(&c->base.base.refcount, 1); + c->base.base.url_scheme = GRPC_SSL_URL_SCHEME; + c->base.base.vtable = &ssl_server_vtable; + result = tsi_create_ssl_server_handshaker_factory_ex( + config->pem_key_cert_pairs, config->num_key_cert_pairs, + config->pem_root_certs, get_tsi_client_certificate_request_type( + config->client_certificate_request), + ssl_cipher_suites(), alpn_protocol_strings, (uint16_t)num_alpn_protocols, + &c->server_handshaker_factory); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", + tsi_result_to_string(result)); + ssl_server_destroy(exec_ctx, &c->base.base); + *sc = NULL; + goto error; + } + c->base.add_handshakers = ssl_server_add_handshakers; + *sc = &c->base; + gpr_free((void *)alpn_protocol_strings); + return GRPC_SECURITY_OK; + +error: + gpr_free((void *)alpn_protocol_strings); + return GRPC_SECURITY_ERROR; +} diff --git a/src/core/lib/security/transport/security_handshaker.c b/src/core/lib/security/transport/security_handshaker.c deleted file mode 100644 index 3d19605617..0000000000 --- a/src/core/lib/security/transport/security_handshaker.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/transport/security_handshaker.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/handshaker.h" -#include "src/core/lib/channel/handshaker_registry.h" -#include "src/core/lib/security/context/security_context.h" -#include "src/core/lib/security/transport/secure_endpoint.h" -#include "src/core/lib/security/transport/tsi_error.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/tsi/transport_security_grpc.h" - -#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 - -typedef struct { - grpc_handshaker base; - - // State set at creation time. - tsi_handshaker *handshaker; - grpc_security_connector *connector; - - gpr_mu mu; - gpr_refcount refs; - - bool shutdown; - // Endpoint and read buffer to destroy after a shutdown. - grpc_endpoint *endpoint_to_destroy; - grpc_slice_buffer *read_buffer_to_destroy; - - // State saved while performing the handshake. - grpc_handshaker_args *args; - grpc_closure *on_handshake_done; - - unsigned char *handshake_buffer; - size_t handshake_buffer_size; - 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, - security_handshaker *h) { - 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); - } - if (h->read_buffer_to_destroy != NULL) { - grpc_slice_buffer_destroy_internal(exec_ctx, h->read_buffer_to_destroy); - gpr_free(h->read_buffer_to_destroy); - } - gpr_free(h->handshake_buffer); - 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"); - gpr_free(h); - } -} - -// Set args fields to NULL, saving the endpoint and read buffer for -// later destruction. -static void cleanup_args_for_failure_locked(grpc_exec_ctx *exec_ctx, - security_handshaker *h) { - h->endpoint_to_destroy = h->args->endpoint; - h->args->endpoint = NULL; - h->read_buffer_to_destroy = h->args->read_buffer; - h->args->read_buffer = NULL; - grpc_channel_args_destroy(exec_ctx, h->args->args); - h->args->args = NULL; -} - -// If the handshake failed or we're shutting down, clean up and invoke the -// callback with the error. -static void security_handshake_failed_locked(grpc_exec_ctx *exec_ctx, - security_handshaker *h, - grpc_error *error) { - if (error == GRPC_ERROR_NONE) { - // If we were shut down after the handshake succeeded but before an - // endpoint callback was invoked, we need to generate our own error. - error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); - } - const char *msg = grpc_error_string(error); - gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg); - - if (!h->shutdown) { - // TODO(ctiller): It is currently necessary to shutdown endpoints - // before destroying them, even if we know that there are no - // pending read/write callbacks. This should be fixed, at which - // point this can be removed. - grpc_endpoint_shutdown(exec_ctx, h->args->endpoint, GRPC_ERROR_REF(error)); - // Not shutting down, so the write failed. Clean up before - // invoking the callback. - cleanup_args_for_failure_locked(exec_ctx, h); - // Set shutdown to true so that subsequent calls to - // security_handshaker_shutdown() do nothing. - h->shutdown = true; - } - // Invoke callback. - GRPC_CLOSURE_SCHED(exec_ctx, h->on_handshake_done, error); -} - -static void on_peer_checked_inner(grpc_exec_ctx *exec_ctx, - security_handshaker *h, grpc_error *error) { - if (error != GRPC_ERROR_NONE || h->shutdown) { - security_handshake_failed_locked(exec_ctx, h, GRPC_ERROR_REF(error)); - return; - } - // Create zero-copy frame protector, if implemented. - tsi_zero_copy_grpc_protector *zero_copy_protector = NULL; - tsi_result result = tsi_handshaker_result_create_zero_copy_grpc_protector( - exec_ctx, h->handshaker_result, NULL, &zero_copy_protector); - if (result != TSI_OK && result != TSI_UNIMPLEMENTED) { - error = grpc_set_tsi_error_result( - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Zero-copy frame protector creation failed"), - result); - security_handshake_failed_locked(exec_ctx, h, error); - return; - } - // Create frame protector if zero-copy frame protector is NULL. - tsi_frame_protector *protector = NULL; - if (zero_copy_protector == NULL) { - 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"), - result); - security_handshake_failed_locked(exec_ctx, h, error); - return; - } - } - // Get unused bytes. - const 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. - 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, zero_copy_protector, h->args->endpoint, &slice, 1); - grpc_slice_unref_internal(exec_ctx, slice); - } else { - h->args->endpoint = grpc_secure_endpoint_create( - protector, zero_copy_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); - grpc_channel_args *tmp_args = h->args->args; - h->args->args = - grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1); - grpc_channel_args_destroy(exec_ctx, tmp_args); - // Invoke callback. - GRPC_CLOSURE_SCHED(exec_ctx, h->on_handshake_done, GRPC_ERROR_NONE); - // Set shutdown to true so that subsequent calls to - // security_handshaker_shutdown() do nothing. - h->shutdown = true; -} - -static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - security_handshaker *h = (security_handshaker *)arg; - gpr_mu_lock(&h->mu); - on_peer_checked_inner(exec_ctx, h, error); - gpr_mu_unlock(&h->mu); - security_handshaker_unref(exec_ctx, h); -} - -static grpc_error *check_peer_locked(grpc_exec_ctx *exec_ctx, - security_handshaker *h) { - tsi_peer 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); - } - grpc_security_connector_check_peer(exec_ctx, h->connector, peer, - &h->auth_context, &h->on_peer_checked); - return GRPC_ERROR_NONE; -} - -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); - } - // 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 = (security_handshaker *)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. - const 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, - void *arg, grpc_error *error) { - security_handshaker *h = (security_handshaker *)arg; - gpr_mu_lock(&h->mu); - if (error != GRPC_ERROR_NONE || h->shutdown) { - security_handshake_failed_locked( - exec_ctx, h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Handshake read failed", &error, 1)); - gpr_mu_unlock(&h->mu); - security_handshaker_unref(exec_ctx, h); - return; - } - // Copy all slices received. - size_t i; - size_t bytes_received_size = 0; - for (i = 0; i < h->args->read_buffer->count; i++) { - bytes_received_size += GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); - } - if (bytes_received_size > h->handshake_buffer_size) { - h->handshake_buffer = - (uint8_t *)gpr_realloc(h->handshake_buffer, bytes_received_size); - h->handshake_buffer_size = bytes_received_size; - } - 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; - } - // 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); - } else { - gpr_mu_unlock(&h->mu); - } -} - -static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - security_handshaker *h = (security_handshaker *)arg; - gpr_mu_lock(&h->mu); - if (error != GRPC_ERROR_NONE || h->shutdown) { - security_handshake_failed_locked( - exec_ctx, h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Handshake write failed", &error, 1)); - gpr_mu_unlock(&h->mu); - security_handshaker_unref(exec_ctx, h); - return; - } - // 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 { - error = check_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; - } - } - gpr_mu_unlock(&h->mu); -} - -// -// public handshaker API -// - -static void security_handshaker_destroy(grpc_exec_ctx *exec_ctx, - grpc_handshaker *handshaker) { - security_handshaker *h = (security_handshaker *)handshaker; - security_handshaker_unref(exec_ctx, h); -} - -static void security_handshaker_shutdown(grpc_exec_ctx *exec_ctx, - grpc_handshaker *handshaker, - grpc_error *why) { - security_handshaker *h = (security_handshaker *)handshaker; - gpr_mu_lock(&h->mu); - if (!h->shutdown) { - h->shutdown = true; - grpc_endpoint_shutdown(exec_ctx, h->args->endpoint, GRPC_ERROR_REF(why)); - cleanup_args_for_failure_locked(exec_ctx, h); - } - gpr_mu_unlock(&h->mu); - GRPC_ERROR_UNREF(why); -} - -static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_handshaker *handshaker, - grpc_tcp_server_acceptor *acceptor, - grpc_closure *on_handshake_done, - grpc_handshaker_args *args) { - security_handshaker *h = (security_handshaker *)handshaker; - gpr_mu_lock(&h->mu); - h->args = args; - h->on_handshake_done = on_handshake_done; - gpr_ref(&h->refs); - 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); - security_handshaker_unref(exec_ctx, h); - return; - } - gpr_mu_unlock(&h->mu); -} - -static const grpc_handshaker_vtable security_handshaker_vtable = { - security_handshaker_destroy, security_handshaker_shutdown, - security_handshaker_do_handshake}; - -static grpc_handshaker *security_handshaker_create( - grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, - grpc_security_connector *connector) { - security_handshaker *h = - (security_handshaker *)gpr_zalloc(sizeof(security_handshaker)); - grpc_handshaker_init(&security_handshaker_vtable, &h->base); - h->handshaker = handshaker; - h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake"); - gpr_mu_init(&h->mu); - gpr_ref_init(&h->refs, 1); - h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; - h->handshake_buffer = (uint8_t *)gpr_malloc(h->handshake_buffer_size); - GRPC_CLOSURE_INIT(&h->on_handshake_data_sent_to_peer, - on_handshake_data_sent_to_peer, h, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_INIT(&h->on_handshake_data_received_from_peer, - on_handshake_data_received_from_peer, h, - 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->outgoing); - return &h->base; -} - -// -// fail_handshaker -// - -static void fail_handshaker_destroy(grpc_exec_ctx *exec_ctx, - grpc_handshaker *handshaker) { - gpr_free(handshaker); -} - -static void fail_handshaker_shutdown(grpc_exec_ctx *exec_ctx, - grpc_handshaker *handshaker, - grpc_error *why) { - GRPC_ERROR_UNREF(why); -} - -static void fail_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, - grpc_handshaker *handshaker, - grpc_tcp_server_acceptor *acceptor, - grpc_closure *on_handshake_done, - grpc_handshaker_args *args) { - GRPC_CLOSURE_SCHED(exec_ctx, on_handshake_done, - GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Failed to create security handshaker")); -} - -static const grpc_handshaker_vtable fail_handshaker_vtable = { - fail_handshaker_destroy, fail_handshaker_shutdown, - fail_handshaker_do_handshake}; - -static grpc_handshaker *fail_handshaker_create() { - grpc_handshaker *h = (grpc_handshaker *)gpr_malloc(sizeof(*h)); - grpc_handshaker_init(&fail_handshaker_vtable, h); - return h; -} - -// -// handshaker factories -// - -static void client_handshaker_factory_add_handshakers( - grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory, - const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { - grpc_channel_security_connector *security_connector = - (grpc_channel_security_connector *)grpc_security_connector_find_in_args( - args); - grpc_channel_security_connector_add_handshakers(exec_ctx, security_connector, - handshake_mgr); -} - -static void server_handshaker_factory_add_handshakers( - grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *hf, - const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { - grpc_server_security_connector *security_connector = - (grpc_server_security_connector *)grpc_security_connector_find_in_args( - args); - grpc_server_security_connector_add_handshakers(exec_ctx, security_connector, - handshake_mgr); -} - -static void handshaker_factory_destroy( - grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) {} - -static const grpc_handshaker_factory_vtable client_handshaker_factory_vtable = { - client_handshaker_factory_add_handshakers, handshaker_factory_destroy}; - -static grpc_handshaker_factory client_handshaker_factory = { - &client_handshaker_factory_vtable}; - -static const grpc_handshaker_factory_vtable server_handshaker_factory_vtable = { - server_handshaker_factory_add_handshakers, handshaker_factory_destroy}; - -static grpc_handshaker_factory server_handshaker_factory = { - &server_handshaker_factory_vtable}; - -// -// exported functions -// - -grpc_handshaker *grpc_security_handshaker_create( - grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, - grpc_security_connector *connector) { - // If no TSI handshaker was created, return a handshaker that always fails. - // Otherwise, return a real security handshaker. - if (handshaker == NULL) { - return fail_handshaker_create(); - } else { - return security_handshaker_create(exec_ctx, handshaker, connector); - } -} - -void grpc_security_register_handshaker_factories() { - grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_CLIENT, - &client_handshaker_factory); - grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_SERVER, - &server_handshaker_factory); -} diff --git a/src/core/lib/security/transport/security_handshaker.cc b/src/core/lib/security/transport/security_handshaker.cc new file mode 100644 index 0000000000..3d19605617 --- /dev/null +++ b/src/core/lib/security/transport/security_handshaker.cc @@ -0,0 +1,539 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/transport/security_handshaker.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/handshaker.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/tsi_error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/transport_security_grpc.h" + +#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 + +typedef struct { + grpc_handshaker base; + + // State set at creation time. + tsi_handshaker *handshaker; + grpc_security_connector *connector; + + gpr_mu mu; + gpr_refcount refs; + + bool shutdown; + // Endpoint and read buffer to destroy after a shutdown. + grpc_endpoint *endpoint_to_destroy; + grpc_slice_buffer *read_buffer_to_destroy; + + // State saved while performing the handshake. + grpc_handshaker_args *args; + grpc_closure *on_handshake_done; + + unsigned char *handshake_buffer; + size_t handshake_buffer_size; + 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, + security_handshaker *h) { + 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); + } + if (h->read_buffer_to_destroy != NULL) { + grpc_slice_buffer_destroy_internal(exec_ctx, h->read_buffer_to_destroy); + gpr_free(h->read_buffer_to_destroy); + } + gpr_free(h->handshake_buffer); + 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"); + gpr_free(h); + } +} + +// Set args fields to NULL, saving the endpoint and read buffer for +// later destruction. +static void cleanup_args_for_failure_locked(grpc_exec_ctx *exec_ctx, + security_handshaker *h) { + h->endpoint_to_destroy = h->args->endpoint; + h->args->endpoint = NULL; + h->read_buffer_to_destroy = h->args->read_buffer; + h->args->read_buffer = NULL; + grpc_channel_args_destroy(exec_ctx, h->args->args); + h->args->args = NULL; +} + +// If the handshake failed or we're shutting down, clean up and invoke the +// callback with the error. +static void security_handshake_failed_locked(grpc_exec_ctx *exec_ctx, + security_handshaker *h, + grpc_error *error) { + if (error == GRPC_ERROR_NONE) { + // If we were shut down after the handshake succeeded but before an + // endpoint callback was invoked, we need to generate our own error. + error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshaker shutdown"); + } + const char *msg = grpc_error_string(error); + gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg); + + if (!h->shutdown) { + // TODO(ctiller): It is currently necessary to shutdown endpoints + // before destroying them, even if we know that there are no + // pending read/write callbacks. This should be fixed, at which + // point this can be removed. + grpc_endpoint_shutdown(exec_ctx, h->args->endpoint, GRPC_ERROR_REF(error)); + // Not shutting down, so the write failed. Clean up before + // invoking the callback. + cleanup_args_for_failure_locked(exec_ctx, h); + // Set shutdown to true so that subsequent calls to + // security_handshaker_shutdown() do nothing. + h->shutdown = true; + } + // Invoke callback. + GRPC_CLOSURE_SCHED(exec_ctx, h->on_handshake_done, error); +} + +static void on_peer_checked_inner(grpc_exec_ctx *exec_ctx, + security_handshaker *h, grpc_error *error) { + if (error != GRPC_ERROR_NONE || h->shutdown) { + security_handshake_failed_locked(exec_ctx, h, GRPC_ERROR_REF(error)); + return; + } + // Create zero-copy frame protector, if implemented. + tsi_zero_copy_grpc_protector *zero_copy_protector = NULL; + tsi_result result = tsi_handshaker_result_create_zero_copy_grpc_protector( + exec_ctx, h->handshaker_result, NULL, &zero_copy_protector); + if (result != TSI_OK && result != TSI_UNIMPLEMENTED) { + error = grpc_set_tsi_error_result( + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Zero-copy frame protector creation failed"), + result); + security_handshake_failed_locked(exec_ctx, h, error); + return; + } + // Create frame protector if zero-copy frame protector is NULL. + tsi_frame_protector *protector = NULL; + if (zero_copy_protector == NULL) { + 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"), + result); + security_handshake_failed_locked(exec_ctx, h, error); + return; + } + } + // Get unused bytes. + const 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. + 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, zero_copy_protector, h->args->endpoint, &slice, 1); + grpc_slice_unref_internal(exec_ctx, slice); + } else { + h->args->endpoint = grpc_secure_endpoint_create( + protector, zero_copy_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); + grpc_channel_args *tmp_args = h->args->args; + h->args->args = + grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1); + grpc_channel_args_destroy(exec_ctx, tmp_args); + // Invoke callback. + GRPC_CLOSURE_SCHED(exec_ctx, h->on_handshake_done, GRPC_ERROR_NONE); + // Set shutdown to true so that subsequent calls to + // security_handshaker_shutdown() do nothing. + h->shutdown = true; +} + +static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + security_handshaker *h = (security_handshaker *)arg; + gpr_mu_lock(&h->mu); + on_peer_checked_inner(exec_ctx, h, error); + gpr_mu_unlock(&h->mu); + security_handshaker_unref(exec_ctx, h); +} + +static grpc_error *check_peer_locked(grpc_exec_ctx *exec_ctx, + security_handshaker *h) { + tsi_peer 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); + } + grpc_security_connector_check_peer(exec_ctx, h->connector, peer, + &h->auth_context, &h->on_peer_checked); + return GRPC_ERROR_NONE; +} + +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); + } + // 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 = (security_handshaker *)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. + const 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, + void *arg, grpc_error *error) { + security_handshaker *h = (security_handshaker *)arg; + gpr_mu_lock(&h->mu); + if (error != GRPC_ERROR_NONE || h->shutdown) { + security_handshake_failed_locked( + exec_ctx, h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Handshake read failed", &error, 1)); + gpr_mu_unlock(&h->mu); + security_handshaker_unref(exec_ctx, h); + return; + } + // Copy all slices received. + size_t i; + size_t bytes_received_size = 0; + for (i = 0; i < h->args->read_buffer->count; i++) { + bytes_received_size += GRPC_SLICE_LENGTH(h->args->read_buffer->slices[i]); + } + if (bytes_received_size > h->handshake_buffer_size) { + h->handshake_buffer = + (uint8_t *)gpr_realloc(h->handshake_buffer, bytes_received_size); + h->handshake_buffer_size = bytes_received_size; + } + 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; + } + // 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); + } else { + gpr_mu_unlock(&h->mu); + } +} + +static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + security_handshaker *h = (security_handshaker *)arg; + gpr_mu_lock(&h->mu); + if (error != GRPC_ERROR_NONE || h->shutdown) { + security_handshake_failed_locked( + exec_ctx, h, GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Handshake write failed", &error, 1)); + gpr_mu_unlock(&h->mu); + security_handshaker_unref(exec_ctx, h); + return; + } + // 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 { + error = check_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; + } + } + gpr_mu_unlock(&h->mu); +} + +// +// public handshaker API +// + +static void security_handshaker_destroy(grpc_exec_ctx *exec_ctx, + grpc_handshaker *handshaker) { + security_handshaker *h = (security_handshaker *)handshaker; + security_handshaker_unref(exec_ctx, h); +} + +static void security_handshaker_shutdown(grpc_exec_ctx *exec_ctx, + grpc_handshaker *handshaker, + grpc_error *why) { + security_handshaker *h = (security_handshaker *)handshaker; + gpr_mu_lock(&h->mu); + if (!h->shutdown) { + h->shutdown = true; + grpc_endpoint_shutdown(exec_ctx, h->args->endpoint, GRPC_ERROR_REF(why)); + cleanup_args_for_failure_locked(exec_ctx, h); + } + gpr_mu_unlock(&h->mu); + GRPC_ERROR_UNREF(why); +} + +static void security_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_handshaker *handshaker, + grpc_tcp_server_acceptor *acceptor, + grpc_closure *on_handshake_done, + grpc_handshaker_args *args) { + security_handshaker *h = (security_handshaker *)handshaker; + gpr_mu_lock(&h->mu); + h->args = args; + h->on_handshake_done = on_handshake_done; + gpr_ref(&h->refs); + 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); + security_handshaker_unref(exec_ctx, h); + return; + } + gpr_mu_unlock(&h->mu); +} + +static const grpc_handshaker_vtable security_handshaker_vtable = { + security_handshaker_destroy, security_handshaker_shutdown, + security_handshaker_do_handshake}; + +static grpc_handshaker *security_handshaker_create( + grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, + grpc_security_connector *connector) { + security_handshaker *h = + (security_handshaker *)gpr_zalloc(sizeof(security_handshaker)); + grpc_handshaker_init(&security_handshaker_vtable, &h->base); + h->handshaker = handshaker; + h->connector = GRPC_SECURITY_CONNECTOR_REF(connector, "handshake"); + gpr_mu_init(&h->mu); + gpr_ref_init(&h->refs, 1); + h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; + h->handshake_buffer = (uint8_t *)gpr_malloc(h->handshake_buffer_size); + GRPC_CLOSURE_INIT(&h->on_handshake_data_sent_to_peer, + on_handshake_data_sent_to_peer, h, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_INIT(&h->on_handshake_data_received_from_peer, + on_handshake_data_received_from_peer, h, + 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->outgoing); + return &h->base; +} + +// +// fail_handshaker +// + +static void fail_handshaker_destroy(grpc_exec_ctx *exec_ctx, + grpc_handshaker *handshaker) { + gpr_free(handshaker); +} + +static void fail_handshaker_shutdown(grpc_exec_ctx *exec_ctx, + grpc_handshaker *handshaker, + grpc_error *why) { + GRPC_ERROR_UNREF(why); +} + +static void fail_handshaker_do_handshake(grpc_exec_ctx *exec_ctx, + grpc_handshaker *handshaker, + grpc_tcp_server_acceptor *acceptor, + grpc_closure *on_handshake_done, + grpc_handshaker_args *args) { + GRPC_CLOSURE_SCHED(exec_ctx, on_handshake_done, + GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Failed to create security handshaker")); +} + +static const grpc_handshaker_vtable fail_handshaker_vtable = { + fail_handshaker_destroy, fail_handshaker_shutdown, + fail_handshaker_do_handshake}; + +static grpc_handshaker *fail_handshaker_create() { + grpc_handshaker *h = (grpc_handshaker *)gpr_malloc(sizeof(*h)); + grpc_handshaker_init(&fail_handshaker_vtable, h); + return h; +} + +// +// handshaker factories +// + +static void client_handshaker_factory_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory, + const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { + grpc_channel_security_connector *security_connector = + (grpc_channel_security_connector *)grpc_security_connector_find_in_args( + args); + grpc_channel_security_connector_add_handshakers(exec_ctx, security_connector, + handshake_mgr); +} + +static void server_handshaker_factory_add_handshakers( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *hf, + const grpc_channel_args *args, grpc_handshake_manager *handshake_mgr) { + grpc_server_security_connector *security_connector = + (grpc_server_security_connector *)grpc_security_connector_find_in_args( + args); + grpc_server_security_connector_add_handshakers(exec_ctx, security_connector, + handshake_mgr); +} + +static void handshaker_factory_destroy( + grpc_exec_ctx *exec_ctx, grpc_handshaker_factory *handshaker_factory) {} + +static const grpc_handshaker_factory_vtable client_handshaker_factory_vtable = { + client_handshaker_factory_add_handshakers, handshaker_factory_destroy}; + +static grpc_handshaker_factory client_handshaker_factory = { + &client_handshaker_factory_vtable}; + +static const grpc_handshaker_factory_vtable server_handshaker_factory_vtable = { + server_handshaker_factory_add_handshakers, handshaker_factory_destroy}; + +static grpc_handshaker_factory server_handshaker_factory = { + &server_handshaker_factory_vtable}; + +// +// exported functions +// + +grpc_handshaker *grpc_security_handshaker_create( + grpc_exec_ctx *exec_ctx, tsi_handshaker *handshaker, + grpc_security_connector *connector) { + // If no TSI handshaker was created, return a handshaker that always fails. + // Otherwise, return a real security handshaker. + if (handshaker == NULL) { + return fail_handshaker_create(); + } else { + return security_handshaker_create(exec_ctx, handshaker, connector); + } +} + +void grpc_security_register_handshaker_factories() { + grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_CLIENT, + &client_handshaker_factory); + grpc_handshaker_factory_register(false /* at_start */, HANDSHAKER_SERVER, + &server_handshaker_factory); +} diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c deleted file mode 100644 index f5e02f42fe..0000000000 --- a/src/core/lib/security/transport/server_auth_filter.c +++ /dev/null @@ -1,274 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include "src/core/lib/security/context/security_context.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/transport/auth_filters.h" -#include "src/core/lib/slice/slice_internal.h" - -typedef enum { - STATE_INIT = 0, - STATE_DONE, - STATE_CANCELLED, -} async_state; - -typedef struct call_data { - grpc_call_combiner *call_combiner; - grpc_call_stack *owning_call; - grpc_transport_stream_op_batch *recv_initial_metadata_batch; - grpc_closure *original_recv_initial_metadata_ready; - grpc_closure recv_initial_metadata_ready; - grpc_metadata_array md; - const grpc_metadata *consumed_md; - size_t num_consumed_md; - grpc_auth_context *auth_context; - grpc_closure cancel_closure; - gpr_atm state; // async_state -} call_data; - -typedef struct channel_data { - grpc_auth_context *auth_context; - grpc_server_credentials *creds; -} channel_data; - -static grpc_metadata_array metadata_batch_to_md_array( - const grpc_metadata_batch *batch) { - grpc_linked_mdelem *l; - grpc_metadata_array result; - grpc_metadata_array_init(&result); - for (l = batch->list.head; l != NULL; l = l->next) { - grpc_metadata *usr_md = NULL; - grpc_mdelem md = l->md; - grpc_slice key = GRPC_MDKEY(md); - grpc_slice value = GRPC_MDVALUE(md); - if (result.count == result.capacity) { - result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2); - result.metadata = (grpc_metadata *)gpr_realloc( - result.metadata, result.capacity * sizeof(grpc_metadata)); - } - usr_md = &result.metadata[result.count++]; - usr_md->key = grpc_slice_ref_internal(key); - usr_md->value = grpc_slice_ref_internal(value); - } - return result; -} - -static grpc_filtered_mdelem remove_consumed_md(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_mdelem md) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - size_t i; - for (i = 0; i < calld->num_consumed_md; i++) { - const grpc_metadata *consumed_md = &calld->consumed_md[i]; - if (grpc_slice_eq(GRPC_MDKEY(md), consumed_md->key) && - grpc_slice_eq(GRPC_MDVALUE(md), consumed_md->value)) - return GRPC_FILTERED_REMOVE(); - } - return GRPC_FILTERED_MDELEM(md); -} - -static void on_md_processing_done_inner(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_metadata *consumed_md, - size_t num_consumed_md, - const grpc_metadata *response_md, - size_t num_response_md, - grpc_error *error) { - call_data *calld = (call_data *)elem->call_data; - grpc_transport_stream_op_batch *batch = calld->recv_initial_metadata_batch; - /* TODO(jboeuf): Implement support for response_md. */ - if (response_md != NULL && num_response_md > 0) { - gpr_log(GPR_INFO, - "response_md in auth metadata processing not supported for now. " - "Ignoring..."); - } - if (error == GRPC_ERROR_NONE) { - calld->consumed_md = consumed_md; - calld->num_consumed_md = num_consumed_md; - error = grpc_metadata_batch_filter( - exec_ctx, batch->payload->recv_initial_metadata.recv_initial_metadata, - remove_consumed_md, elem, "Response metadata filtering error"); - } - GRPC_CLOSURE_SCHED(exec_ctx, calld->original_recv_initial_metadata_ready, - error); -} - -// Called from application code. -static void on_md_processing_done( - void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, - const grpc_metadata *response_md, size_t num_response_md, - grpc_status_code status, const char *error_details) { - grpc_call_element *elem = (grpc_call_element *)user_data; - call_data *calld = (call_data *)elem->call_data; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - // If the call was not cancelled while we were in flight, process the result. - if (gpr_atm_full_cas(&calld->state, (gpr_atm)STATE_INIT, - (gpr_atm)STATE_DONE)) { - grpc_error *error = GRPC_ERROR_NONE; - if (status != GRPC_STATUS_OK) { - if (error_details == NULL) { - error_details = "Authentication metadata processing failed."; - } - error = grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details), - GRPC_ERROR_INT_GRPC_STATUS, status); - } - on_md_processing_done_inner(&exec_ctx, elem, consumed_md, num_consumed_md, - response_md, num_response_md, error); - } - // Clean up. - for (size_t i = 0; i < calld->md.count; i++) { - grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].key); - grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].value); - } - grpc_metadata_array_destroy(&calld->md); - GRPC_CALL_STACK_UNREF(&exec_ctx, calld->owning_call, "server_auth_metadata"); - grpc_exec_ctx_finish(&exec_ctx); -} - -static void cancel_call(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)elem->call_data; - // If the result was not already processed, invoke the callback now. - if (error != GRPC_ERROR_NONE && - gpr_atm_full_cas(&calld->state, (gpr_atm)STATE_INIT, - (gpr_atm)STATE_CANCELLED)) { - on_md_processing_done_inner(exec_ctx, elem, NULL, 0, NULL, 0, - GRPC_ERROR_REF(error)); - } - GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "cancel_call"); -} - -static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)arg; - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - grpc_transport_stream_op_batch *batch = calld->recv_initial_metadata_batch; - if (error == GRPC_ERROR_NONE) { - if (chand->creds != NULL && chand->creds->processor.process != NULL) { - // We're calling out to the application, so we need to make sure - // to drop the call combiner early if we get cancelled. - GRPC_CALL_STACK_REF(calld->owning_call, "cancel_call"); - GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem, - grpc_schedule_on_exec_ctx); - grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, - &calld->cancel_closure); - GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata"); - calld->md = metadata_batch_to_md_array( - batch->payload->recv_initial_metadata.recv_initial_metadata); - chand->creds->processor.process( - chand->creds->processor.state, calld->auth_context, - calld->md.metadata, calld->md.count, on_md_processing_done, elem); - return; - } - } - GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_initial_metadata_ready, - GRPC_ERROR_REF(error)); -} - -static void auth_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *batch) { - call_data *calld = (call_data *)elem->call_data; - if (batch->recv_initial_metadata) { - // Inject our callback. - calld->recv_initial_metadata_batch = batch; - calld->original_recv_initial_metadata_ready = - batch->payload->recv_initial_metadata.recv_initial_metadata_ready; - batch->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->recv_initial_metadata_ready; - } - grpc_call_next_op(exec_ctx, elem, batch); -} - -/* Constructor for call_data */ -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - calld->call_combiner = args->call_combiner; - calld->owning_call = args->call_stack; - GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, - recv_initial_metadata_ready, elem, - grpc_schedule_on_exec_ctx); - // Create server security context. Set its auth context from channel - // data and save it in the call context. - grpc_server_security_context *server_ctx = - grpc_server_security_context_create(); - server_ctx->auth_context = grpc_auth_context_create(chand->auth_context); - calld->auth_context = server_ctx->auth_context; - if (args->context[GRPC_CONTEXT_SECURITY].value != NULL) { - args->context[GRPC_CONTEXT_SECURITY].destroy( - args->context[GRPC_CONTEXT_SECURITY].value); - } - args->context[GRPC_CONTEXT_SECURITY].value = server_ctx; - args->context[GRPC_CONTEXT_SECURITY].destroy = - grpc_server_security_context_destroy; - return GRPC_ERROR_NONE; -} - -/* Destructor for call_data */ -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) {} - -/* Constructor for channel_data */ -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - GPR_ASSERT(!args->is_last); - channel_data *chand = (channel_data *)elem->channel_data; - grpc_auth_context *auth_context = - grpc_find_auth_context_in_args(args->channel_args); - GPR_ASSERT(auth_context != NULL); - chand->auth_context = - GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter"); - grpc_server_credentials *creds = - grpc_find_server_credentials_in_args(args->channel_args); - chand->creds = grpc_server_credentials_ref(creds); - return GRPC_ERROR_NONE; -} - -/* Destructor for channel data */ -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter"); - grpc_server_credentials_unref(exec_ctx, chand->creds); -} - -const grpc_channel_filter grpc_server_auth_filter = { - auth_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "server-auth"}; diff --git a/src/core/lib/security/transport/server_auth_filter.cc b/src/core/lib/security/transport/server_auth_filter.cc new file mode 100644 index 0000000000..f5e02f42fe --- /dev/null +++ b/src/core/lib/security/transport/server_auth_filter.cc @@ -0,0 +1,274 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include "src/core/lib/security/context/security_context.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/transport/auth_filters.h" +#include "src/core/lib/slice/slice_internal.h" + +typedef enum { + STATE_INIT = 0, + STATE_DONE, + STATE_CANCELLED, +} async_state; + +typedef struct call_data { + grpc_call_combiner *call_combiner; + grpc_call_stack *owning_call; + grpc_transport_stream_op_batch *recv_initial_metadata_batch; + grpc_closure *original_recv_initial_metadata_ready; + grpc_closure recv_initial_metadata_ready; + grpc_metadata_array md; + const grpc_metadata *consumed_md; + size_t num_consumed_md; + grpc_auth_context *auth_context; + grpc_closure cancel_closure; + gpr_atm state; // async_state +} call_data; + +typedef struct channel_data { + grpc_auth_context *auth_context; + grpc_server_credentials *creds; +} channel_data; + +static grpc_metadata_array metadata_batch_to_md_array( + const grpc_metadata_batch *batch) { + grpc_linked_mdelem *l; + grpc_metadata_array result; + grpc_metadata_array_init(&result); + for (l = batch->list.head; l != NULL; l = l->next) { + grpc_metadata *usr_md = NULL; + grpc_mdelem md = l->md; + grpc_slice key = GRPC_MDKEY(md); + grpc_slice value = GRPC_MDVALUE(md); + if (result.count == result.capacity) { + result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2); + result.metadata = (grpc_metadata *)gpr_realloc( + result.metadata, result.capacity * sizeof(grpc_metadata)); + } + usr_md = &result.metadata[result.count++]; + usr_md->key = grpc_slice_ref_internal(key); + usr_md->value = grpc_slice_ref_internal(value); + } + return result; +} + +static grpc_filtered_mdelem remove_consumed_md(grpc_exec_ctx *exec_ctx, + void *user_data, + grpc_mdelem md) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + size_t i; + for (i = 0; i < calld->num_consumed_md; i++) { + const grpc_metadata *consumed_md = &calld->consumed_md[i]; + if (grpc_slice_eq(GRPC_MDKEY(md), consumed_md->key) && + grpc_slice_eq(GRPC_MDVALUE(md), consumed_md->value)) + return GRPC_FILTERED_REMOVE(); + } + return GRPC_FILTERED_MDELEM(md); +} + +static void on_md_processing_done_inner(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_metadata *consumed_md, + size_t num_consumed_md, + const grpc_metadata *response_md, + size_t num_response_md, + grpc_error *error) { + call_data *calld = (call_data *)elem->call_data; + grpc_transport_stream_op_batch *batch = calld->recv_initial_metadata_batch; + /* TODO(jboeuf): Implement support for response_md. */ + if (response_md != NULL && num_response_md > 0) { + gpr_log(GPR_INFO, + "response_md in auth metadata processing not supported for now. " + "Ignoring..."); + } + if (error == GRPC_ERROR_NONE) { + calld->consumed_md = consumed_md; + calld->num_consumed_md = num_consumed_md; + error = grpc_metadata_batch_filter( + exec_ctx, batch->payload->recv_initial_metadata.recv_initial_metadata, + remove_consumed_md, elem, "Response metadata filtering error"); + } + GRPC_CLOSURE_SCHED(exec_ctx, calld->original_recv_initial_metadata_ready, + error); +} + +// Called from application code. +static void on_md_processing_done( + void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, + const grpc_metadata *response_md, size_t num_response_md, + grpc_status_code status, const char *error_details) { + grpc_call_element *elem = (grpc_call_element *)user_data; + call_data *calld = (call_data *)elem->call_data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + // If the call was not cancelled while we were in flight, process the result. + if (gpr_atm_full_cas(&calld->state, (gpr_atm)STATE_INIT, + (gpr_atm)STATE_DONE)) { + grpc_error *error = GRPC_ERROR_NONE; + if (status != GRPC_STATUS_OK) { + if (error_details == NULL) { + error_details = "Authentication metadata processing failed."; + } + error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details), + GRPC_ERROR_INT_GRPC_STATUS, status); + } + on_md_processing_done_inner(&exec_ctx, elem, consumed_md, num_consumed_md, + response_md, num_response_md, error); + } + // Clean up. + for (size_t i = 0; i < calld->md.count; i++) { + grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].key); + grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].value); + } + grpc_metadata_array_destroy(&calld->md); + GRPC_CALL_STACK_UNREF(&exec_ctx, calld->owning_call, "server_auth_metadata"); + grpc_exec_ctx_finish(&exec_ctx); +} + +static void cancel_call(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + // If the result was not already processed, invoke the callback now. + if (error != GRPC_ERROR_NONE && + gpr_atm_full_cas(&calld->state, (gpr_atm)STATE_INIT, + (gpr_atm)STATE_CANCELLED)) { + on_md_processing_done_inner(exec_ctx, elem, NULL, 0, NULL, 0, + GRPC_ERROR_REF(error)); + } + GRPC_CALL_STACK_UNREF(exec_ctx, calld->owning_call, "cancel_call"); +} + +static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + grpc_transport_stream_op_batch *batch = calld->recv_initial_metadata_batch; + if (error == GRPC_ERROR_NONE) { + if (chand->creds != NULL && chand->creds->processor.process != NULL) { + // We're calling out to the application, so we need to make sure + // to drop the call combiner early if we get cancelled. + GRPC_CALL_STACK_REF(calld->owning_call, "cancel_call"); + GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem, + grpc_schedule_on_exec_ctx); + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, + &calld->cancel_closure); + GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata"); + calld->md = metadata_batch_to_md_array( + batch->payload->recv_initial_metadata.recv_initial_metadata); + chand->creds->processor.process( + chand->creds->processor.state, calld->auth_context, + calld->md.metadata, calld->md.count, on_md_processing_done, elem); + return; + } + } + GRPC_CLOSURE_RUN(exec_ctx, calld->original_recv_initial_metadata_ready, + GRPC_ERROR_REF(error)); +} + +static void auth_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *batch) { + call_data *calld = (call_data *)elem->call_data; + if (batch->recv_initial_metadata) { + // Inject our callback. + calld->recv_initial_metadata_batch = batch; + calld->original_recv_initial_metadata_ready = + batch->payload->recv_initial_metadata.recv_initial_metadata_ready; + batch->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->recv_initial_metadata_ready; + } + grpc_call_next_op(exec_ctx, elem, batch); +} + +/* Constructor for call_data */ +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + calld->call_combiner = args->call_combiner; + calld->owning_call = args->call_stack; + GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, + recv_initial_metadata_ready, elem, + grpc_schedule_on_exec_ctx); + // Create server security context. Set its auth context from channel + // data and save it in the call context. + grpc_server_security_context *server_ctx = + grpc_server_security_context_create(); + server_ctx->auth_context = grpc_auth_context_create(chand->auth_context); + calld->auth_context = server_ctx->auth_context; + if (args->context[GRPC_CONTEXT_SECURITY].value != NULL) { + args->context[GRPC_CONTEXT_SECURITY].destroy( + args->context[GRPC_CONTEXT_SECURITY].value); + } + args->context[GRPC_CONTEXT_SECURITY].value = server_ctx; + args->context[GRPC_CONTEXT_SECURITY].destroy = + grpc_server_security_context_destroy; + return GRPC_ERROR_NONE; +} + +/* Destructor for call_data */ +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) {} + +/* Constructor for channel_data */ +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + GPR_ASSERT(!args->is_last); + channel_data *chand = (channel_data *)elem->channel_data; + grpc_auth_context *auth_context = + grpc_find_auth_context_in_args(args->channel_args); + GPR_ASSERT(auth_context != NULL); + chand->auth_context = + GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter"); + grpc_server_credentials *creds = + grpc_find_server_credentials_in_args(args->channel_args); + chand->creds = grpc_server_credentials_ref(creds); + return GRPC_ERROR_NONE; +} + +/* Destructor for channel data */ +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter"); + grpc_server_credentials_unref(exec_ctx, chand->creds); +} + +const grpc_channel_filter grpc_server_auth_filter = { + auth_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "server-auth"}; diff --git a/src/core/lib/security/transport/tsi_error.c b/src/core/lib/security/transport/tsi_error.c deleted file mode 100644 index 72f9600e84..0000000000 --- a/src/core/lib/security/transport/tsi_error.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/transport/tsi_error.h" - -grpc_error *grpc_set_tsi_error_result(grpc_error *error, tsi_result result) { - return grpc_error_set_int( - grpc_error_set_str( - error, GRPC_ERROR_STR_TSI_ERROR, - grpc_slice_from_static_string(tsi_result_to_string(result))), - GRPC_ERROR_INT_TSI_CODE, result); -} diff --git a/src/core/lib/security/transport/tsi_error.cc b/src/core/lib/security/transport/tsi_error.cc new file mode 100644 index 0000000000..72f9600e84 --- /dev/null +++ b/src/core/lib/security/transport/tsi_error.cc @@ -0,0 +1,27 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/transport/tsi_error.h" + +grpc_error *grpc_set_tsi_error_result(grpc_error *error, tsi_result result) { + return grpc_error_set_int( + grpc_error_set_str( + error, GRPC_ERROR_STR_TSI_ERROR, + grpc_slice_from_static_string(tsi_result_to_string(result))), + GRPC_ERROR_INT_TSI_CODE, result); +} diff --git a/src/core/lib/security/util/json_util.c b/src/core/lib/security/util/json_util.c deleted file mode 100644 index d847addef9..0000000000 --- a/src/core/lib/security/util/json_util.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/security/util/json_util.h" - -#include - -#include -#include - -const char *grpc_json_get_string_property(const grpc_json *json, - const char *prop_name) { - grpc_json *child; - for (child = json->child; child != NULL; child = child->next) { - if (strcmp(child->key, prop_name) == 0) break; - } - if (child == NULL || child->type != GRPC_JSON_STRING) { - gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name); - return NULL; - } - return child->value; -} - -bool grpc_copy_json_string_property(const grpc_json *json, - const char *prop_name, - char **copied_value) { - const char *prop_value = grpc_json_get_string_property(json, prop_name); - if (prop_value == NULL) return false; - *copied_value = gpr_strdup(prop_value); - return true; -} diff --git a/src/core/lib/security/util/json_util.cc b/src/core/lib/security/util/json_util.cc new file mode 100644 index 0000000000..d847addef9 --- /dev/null +++ b/src/core/lib/security/util/json_util.cc @@ -0,0 +1,46 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/security/util/json_util.h" + +#include + +#include +#include + +const char *grpc_json_get_string_property(const grpc_json *json, + const char *prop_name) { + grpc_json *child; + for (child = json->child; child != NULL; child = child->next) { + if (strcmp(child->key, prop_name) == 0) break; + } + if (child == NULL || child->type != GRPC_JSON_STRING) { + gpr_log(GPR_ERROR, "Invalid or missing %s property.", prop_name); + return NULL; + } + return child->value; +} + +bool grpc_copy_json_string_property(const grpc_json *json, + const char *prop_name, + char **copied_value) { + const char *prop_value = grpc_json_get_string_property(json, prop_name); + if (prop_value == NULL) return false; + *copied_value = gpr_strdup(prop_value); + return true; +} diff --git a/src/core/lib/slice/b64.c b/src/core/lib/slice/b64.c deleted file mode 100644 index 50264719a4..0000000000 --- a/src/core/lib/slice/b64.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/slice/b64.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/slice/slice_internal.h" - -/* --- Constants. --- */ - -static const int8_t base64_bytes[] = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 0x3E, -1, -1, -1, 0x3F, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1, - -1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, - 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1, -1, -1, -1, -1, - -1, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, - 0x31, 0x32, 0x33, -1, -1, -1, -1, -1}; - -static const char base64_url_unsafe_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static const char base64_url_safe_chars[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - -#define GRPC_BASE64_PAD_CHAR '=' -#define GRPC_BASE64_PAD_BYTE 0x7F -#define GRPC_BASE64_MULTILINE_LINE_LEN 76 -#define GRPC_BASE64_MULTILINE_NUM_BLOCKS (GRPC_BASE64_MULTILINE_LINE_LEN / 4) - -/* --- base64 functions. --- */ - -char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe, - int multiline) { - size_t result_projected_size = - grpc_base64_estimate_encoded_size(data_size, url_safe, multiline); - char *result = (char *)gpr_malloc(result_projected_size); - grpc_base64_encode_core(result, vdata, data_size, url_safe, multiline); - return result; -} - -size_t grpc_base64_estimate_encoded_size(size_t data_size, int url_safe, - int multiline) { - size_t result_projected_size = - 4 * ((data_size + 3) / 3) + - 2 * (multiline ? (data_size / (3 * GRPC_BASE64_MULTILINE_NUM_BLOCKS)) - : 0) + - 1; - return result_projected_size; -} - -void grpc_base64_encode_core(char *result, const void *vdata, size_t data_size, - int url_safe, int multiline) { - const unsigned char *data = (const unsigned char *)vdata; - const char *base64_chars = - url_safe ? base64_url_safe_chars : base64_url_unsafe_chars; - const size_t result_projected_size = - grpc_base64_estimate_encoded_size(data_size, url_safe, multiline); - - char *current = result; - size_t num_blocks = 0; - size_t i = 0; - - /* Encode each block. */ - while (data_size >= 3) { - *current++ = base64_chars[(data[i] >> 2) & 0x3F]; - *current++ = - base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; - *current++ = - base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)]; - *current++ = base64_chars[data[i + 2] & 0x3F]; - - data_size -= 3; - i += 3; - if (multiline && (++num_blocks == GRPC_BASE64_MULTILINE_NUM_BLOCKS)) { - *current++ = '\r'; - *current++ = '\n'; - num_blocks = 0; - } - } - - /* Take care of the tail. */ - if (data_size == 2) { - *current++ = base64_chars[(data[i] >> 2) & 0x3F]; - *current++ = - base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; - *current++ = base64_chars[(data[i + 1] & 0x0F) << 2]; - *current++ = GRPC_BASE64_PAD_CHAR; - } else if (data_size == 1) { - *current++ = base64_chars[(data[i] >> 2) & 0x3F]; - *current++ = base64_chars[(data[i] & 0x03) << 4]; - *current++ = GRPC_BASE64_PAD_CHAR; - *current++ = GRPC_BASE64_PAD_CHAR; - } - - GPR_ASSERT(current >= result); - GPR_ASSERT((uintptr_t)(current - result) < result_projected_size); - result[current - result] = '\0'; -} - -grpc_slice grpc_base64_decode(grpc_exec_ctx *exec_ctx, const char *b64, - int url_safe) { - return grpc_base64_decode_with_len(exec_ctx, b64, strlen(b64), url_safe); -} - -static void decode_one_char(const unsigned char *codes, unsigned char *result, - size_t *result_offset) { - uint32_t packed = ((uint32_t)codes[0] << 2) | ((uint32_t)codes[1] >> 4); - result[(*result_offset)++] = (unsigned char)packed; -} - -static void decode_two_chars(const unsigned char *codes, unsigned char *result, - size_t *result_offset) { - uint32_t packed = ((uint32_t)codes[0] << 10) | ((uint32_t)codes[1] << 4) | - ((uint32_t)codes[2] >> 2); - result[(*result_offset)++] = (unsigned char)(packed >> 8); - result[(*result_offset)++] = (unsigned char)(packed); -} - -static int decode_group(const unsigned char *codes, size_t num_codes, - unsigned char *result, size_t *result_offset) { - GPR_ASSERT(num_codes <= 4); - - /* Short end groups that may not have padding. */ - if (num_codes == 1) { - gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes."); - return 0; - } - if (num_codes == 2) { - decode_one_char(codes, result, result_offset); - return 1; - } - if (num_codes == 3) { - decode_two_chars(codes, result, result_offset); - return 1; - } - - /* Regular 4 byte groups with padding or not. */ - GPR_ASSERT(num_codes == 4); - if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) { - gpr_log(GPR_ERROR, "Invalid padding detected."); - return 0; - } - if (codes[2] == GRPC_BASE64_PAD_BYTE) { - if (codes[3] == GRPC_BASE64_PAD_BYTE) { - decode_one_char(codes, result, result_offset); - } else { - gpr_log(GPR_ERROR, "Invalid padding detected."); - return 0; - } - } else if (codes[3] == GRPC_BASE64_PAD_BYTE) { - decode_two_chars(codes, result, result_offset); - } else { - /* No padding. */ - uint32_t packed = ((uint32_t)codes[0] << 18) | ((uint32_t)codes[1] << 12) | - ((uint32_t)codes[2] << 6) | codes[3]; - result[(*result_offset)++] = (unsigned char)(packed >> 16); - result[(*result_offset)++] = (unsigned char)(packed >> 8); - result[(*result_offset)++] = (unsigned char)(packed); - } - return 1; -} - -grpc_slice grpc_base64_decode_with_len(grpc_exec_ctx *exec_ctx, const char *b64, - size_t b64_len, int url_safe) { - grpc_slice result = GRPC_SLICE_MALLOC(b64_len); - unsigned char *current = GRPC_SLICE_START_PTR(result); - size_t result_size = 0; - unsigned char codes[4]; - size_t num_codes = 0; - - while (b64_len--) { - unsigned char c = (unsigned char)(*b64++); - signed char code; - if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue; - if (url_safe) { - if (c == '+' || c == '/') { - gpr_log(GPR_ERROR, "Invalid character for url safe base64 %c", c); - goto fail; - } - if (c == '-') { - c = '+'; - } else if (c == '_') { - c = '/'; - } - } - code = base64_bytes[c]; - if (code == -1) { - if (c != '\r' && c != '\n') { - gpr_log(GPR_ERROR, "Invalid character %c", c); - goto fail; - } - } else { - codes[num_codes++] = (unsigned char)code; - if (num_codes == 4) { - if (!decode_group(codes, num_codes, current, &result_size)) goto fail; - num_codes = 0; - } - } - } - - if (num_codes != 0 && - !decode_group(codes, num_codes, current, &result_size)) { - goto fail; - } - GRPC_SLICE_SET_LENGTH(result, result_size); - return result; - -fail: - grpc_slice_unref_internal(exec_ctx, result); - return grpc_empty_slice(); -} diff --git a/src/core/lib/slice/b64.cc b/src/core/lib/slice/b64.cc new file mode 100644 index 0000000000..50264719a4 --- /dev/null +++ b/src/core/lib/slice/b64.cc @@ -0,0 +1,236 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/slice/b64.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/slice/slice_internal.h" + +/* --- Constants. --- */ + +static const int8_t base64_bytes[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 0x3E, -1, -1, -1, 0x3F, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, -1, -1, + -1, 0x7F, -1, -1, -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, -1, -1, -1, -1, -1, + -1, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32, 0x33, -1, -1, -1, -1, -1}; + +static const char base64_url_unsafe_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char base64_url_safe_chars[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +#define GRPC_BASE64_PAD_CHAR '=' +#define GRPC_BASE64_PAD_BYTE 0x7F +#define GRPC_BASE64_MULTILINE_LINE_LEN 76 +#define GRPC_BASE64_MULTILINE_NUM_BLOCKS (GRPC_BASE64_MULTILINE_LINE_LEN / 4) + +/* --- base64 functions. --- */ + +char *grpc_base64_encode(const void *vdata, size_t data_size, int url_safe, + int multiline) { + size_t result_projected_size = + grpc_base64_estimate_encoded_size(data_size, url_safe, multiline); + char *result = (char *)gpr_malloc(result_projected_size); + grpc_base64_encode_core(result, vdata, data_size, url_safe, multiline); + return result; +} + +size_t grpc_base64_estimate_encoded_size(size_t data_size, int url_safe, + int multiline) { + size_t result_projected_size = + 4 * ((data_size + 3) / 3) + + 2 * (multiline ? (data_size / (3 * GRPC_BASE64_MULTILINE_NUM_BLOCKS)) + : 0) + + 1; + return result_projected_size; +} + +void grpc_base64_encode_core(char *result, const void *vdata, size_t data_size, + int url_safe, int multiline) { + const unsigned char *data = (const unsigned char *)vdata; + const char *base64_chars = + url_safe ? base64_url_safe_chars : base64_url_unsafe_chars; + const size_t result_projected_size = + grpc_base64_estimate_encoded_size(data_size, url_safe, multiline); + + char *current = result; + size_t num_blocks = 0; + size_t i = 0; + + /* Encode each block. */ + while (data_size >= 3) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = + base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; + *current++ = + base64_chars[((data[i + 1] & 0x0F) << 2) | ((data[i + 2] >> 6) & 0x03)]; + *current++ = base64_chars[data[i + 2] & 0x3F]; + + data_size -= 3; + i += 3; + if (multiline && (++num_blocks == GRPC_BASE64_MULTILINE_NUM_BLOCKS)) { + *current++ = '\r'; + *current++ = '\n'; + num_blocks = 0; + } + } + + /* Take care of the tail. */ + if (data_size == 2) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = + base64_chars[((data[i] & 0x03) << 4) | ((data[i + 1] >> 4) & 0x0F)]; + *current++ = base64_chars[(data[i + 1] & 0x0F) << 2]; + *current++ = GRPC_BASE64_PAD_CHAR; + } else if (data_size == 1) { + *current++ = base64_chars[(data[i] >> 2) & 0x3F]; + *current++ = base64_chars[(data[i] & 0x03) << 4]; + *current++ = GRPC_BASE64_PAD_CHAR; + *current++ = GRPC_BASE64_PAD_CHAR; + } + + GPR_ASSERT(current >= result); + GPR_ASSERT((uintptr_t)(current - result) < result_projected_size); + result[current - result] = '\0'; +} + +grpc_slice grpc_base64_decode(grpc_exec_ctx *exec_ctx, const char *b64, + int url_safe) { + return grpc_base64_decode_with_len(exec_ctx, b64, strlen(b64), url_safe); +} + +static void decode_one_char(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + uint32_t packed = ((uint32_t)codes[0] << 2) | ((uint32_t)codes[1] >> 4); + result[(*result_offset)++] = (unsigned char)packed; +} + +static void decode_two_chars(const unsigned char *codes, unsigned char *result, + size_t *result_offset) { + uint32_t packed = ((uint32_t)codes[0] << 10) | ((uint32_t)codes[1] << 4) | + ((uint32_t)codes[2] >> 2); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); +} + +static int decode_group(const unsigned char *codes, size_t num_codes, + unsigned char *result, size_t *result_offset) { + GPR_ASSERT(num_codes <= 4); + + /* Short end groups that may not have padding. */ + if (num_codes == 1) { + gpr_log(GPR_ERROR, "Invalid group. Must be at least 2 bytes."); + return 0; + } + if (num_codes == 2) { + decode_one_char(codes, result, result_offset); + return 1; + } + if (num_codes == 3) { + decode_two_chars(codes, result, result_offset); + return 1; + } + + /* Regular 4 byte groups with padding or not. */ + GPR_ASSERT(num_codes == 4); + if (codes[0] == GRPC_BASE64_PAD_BYTE || codes[1] == GRPC_BASE64_PAD_BYTE) { + gpr_log(GPR_ERROR, "Invalid padding detected."); + return 0; + } + if (codes[2] == GRPC_BASE64_PAD_BYTE) { + if (codes[3] == GRPC_BASE64_PAD_BYTE) { + decode_one_char(codes, result, result_offset); + } else { + gpr_log(GPR_ERROR, "Invalid padding detected."); + return 0; + } + } else if (codes[3] == GRPC_BASE64_PAD_BYTE) { + decode_two_chars(codes, result, result_offset); + } else { + /* No padding. */ + uint32_t packed = ((uint32_t)codes[0] << 18) | ((uint32_t)codes[1] << 12) | + ((uint32_t)codes[2] << 6) | codes[3]; + result[(*result_offset)++] = (unsigned char)(packed >> 16); + result[(*result_offset)++] = (unsigned char)(packed >> 8); + result[(*result_offset)++] = (unsigned char)(packed); + } + return 1; +} + +grpc_slice grpc_base64_decode_with_len(grpc_exec_ctx *exec_ctx, const char *b64, + size_t b64_len, int url_safe) { + grpc_slice result = GRPC_SLICE_MALLOC(b64_len); + unsigned char *current = GRPC_SLICE_START_PTR(result); + size_t result_size = 0; + unsigned char codes[4]; + size_t num_codes = 0; + + while (b64_len--) { + unsigned char c = (unsigned char)(*b64++); + signed char code; + if (c >= GPR_ARRAY_SIZE(base64_bytes)) continue; + if (url_safe) { + if (c == '+' || c == '/') { + gpr_log(GPR_ERROR, "Invalid character for url safe base64 %c", c); + goto fail; + } + if (c == '-') { + c = '+'; + } else if (c == '_') { + c = '/'; + } + } + code = base64_bytes[c]; + if (code == -1) { + if (c != '\r' && c != '\n') { + gpr_log(GPR_ERROR, "Invalid character %c", c); + goto fail; + } + } else { + codes[num_codes++] = (unsigned char)code; + if (num_codes == 4) { + if (!decode_group(codes, num_codes, current, &result_size)) goto fail; + num_codes = 0; + } + } + } + + if (num_codes != 0 && + !decode_group(codes, num_codes, current, &result_size)) { + goto fail; + } + GRPC_SLICE_SET_LENGTH(result, result_size); + return result; + +fail: + grpc_slice_unref_internal(exec_ctx, result); + return grpc_empty_slice(); +} diff --git a/src/core/lib/slice/percent_encoding.c b/src/core/lib/slice/percent_encoding.c deleted file mode 100644 index effc8d7ad6..0000000000 --- a/src/core/lib/slice/percent_encoding.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/slice/percent_encoding.h" - -#include - -#include "src/core/lib/slice/slice_internal.h" - -const uint8_t grpc_url_percent_encoding_unreserved_bytes[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0xfe, 0xff, 0xff, - 0x87, 0xfe, 0xff, 0xff, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -const uint8_t grpc_compatible_percent_encoding_unreserved_bytes[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -static bool is_unreserved_character(uint8_t c, - const uint8_t *unreserved_bytes) { - return ((unreserved_bytes[c / 8] >> (c % 8)) & 1) != 0; -} - -grpc_slice grpc_percent_encode_slice(grpc_slice slice, - const uint8_t *unreserved_bytes) { - static const uint8_t hex[] = "0123456789ABCDEF"; - - // first pass: count the number of bytes needed to output this string - size_t output_length = 0; - const uint8_t *slice_start = GRPC_SLICE_START_PTR(slice); - const uint8_t *slice_end = GRPC_SLICE_END_PTR(slice); - const uint8_t *p; - bool any_reserved_bytes = false; - for (p = slice_start; p < slice_end; p++) { - bool unres = is_unreserved_character(*p, unreserved_bytes); - output_length += unres ? 1 : 3; - any_reserved_bytes |= !unres; - } - // no unreserved bytes: return the string unmodified - if (!any_reserved_bytes) { - return grpc_slice_ref_internal(slice); - } - // second pass: actually encode - grpc_slice out = GRPC_SLICE_MALLOC(output_length); - uint8_t *q = GRPC_SLICE_START_PTR(out); - for (p = slice_start; p < slice_end; p++) { - if (is_unreserved_character(*p, unreserved_bytes)) { - *q++ = *p; - } else { - *q++ = '%'; - *q++ = hex[*p >> 4]; - *q++ = hex[*p & 15]; - } - } - GPR_ASSERT(q == GRPC_SLICE_END_PTR(out)); - return out; -} - -static bool valid_hex(const uint8_t *p, const uint8_t *end) { - if (p >= end) return false; - return (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || - (*p >= 'A' && *p <= 'F'); -} - -static uint8_t dehex(uint8_t c) { - if (c >= '0' && c <= '9') return (uint8_t)(c - '0'); - if (c >= 'A' && c <= 'F') return (uint8_t)(c - 'A' + 10); - if (c >= 'a' && c <= 'f') return (uint8_t)(c - 'a' + 10); - GPR_UNREACHABLE_CODE(return 255); -} - -bool grpc_strict_percent_decode_slice(grpc_slice slice_in, - const uint8_t *unreserved_bytes, - grpc_slice *slice_out) { - const uint8_t *p = GRPC_SLICE_START_PTR(slice_in); - const uint8_t *in_end = GRPC_SLICE_END_PTR(slice_in); - size_t out_length = 0; - bool any_percent_encoded_stuff = false; - while (p != in_end) { - if (*p == '%') { - if (!valid_hex(++p, in_end)) return false; - if (!valid_hex(++p, in_end)) return false; - p++; - out_length++; - any_percent_encoded_stuff = true; - } else if (is_unreserved_character(*p, unreserved_bytes)) { - p++; - out_length++; - } else { - return false; - } - } - if (!any_percent_encoded_stuff) { - *slice_out = grpc_slice_ref_internal(slice_in); - return true; - } - p = GRPC_SLICE_START_PTR(slice_in); - *slice_out = GRPC_SLICE_MALLOC(out_length); - uint8_t *q = GRPC_SLICE_START_PTR(*slice_out); - while (p != in_end) { - if (*p == '%') { - *q++ = (uint8_t)(dehex(p[1]) << 4) | (dehex(p[2])); - p += 3; - } else { - *q++ = *p++; - } - } - GPR_ASSERT(q == GRPC_SLICE_END_PTR(*slice_out)); - return true; -} - -grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in) { - const uint8_t *p = GRPC_SLICE_START_PTR(slice_in); - const uint8_t *in_end = GRPC_SLICE_END_PTR(slice_in); - size_t out_length = 0; - bool any_percent_encoded_stuff = false; - while (p != in_end) { - if (*p == '%') { - if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) { - p++; - out_length++; - } else { - p += 3; - out_length++; - any_percent_encoded_stuff = true; - } - } else { - p++; - out_length++; - } - } - if (!any_percent_encoded_stuff) { - return grpc_slice_ref_internal(slice_in); - } - p = GRPC_SLICE_START_PTR(slice_in); - grpc_slice out = GRPC_SLICE_MALLOC(out_length); - uint8_t *q = GRPC_SLICE_START_PTR(out); - while (p != in_end) { - if (*p == '%') { - if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) { - *q++ = *p++; - } else { - *q++ = (uint8_t)(dehex(p[1]) << 4) | (dehex(p[2])); - p += 3; - } - } else { - *q++ = *p++; - } - } - GPR_ASSERT(q == GRPC_SLICE_END_PTR(out)); - return out; -} diff --git a/src/core/lib/slice/percent_encoding.cc b/src/core/lib/slice/percent_encoding.cc new file mode 100644 index 0000000000..effc8d7ad6 --- /dev/null +++ b/src/core/lib/slice/percent_encoding.cc @@ -0,0 +1,167 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/slice/percent_encoding.h" + +#include + +#include "src/core/lib/slice/slice_internal.h" + +const uint8_t grpc_url_percent_encoding_unreserved_bytes[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0xfe, 0xff, 0xff, + 0x87, 0xfe, 0xff, 0xff, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +const uint8_t grpc_compatible_percent_encoding_unreserved_bytes[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static bool is_unreserved_character(uint8_t c, + const uint8_t *unreserved_bytes) { + return ((unreserved_bytes[c / 8] >> (c % 8)) & 1) != 0; +} + +grpc_slice grpc_percent_encode_slice(grpc_slice slice, + const uint8_t *unreserved_bytes) { + static const uint8_t hex[] = "0123456789ABCDEF"; + + // first pass: count the number of bytes needed to output this string + size_t output_length = 0; + const uint8_t *slice_start = GRPC_SLICE_START_PTR(slice); + const uint8_t *slice_end = GRPC_SLICE_END_PTR(slice); + const uint8_t *p; + bool any_reserved_bytes = false; + for (p = slice_start; p < slice_end; p++) { + bool unres = is_unreserved_character(*p, unreserved_bytes); + output_length += unres ? 1 : 3; + any_reserved_bytes |= !unres; + } + // no unreserved bytes: return the string unmodified + if (!any_reserved_bytes) { + return grpc_slice_ref_internal(slice); + } + // second pass: actually encode + grpc_slice out = GRPC_SLICE_MALLOC(output_length); + uint8_t *q = GRPC_SLICE_START_PTR(out); + for (p = slice_start; p < slice_end; p++) { + if (is_unreserved_character(*p, unreserved_bytes)) { + *q++ = *p; + } else { + *q++ = '%'; + *q++ = hex[*p >> 4]; + *q++ = hex[*p & 15]; + } + } + GPR_ASSERT(q == GRPC_SLICE_END_PTR(out)); + return out; +} + +static bool valid_hex(const uint8_t *p, const uint8_t *end) { + if (p >= end) return false; + return (*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f') || + (*p >= 'A' && *p <= 'F'); +} + +static uint8_t dehex(uint8_t c) { + if (c >= '0' && c <= '9') return (uint8_t)(c - '0'); + if (c >= 'A' && c <= 'F') return (uint8_t)(c - 'A' + 10); + if (c >= 'a' && c <= 'f') return (uint8_t)(c - 'a' + 10); + GPR_UNREACHABLE_CODE(return 255); +} + +bool grpc_strict_percent_decode_slice(grpc_slice slice_in, + const uint8_t *unreserved_bytes, + grpc_slice *slice_out) { + const uint8_t *p = GRPC_SLICE_START_PTR(slice_in); + const uint8_t *in_end = GRPC_SLICE_END_PTR(slice_in); + size_t out_length = 0; + bool any_percent_encoded_stuff = false; + while (p != in_end) { + if (*p == '%') { + if (!valid_hex(++p, in_end)) return false; + if (!valid_hex(++p, in_end)) return false; + p++; + out_length++; + any_percent_encoded_stuff = true; + } else if (is_unreserved_character(*p, unreserved_bytes)) { + p++; + out_length++; + } else { + return false; + } + } + if (!any_percent_encoded_stuff) { + *slice_out = grpc_slice_ref_internal(slice_in); + return true; + } + p = GRPC_SLICE_START_PTR(slice_in); + *slice_out = GRPC_SLICE_MALLOC(out_length); + uint8_t *q = GRPC_SLICE_START_PTR(*slice_out); + while (p != in_end) { + if (*p == '%') { + *q++ = (uint8_t)(dehex(p[1]) << 4) | (dehex(p[2])); + p += 3; + } else { + *q++ = *p++; + } + } + GPR_ASSERT(q == GRPC_SLICE_END_PTR(*slice_out)); + return true; +} + +grpc_slice grpc_permissive_percent_decode_slice(grpc_slice slice_in) { + const uint8_t *p = GRPC_SLICE_START_PTR(slice_in); + const uint8_t *in_end = GRPC_SLICE_END_PTR(slice_in); + size_t out_length = 0; + bool any_percent_encoded_stuff = false; + while (p != in_end) { + if (*p == '%') { + if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) { + p++; + out_length++; + } else { + p += 3; + out_length++; + any_percent_encoded_stuff = true; + } + } else { + p++; + out_length++; + } + } + if (!any_percent_encoded_stuff) { + return grpc_slice_ref_internal(slice_in); + } + p = GRPC_SLICE_START_PTR(slice_in); + grpc_slice out = GRPC_SLICE_MALLOC(out_length); + uint8_t *q = GRPC_SLICE_START_PTR(out); + while (p != in_end) { + if (*p == '%') { + if (!valid_hex(p + 1, in_end) || !valid_hex(p + 2, in_end)) { + *q++ = *p++; + } else { + *q++ = (uint8_t)(dehex(p[1]) << 4) | (dehex(p[2])); + p += 3; + } + } else { + *q++ = *p++; + } + } + GPR_ASSERT(q == GRPC_SLICE_END_PTR(out)); + return out; +} diff --git a/src/core/lib/slice/slice.c b/src/core/lib/slice/slice.c deleted file mode 100644 index 0764eda052..0000000000 --- a/src/core/lib/slice/slice.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/slice/slice_internal.h" - -#include -#include -#include - -#include - -#include "src/core/lib/iomgr/exec_ctx.h" - -char *grpc_slice_to_c_string(grpc_slice slice) { - char *out = (char *)gpr_malloc(GRPC_SLICE_LENGTH(slice) + 1); - memcpy(out, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice)); - out[GRPC_SLICE_LENGTH(slice)] = 0; - return out; -} - -grpc_slice grpc_empty_slice(void) { - grpc_slice out; - out.refcount = NULL; - out.data.inlined.length = 0; - return out; -} - -grpc_slice grpc_slice_copy(grpc_slice s) { - grpc_slice out = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(s)); - memcpy(GRPC_SLICE_START_PTR(out), GRPC_SLICE_START_PTR(s), - GRPC_SLICE_LENGTH(s)); - return out; -} - -grpc_slice grpc_slice_ref_internal(grpc_slice slice) { - if (slice.refcount) { - slice.refcount->vtable->ref(slice.refcount); - } - return slice; -} - -void grpc_slice_unref_internal(grpc_exec_ctx *exec_ctx, grpc_slice slice) { - if (slice.refcount) { - slice.refcount->vtable->unref(exec_ctx, slice.refcount); - } -} - -/* Public API */ -grpc_slice grpc_slice_ref(grpc_slice slice) { - return grpc_slice_ref_internal(slice); -} - -/* Public API */ -void grpc_slice_unref(grpc_slice slice) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_unref_internal(&exec_ctx, slice); - grpc_exec_ctx_finish(&exec_ctx); -} - -/* grpc_slice_from_static_string support structure - a refcount that does - nothing */ -static void noop_ref(void *unused) {} -static void noop_unref(grpc_exec_ctx *exec_ctx, void *unused) {} - -static const grpc_slice_refcount_vtable noop_refcount_vtable = { - noop_ref, noop_unref, grpc_slice_default_eq_impl, - grpc_slice_default_hash_impl}; -static grpc_slice_refcount noop_refcount = {&noop_refcount_vtable, - &noop_refcount}; - -grpc_slice grpc_slice_from_static_buffer(const void *s, size_t len) { - grpc_slice slice; - slice.refcount = &noop_refcount; - slice.data.refcounted.bytes = (uint8_t *)s; - slice.data.refcounted.length = len; - return slice; -} - -grpc_slice grpc_slice_from_static_string(const char *s) { - return grpc_slice_from_static_buffer(s, strlen(s)); -} - -/* grpc_slice_new support structures - we create a refcount object extended - with the user provided data pointer & destroy function */ -typedef struct new_slice_refcount { - grpc_slice_refcount rc; - gpr_refcount refs; - void (*user_destroy)(void *); - void *user_data; -} new_slice_refcount; - -static void new_slice_ref(void *p) { - new_slice_refcount *r = (new_slice_refcount *)p; - gpr_ref(&r->refs); -} - -static void new_slice_unref(grpc_exec_ctx *exec_ctx, void *p) { - new_slice_refcount *r = (new_slice_refcount *)p; - if (gpr_unref(&r->refs)) { - r->user_destroy(r->user_data); - gpr_free(r); - } -} - -static const grpc_slice_refcount_vtable new_slice_vtable = { - new_slice_ref, new_slice_unref, grpc_slice_default_eq_impl, - grpc_slice_default_hash_impl}; - -grpc_slice grpc_slice_new_with_user_data(void *p, size_t len, - void (*destroy)(void *), - void *user_data) { - grpc_slice slice; - new_slice_refcount *rc = - (new_slice_refcount *)gpr_malloc(sizeof(new_slice_refcount)); - gpr_ref_init(&rc->refs, 1); - rc->rc.vtable = &new_slice_vtable; - rc->rc.sub_refcount = &rc->rc; - rc->user_destroy = destroy; - rc->user_data = user_data; - - slice.refcount = &rc->rc; - slice.data.refcounted.bytes = (uint8_t *)p; - slice.data.refcounted.length = len; - return slice; -} - -grpc_slice grpc_slice_new(void *p, size_t len, void (*destroy)(void *)) { - /* Pass "p" to *destroy when the slice is no longer needed. */ - return grpc_slice_new_with_user_data(p, len, destroy, p); -} - -/* grpc_slice_new_with_len support structures - we create a refcount object - extended with the user provided data pointer & destroy function */ -typedef struct new_with_len_slice_refcount { - grpc_slice_refcount rc; - gpr_refcount refs; - void *user_data; - size_t user_length; - void (*user_destroy)(void *, size_t); -} new_with_len_slice_refcount; - -static void new_with_len_ref(void *p) { - new_with_len_slice_refcount *r = (new_with_len_slice_refcount *)p; - gpr_ref(&r->refs); -} - -static void new_with_len_unref(grpc_exec_ctx *exec_ctx, void *p) { - new_with_len_slice_refcount *r = (new_with_len_slice_refcount *)p; - if (gpr_unref(&r->refs)) { - r->user_destroy(r->user_data, r->user_length); - gpr_free(r); - } -} - -static const grpc_slice_refcount_vtable new_with_len_vtable = { - new_with_len_ref, new_with_len_unref, grpc_slice_default_eq_impl, - grpc_slice_default_hash_impl}; - -grpc_slice grpc_slice_new_with_len(void *p, size_t len, - void (*destroy)(void *, size_t)) { - grpc_slice slice; - new_with_len_slice_refcount *rc = (new_with_len_slice_refcount *)gpr_malloc( - sizeof(new_with_len_slice_refcount)); - gpr_ref_init(&rc->refs, 1); - rc->rc.vtable = &new_with_len_vtable; - rc->rc.sub_refcount = &rc->rc; - rc->user_destroy = destroy; - rc->user_data = p; - rc->user_length = len; - - slice.refcount = &rc->rc; - slice.data.refcounted.bytes = (uint8_t *)p; - slice.data.refcounted.length = len; - return slice; -} - -grpc_slice grpc_slice_from_copied_buffer(const char *source, size_t length) { - if (length == 0) return grpc_empty_slice(); - grpc_slice slice = GRPC_SLICE_MALLOC(length); - memcpy(GRPC_SLICE_START_PTR(slice), source, length); - return slice; -} - -grpc_slice grpc_slice_from_copied_string(const char *source) { - return grpc_slice_from_copied_buffer(source, strlen(source)); -} - -typedef struct { - grpc_slice_refcount base; - gpr_refcount refs; -} malloc_refcount; - -static void malloc_ref(void *p) { - malloc_refcount *r = (malloc_refcount *)p; - gpr_ref(&r->refs); -} - -static void malloc_unref(grpc_exec_ctx *exec_ctx, void *p) { - malloc_refcount *r = (malloc_refcount *)p; - if (gpr_unref(&r->refs)) { - gpr_free(r); - } -} - -static const grpc_slice_refcount_vtable malloc_vtable = { - malloc_ref, malloc_unref, grpc_slice_default_eq_impl, - grpc_slice_default_hash_impl}; - -grpc_slice grpc_slice_malloc_large(size_t length) { - grpc_slice slice; - - /* Memory layout used by the slice created here: - - +-----------+----------------------------------------------------------+ - | refcount | bytes | - +-----------+----------------------------------------------------------+ - - refcount is a malloc_refcount - bytes is an array of bytes of the requested length - Both parts are placed in the same allocation returned from gpr_malloc */ - malloc_refcount *rc = - (malloc_refcount *)gpr_malloc(sizeof(malloc_refcount) + length); - - /* Initial refcount on rc is 1 - and it's up to the caller to release - this reference. */ - gpr_ref_init(&rc->refs, 1); - - rc->base.vtable = &malloc_vtable; - rc->base.sub_refcount = &rc->base; - - /* Build up the slice to be returned. */ - /* The slices refcount points back to the allocated block. */ - slice.refcount = &rc->base; - /* The data bytes are placed immediately after the refcount struct */ - slice.data.refcounted.bytes = (uint8_t *)(rc + 1); - /* And the length of the block is set to the requested length */ - slice.data.refcounted.length = length; - return slice; -} - -grpc_slice grpc_slice_malloc(size_t length) { - grpc_slice slice; - - if (length > sizeof(slice.data.inlined.bytes)) { - return grpc_slice_malloc_large(length); - } else { - /* small slice: just inline the data */ - slice.refcount = NULL; - slice.data.inlined.length = (uint8_t)length; - } - return slice; -} - -grpc_slice grpc_slice_sub_no_ref(grpc_slice source, size_t begin, size_t end) { - grpc_slice subset; - - GPR_ASSERT(end >= begin); - - if (source.refcount) { - /* Enforce preconditions */ - GPR_ASSERT(source.data.refcounted.length >= end); - - /* Build the result */ - subset.refcount = source.refcount->sub_refcount; - /* Point into the source array */ - subset.data.refcounted.bytes = source.data.refcounted.bytes + begin; - subset.data.refcounted.length = end - begin; - } else { - /* Enforce preconditions */ - GPR_ASSERT(source.data.inlined.length >= end); - subset.refcount = NULL; - subset.data.inlined.length = (uint8_t)(end - begin); - memcpy(subset.data.inlined.bytes, source.data.inlined.bytes + begin, - end - begin); - } - return subset; -} - -grpc_slice grpc_slice_sub(grpc_slice source, size_t begin, size_t end) { - grpc_slice subset; - - if (end - begin <= sizeof(subset.data.inlined.bytes)) { - subset.refcount = NULL; - subset.data.inlined.length = (uint8_t)(end - begin); - memcpy(subset.data.inlined.bytes, GRPC_SLICE_START_PTR(source) + begin, - end - begin); - } else { - subset = grpc_slice_sub_no_ref(source, begin, end); - /* Bump the refcount */ - subset.refcount->vtable->ref(subset.refcount); - } - return subset; -} - -grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice *source, size_t split, - grpc_slice_ref_whom ref_whom) { - grpc_slice tail; - - if (source->refcount == NULL) { - /* inlined data, copy it out */ - GPR_ASSERT(source->data.inlined.length >= split); - tail.refcount = NULL; - tail.data.inlined.length = (uint8_t)(source->data.inlined.length - split); - memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split, - tail.data.inlined.length); - source->data.inlined.length = (uint8_t)split; - } else { - size_t tail_length = source->data.refcounted.length - split; - GPR_ASSERT(source->data.refcounted.length >= split); - if (tail_length < sizeof(tail.data.inlined.bytes) && - ref_whom != GRPC_SLICE_REF_TAIL) { - /* Copy out the bytes - it'll be cheaper than refcounting */ - tail.refcount = NULL; - tail.data.inlined.length = (uint8_t)tail_length; - memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split, - tail_length); - source->refcount = source->refcount->sub_refcount; - } else { - /* Build the result */ - switch (ref_whom) { - case GRPC_SLICE_REF_TAIL: - tail.refcount = source->refcount->sub_refcount; - source->refcount = &noop_refcount; - break; - case GRPC_SLICE_REF_HEAD: - tail.refcount = &noop_refcount; - source->refcount = source->refcount->sub_refcount; - break; - case GRPC_SLICE_REF_BOTH: - tail.refcount = source->refcount->sub_refcount; - source->refcount = source->refcount->sub_refcount; - /* Bump the refcount */ - tail.refcount->vtable->ref(tail.refcount); - break; - } - /* Point into the source array */ - tail.data.refcounted.bytes = source->data.refcounted.bytes + split; - tail.data.refcounted.length = tail_length; - } - source->data.refcounted.length = split; - } - - return tail; -} - -grpc_slice grpc_slice_split_tail(grpc_slice *source, size_t split) { - return grpc_slice_split_tail_maybe_ref(source, split, GRPC_SLICE_REF_BOTH); -} - -grpc_slice grpc_slice_split_head(grpc_slice *source, size_t split) { - grpc_slice head; - - if (source->refcount == NULL) { - GPR_ASSERT(source->data.inlined.length >= split); - - head.refcount = NULL; - head.data.inlined.length = (uint8_t)split; - memcpy(head.data.inlined.bytes, source->data.inlined.bytes, split); - source->data.inlined.length = - (uint8_t)(source->data.inlined.length - split); - memmove(source->data.inlined.bytes, source->data.inlined.bytes + split, - source->data.inlined.length); - } else if (split < sizeof(head.data.inlined.bytes)) { - GPR_ASSERT(source->data.refcounted.length >= split); - - head.refcount = NULL; - head.data.inlined.length = (uint8_t)split; - memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split); - source->refcount = source->refcount->sub_refcount; - source->data.refcounted.bytes += split; - source->data.refcounted.length -= split; - } else { - GPR_ASSERT(source->data.refcounted.length >= split); - - /* Build the result */ - head.refcount = source->refcount->sub_refcount; - /* Bump the refcount */ - head.refcount->vtable->ref(head.refcount); - /* Point into the source array */ - head.data.refcounted.bytes = source->data.refcounted.bytes; - head.data.refcounted.length = split; - source->refcount = source->refcount->sub_refcount; - source->data.refcounted.bytes += split; - source->data.refcounted.length -= split; - } - - return head; -} - -int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) { - if (GRPC_SLICE_LENGTH(a) != GRPC_SLICE_LENGTH(b)) return false; - if (GRPC_SLICE_LENGTH(a) == 0) return true; - return 0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b), - GRPC_SLICE_LENGTH(a)); -} - -int grpc_slice_eq(grpc_slice a, grpc_slice b) { - if (a.refcount && b.refcount && a.refcount->vtable == b.refcount->vtable) { - return a.refcount->vtable->eq(a, b); - } - return grpc_slice_default_eq_impl(a, b); -} - -int grpc_slice_cmp(grpc_slice a, grpc_slice b) { - int d = (int)(GRPC_SLICE_LENGTH(a) - GRPC_SLICE_LENGTH(b)); - if (d != 0) return d; - return memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b), - GRPC_SLICE_LENGTH(a)); -} - -int grpc_slice_str_cmp(grpc_slice a, const char *b) { - size_t b_length = strlen(b); - int d = (int)(GRPC_SLICE_LENGTH(a) - b_length); - if (d != 0) return d; - return memcmp(GRPC_SLICE_START_PTR(a), b, b_length); -} - -int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b) { - if (a.refcount == NULL || b.refcount == NULL) { - return grpc_slice_eq(a, b); - } - return a.data.refcounted.length == b.data.refcounted.length && - a.data.refcounted.bytes == b.data.refcounted.bytes; -} - -int grpc_slice_buf_start_eq(grpc_slice a, const void *b, size_t len) { - if (GRPC_SLICE_LENGTH(a) < len) return 0; - return 0 == memcmp(GRPC_SLICE_START_PTR(a), b, len); -} - -int grpc_slice_rchr(grpc_slice s, char c) { - const char *b = (const char *)GRPC_SLICE_START_PTR(s); - int i; - for (i = (int)GRPC_SLICE_LENGTH(s) - 1; i != -1 && b[i] != c; i--) - ; - return i; -} - -int grpc_slice_chr(grpc_slice s, char c) { - const char *b = (const char *)GRPC_SLICE_START_PTR(s); - const char *p = (const char *)memchr(b, c, GRPC_SLICE_LENGTH(s)); - return p == NULL ? -1 : (int)(p - b); -} - -int grpc_slice_slice(grpc_slice haystack, grpc_slice needle) { - size_t haystack_len = GRPC_SLICE_LENGTH(haystack); - const uint8_t *haystack_bytes = GRPC_SLICE_START_PTR(haystack); - size_t needle_len = GRPC_SLICE_LENGTH(needle); - const uint8_t *needle_bytes = GRPC_SLICE_START_PTR(needle); - - if (haystack_len == 0 || needle_len == 0) return -1; - if (haystack_len < needle_len) return -1; - if (haystack_len == needle_len) - return grpc_slice_eq(haystack, needle) ? 0 : -1; - if (needle_len == 1) return grpc_slice_chr(haystack, (char)*needle_bytes); - - const uint8_t *last = haystack_bytes + haystack_len - needle_len; - for (const uint8_t *cur = haystack_bytes; cur != last; ++cur) { - if (0 == memcmp(cur, needle_bytes, needle_len)) { - return (int)(cur - haystack_bytes); - } - } - return -1; -} - -grpc_slice grpc_slice_dup(grpc_slice a) { - grpc_slice copy = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(a)); - memcpy(GRPC_SLICE_START_PTR(copy), GRPC_SLICE_START_PTR(a), - GRPC_SLICE_LENGTH(a)); - return copy; -} diff --git a/src/core/lib/slice/slice.cc b/src/core/lib/slice/slice.cc new file mode 100644 index 0000000000..0764eda052 --- /dev/null +++ b/src/core/lib/slice/slice.cc @@ -0,0 +1,486 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/slice/slice_internal.h" + +#include +#include +#include + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" + +char *grpc_slice_to_c_string(grpc_slice slice) { + char *out = (char *)gpr_malloc(GRPC_SLICE_LENGTH(slice) + 1); + memcpy(out, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice)); + out[GRPC_SLICE_LENGTH(slice)] = 0; + return out; +} + +grpc_slice grpc_empty_slice(void) { + grpc_slice out; + out.refcount = NULL; + out.data.inlined.length = 0; + return out; +} + +grpc_slice grpc_slice_copy(grpc_slice s) { + grpc_slice out = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(s)); + memcpy(GRPC_SLICE_START_PTR(out), GRPC_SLICE_START_PTR(s), + GRPC_SLICE_LENGTH(s)); + return out; +} + +grpc_slice grpc_slice_ref_internal(grpc_slice slice) { + if (slice.refcount) { + slice.refcount->vtable->ref(slice.refcount); + } + return slice; +} + +void grpc_slice_unref_internal(grpc_exec_ctx *exec_ctx, grpc_slice slice) { + if (slice.refcount) { + slice.refcount->vtable->unref(exec_ctx, slice.refcount); + } +} + +/* Public API */ +grpc_slice grpc_slice_ref(grpc_slice slice) { + return grpc_slice_ref_internal(slice); +} + +/* Public API */ +void grpc_slice_unref(grpc_slice slice) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_unref_internal(&exec_ctx, slice); + grpc_exec_ctx_finish(&exec_ctx); +} + +/* grpc_slice_from_static_string support structure - a refcount that does + nothing */ +static void noop_ref(void *unused) {} +static void noop_unref(grpc_exec_ctx *exec_ctx, void *unused) {} + +static const grpc_slice_refcount_vtable noop_refcount_vtable = { + noop_ref, noop_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; +static grpc_slice_refcount noop_refcount = {&noop_refcount_vtable, + &noop_refcount}; + +grpc_slice grpc_slice_from_static_buffer(const void *s, size_t len) { + grpc_slice slice; + slice.refcount = &noop_refcount; + slice.data.refcounted.bytes = (uint8_t *)s; + slice.data.refcounted.length = len; + return slice; +} + +grpc_slice grpc_slice_from_static_string(const char *s) { + return grpc_slice_from_static_buffer(s, strlen(s)); +} + +/* grpc_slice_new support structures - we create a refcount object extended + with the user provided data pointer & destroy function */ +typedef struct new_slice_refcount { + grpc_slice_refcount rc; + gpr_refcount refs; + void (*user_destroy)(void *); + void *user_data; +} new_slice_refcount; + +static void new_slice_ref(void *p) { + new_slice_refcount *r = (new_slice_refcount *)p; + gpr_ref(&r->refs); +} + +static void new_slice_unref(grpc_exec_ctx *exec_ctx, void *p) { + new_slice_refcount *r = (new_slice_refcount *)p; + if (gpr_unref(&r->refs)) { + r->user_destroy(r->user_data); + gpr_free(r); + } +} + +static const grpc_slice_refcount_vtable new_slice_vtable = { + new_slice_ref, new_slice_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +grpc_slice grpc_slice_new_with_user_data(void *p, size_t len, + void (*destroy)(void *), + void *user_data) { + grpc_slice slice; + new_slice_refcount *rc = + (new_slice_refcount *)gpr_malloc(sizeof(new_slice_refcount)); + gpr_ref_init(&rc->refs, 1); + rc->rc.vtable = &new_slice_vtable; + rc->rc.sub_refcount = &rc->rc; + rc->user_destroy = destroy; + rc->user_data = user_data; + + slice.refcount = &rc->rc; + slice.data.refcounted.bytes = (uint8_t *)p; + slice.data.refcounted.length = len; + return slice; +} + +grpc_slice grpc_slice_new(void *p, size_t len, void (*destroy)(void *)) { + /* Pass "p" to *destroy when the slice is no longer needed. */ + return grpc_slice_new_with_user_data(p, len, destroy, p); +} + +/* grpc_slice_new_with_len support structures - we create a refcount object + extended with the user provided data pointer & destroy function */ +typedef struct new_with_len_slice_refcount { + grpc_slice_refcount rc; + gpr_refcount refs; + void *user_data; + size_t user_length; + void (*user_destroy)(void *, size_t); +} new_with_len_slice_refcount; + +static void new_with_len_ref(void *p) { + new_with_len_slice_refcount *r = (new_with_len_slice_refcount *)p; + gpr_ref(&r->refs); +} + +static void new_with_len_unref(grpc_exec_ctx *exec_ctx, void *p) { + new_with_len_slice_refcount *r = (new_with_len_slice_refcount *)p; + if (gpr_unref(&r->refs)) { + r->user_destroy(r->user_data, r->user_length); + gpr_free(r); + } +} + +static const grpc_slice_refcount_vtable new_with_len_vtable = { + new_with_len_ref, new_with_len_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +grpc_slice grpc_slice_new_with_len(void *p, size_t len, + void (*destroy)(void *, size_t)) { + grpc_slice slice; + new_with_len_slice_refcount *rc = (new_with_len_slice_refcount *)gpr_malloc( + sizeof(new_with_len_slice_refcount)); + gpr_ref_init(&rc->refs, 1); + rc->rc.vtable = &new_with_len_vtable; + rc->rc.sub_refcount = &rc->rc; + rc->user_destroy = destroy; + rc->user_data = p; + rc->user_length = len; + + slice.refcount = &rc->rc; + slice.data.refcounted.bytes = (uint8_t *)p; + slice.data.refcounted.length = len; + return slice; +} + +grpc_slice grpc_slice_from_copied_buffer(const char *source, size_t length) { + if (length == 0) return grpc_empty_slice(); + grpc_slice slice = GRPC_SLICE_MALLOC(length); + memcpy(GRPC_SLICE_START_PTR(slice), source, length); + return slice; +} + +grpc_slice grpc_slice_from_copied_string(const char *source) { + return grpc_slice_from_copied_buffer(source, strlen(source)); +} + +typedef struct { + grpc_slice_refcount base; + gpr_refcount refs; +} malloc_refcount; + +static void malloc_ref(void *p) { + malloc_refcount *r = (malloc_refcount *)p; + gpr_ref(&r->refs); +} + +static void malloc_unref(grpc_exec_ctx *exec_ctx, void *p) { + malloc_refcount *r = (malloc_refcount *)p; + if (gpr_unref(&r->refs)) { + gpr_free(r); + } +} + +static const grpc_slice_refcount_vtable malloc_vtable = { + malloc_ref, malloc_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; + +grpc_slice grpc_slice_malloc_large(size_t length) { + grpc_slice slice; + + /* Memory layout used by the slice created here: + + +-----------+----------------------------------------------------------+ + | refcount | bytes | + +-----------+----------------------------------------------------------+ + + refcount is a malloc_refcount + bytes is an array of bytes of the requested length + Both parts are placed in the same allocation returned from gpr_malloc */ + malloc_refcount *rc = + (malloc_refcount *)gpr_malloc(sizeof(malloc_refcount) + length); + + /* Initial refcount on rc is 1 - and it's up to the caller to release + this reference. */ + gpr_ref_init(&rc->refs, 1); + + rc->base.vtable = &malloc_vtable; + rc->base.sub_refcount = &rc->base; + + /* Build up the slice to be returned. */ + /* The slices refcount points back to the allocated block. */ + slice.refcount = &rc->base; + /* The data bytes are placed immediately after the refcount struct */ + slice.data.refcounted.bytes = (uint8_t *)(rc + 1); + /* And the length of the block is set to the requested length */ + slice.data.refcounted.length = length; + return slice; +} + +grpc_slice grpc_slice_malloc(size_t length) { + grpc_slice slice; + + if (length > sizeof(slice.data.inlined.bytes)) { + return grpc_slice_malloc_large(length); + } else { + /* small slice: just inline the data */ + slice.refcount = NULL; + slice.data.inlined.length = (uint8_t)length; + } + return slice; +} + +grpc_slice grpc_slice_sub_no_ref(grpc_slice source, size_t begin, size_t end) { + grpc_slice subset; + + GPR_ASSERT(end >= begin); + + if (source.refcount) { + /* Enforce preconditions */ + GPR_ASSERT(source.data.refcounted.length >= end); + + /* Build the result */ + subset.refcount = source.refcount->sub_refcount; + /* Point into the source array */ + subset.data.refcounted.bytes = source.data.refcounted.bytes + begin; + subset.data.refcounted.length = end - begin; + } else { + /* Enforce preconditions */ + GPR_ASSERT(source.data.inlined.length >= end); + subset.refcount = NULL; + subset.data.inlined.length = (uint8_t)(end - begin); + memcpy(subset.data.inlined.bytes, source.data.inlined.bytes + begin, + end - begin); + } + return subset; +} + +grpc_slice grpc_slice_sub(grpc_slice source, size_t begin, size_t end) { + grpc_slice subset; + + if (end - begin <= sizeof(subset.data.inlined.bytes)) { + subset.refcount = NULL; + subset.data.inlined.length = (uint8_t)(end - begin); + memcpy(subset.data.inlined.bytes, GRPC_SLICE_START_PTR(source) + begin, + end - begin); + } else { + subset = grpc_slice_sub_no_ref(source, begin, end); + /* Bump the refcount */ + subset.refcount->vtable->ref(subset.refcount); + } + return subset; +} + +grpc_slice grpc_slice_split_tail_maybe_ref(grpc_slice *source, size_t split, + grpc_slice_ref_whom ref_whom) { + grpc_slice tail; + + if (source->refcount == NULL) { + /* inlined data, copy it out */ + GPR_ASSERT(source->data.inlined.length >= split); + tail.refcount = NULL; + tail.data.inlined.length = (uint8_t)(source->data.inlined.length - split); + memcpy(tail.data.inlined.bytes, source->data.inlined.bytes + split, + tail.data.inlined.length); + source->data.inlined.length = (uint8_t)split; + } else { + size_t tail_length = source->data.refcounted.length - split; + GPR_ASSERT(source->data.refcounted.length >= split); + if (tail_length < sizeof(tail.data.inlined.bytes) && + ref_whom != GRPC_SLICE_REF_TAIL) { + /* Copy out the bytes - it'll be cheaper than refcounting */ + tail.refcount = NULL; + tail.data.inlined.length = (uint8_t)tail_length; + memcpy(tail.data.inlined.bytes, source->data.refcounted.bytes + split, + tail_length); + source->refcount = source->refcount->sub_refcount; + } else { + /* Build the result */ + switch (ref_whom) { + case GRPC_SLICE_REF_TAIL: + tail.refcount = source->refcount->sub_refcount; + source->refcount = &noop_refcount; + break; + case GRPC_SLICE_REF_HEAD: + tail.refcount = &noop_refcount; + source->refcount = source->refcount->sub_refcount; + break; + case GRPC_SLICE_REF_BOTH: + tail.refcount = source->refcount->sub_refcount; + source->refcount = source->refcount->sub_refcount; + /* Bump the refcount */ + tail.refcount->vtable->ref(tail.refcount); + break; + } + /* Point into the source array */ + tail.data.refcounted.bytes = source->data.refcounted.bytes + split; + tail.data.refcounted.length = tail_length; + } + source->data.refcounted.length = split; + } + + return tail; +} + +grpc_slice grpc_slice_split_tail(grpc_slice *source, size_t split) { + return grpc_slice_split_tail_maybe_ref(source, split, GRPC_SLICE_REF_BOTH); +} + +grpc_slice grpc_slice_split_head(grpc_slice *source, size_t split) { + grpc_slice head; + + if (source->refcount == NULL) { + GPR_ASSERT(source->data.inlined.length >= split); + + head.refcount = NULL; + head.data.inlined.length = (uint8_t)split; + memcpy(head.data.inlined.bytes, source->data.inlined.bytes, split); + source->data.inlined.length = + (uint8_t)(source->data.inlined.length - split); + memmove(source->data.inlined.bytes, source->data.inlined.bytes + split, + source->data.inlined.length); + } else if (split < sizeof(head.data.inlined.bytes)) { + GPR_ASSERT(source->data.refcounted.length >= split); + + head.refcount = NULL; + head.data.inlined.length = (uint8_t)split; + memcpy(head.data.inlined.bytes, source->data.refcounted.bytes, split); + source->refcount = source->refcount->sub_refcount; + source->data.refcounted.bytes += split; + source->data.refcounted.length -= split; + } else { + GPR_ASSERT(source->data.refcounted.length >= split); + + /* Build the result */ + head.refcount = source->refcount->sub_refcount; + /* Bump the refcount */ + head.refcount->vtable->ref(head.refcount); + /* Point into the source array */ + head.data.refcounted.bytes = source->data.refcounted.bytes; + head.data.refcounted.length = split; + source->refcount = source->refcount->sub_refcount; + source->data.refcounted.bytes += split; + source->data.refcounted.length -= split; + } + + return head; +} + +int grpc_slice_default_eq_impl(grpc_slice a, grpc_slice b) { + if (GRPC_SLICE_LENGTH(a) != GRPC_SLICE_LENGTH(b)) return false; + if (GRPC_SLICE_LENGTH(a) == 0) return true; + return 0 == memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b), + GRPC_SLICE_LENGTH(a)); +} + +int grpc_slice_eq(grpc_slice a, grpc_slice b) { + if (a.refcount && b.refcount && a.refcount->vtable == b.refcount->vtable) { + return a.refcount->vtable->eq(a, b); + } + return grpc_slice_default_eq_impl(a, b); +} + +int grpc_slice_cmp(grpc_slice a, grpc_slice b) { + int d = (int)(GRPC_SLICE_LENGTH(a) - GRPC_SLICE_LENGTH(b)); + if (d != 0) return d; + return memcmp(GRPC_SLICE_START_PTR(a), GRPC_SLICE_START_PTR(b), + GRPC_SLICE_LENGTH(a)); +} + +int grpc_slice_str_cmp(grpc_slice a, const char *b) { + size_t b_length = strlen(b); + int d = (int)(GRPC_SLICE_LENGTH(a) - b_length); + if (d != 0) return d; + return memcmp(GRPC_SLICE_START_PTR(a), b, b_length); +} + +int grpc_slice_is_equivalent(grpc_slice a, grpc_slice b) { + if (a.refcount == NULL || b.refcount == NULL) { + return grpc_slice_eq(a, b); + } + return a.data.refcounted.length == b.data.refcounted.length && + a.data.refcounted.bytes == b.data.refcounted.bytes; +} + +int grpc_slice_buf_start_eq(grpc_slice a, const void *b, size_t len) { + if (GRPC_SLICE_LENGTH(a) < len) return 0; + return 0 == memcmp(GRPC_SLICE_START_PTR(a), b, len); +} + +int grpc_slice_rchr(grpc_slice s, char c) { + const char *b = (const char *)GRPC_SLICE_START_PTR(s); + int i; + for (i = (int)GRPC_SLICE_LENGTH(s) - 1; i != -1 && b[i] != c; i--) + ; + return i; +} + +int grpc_slice_chr(grpc_slice s, char c) { + const char *b = (const char *)GRPC_SLICE_START_PTR(s); + const char *p = (const char *)memchr(b, c, GRPC_SLICE_LENGTH(s)); + return p == NULL ? -1 : (int)(p - b); +} + +int grpc_slice_slice(grpc_slice haystack, grpc_slice needle) { + size_t haystack_len = GRPC_SLICE_LENGTH(haystack); + const uint8_t *haystack_bytes = GRPC_SLICE_START_PTR(haystack); + size_t needle_len = GRPC_SLICE_LENGTH(needle); + const uint8_t *needle_bytes = GRPC_SLICE_START_PTR(needle); + + if (haystack_len == 0 || needle_len == 0) return -1; + if (haystack_len < needle_len) return -1; + if (haystack_len == needle_len) + return grpc_slice_eq(haystack, needle) ? 0 : -1; + if (needle_len == 1) return grpc_slice_chr(haystack, (char)*needle_bytes); + + const uint8_t *last = haystack_bytes + haystack_len - needle_len; + for (const uint8_t *cur = haystack_bytes; cur != last; ++cur) { + if (0 == memcmp(cur, needle_bytes, needle_len)) { + return (int)(cur - haystack_bytes); + } + } + return -1; +} + +grpc_slice grpc_slice_dup(grpc_slice a) { + grpc_slice copy = GRPC_SLICE_MALLOC(GRPC_SLICE_LENGTH(a)); + memcpy(GRPC_SLICE_START_PTR(copy), GRPC_SLICE_START_PTR(a), + GRPC_SLICE_LENGTH(a)); + return copy; +} diff --git a/src/core/lib/slice/slice_buffer.c b/src/core/lib/slice/slice_buffer.c deleted file mode 100644 index 63ffc0b00d..0000000000 --- a/src/core/lib/slice/slice_buffer.c +++ /dev/null @@ -1,360 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include - -#include -#include -#include - -#include "src/core/lib/slice/slice_internal.h" - -/* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */ -#define GROW(x) (3 * (x) / 2) - -static void maybe_embiggen(grpc_slice_buffer *sb) { - /* How far away from sb->base_slices is sb->slices pointer */ - size_t slice_offset = (size_t)(sb->slices - sb->base_slices); - size_t slice_count = sb->count + slice_offset; - - if (slice_count == sb->capacity) { - if (sb->base_slices != sb->slices) { - /* Make room by moving elements if there's still space unused */ - memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice)); - sb->slices = sb->base_slices; - } else { - /* Allocate more memory if no more space is available */ - sb->capacity = GROW(sb->capacity); - GPR_ASSERT(sb->capacity > slice_count); - if (sb->base_slices == sb->inlined) { - sb->base_slices = - (grpc_slice *)gpr_malloc(sb->capacity * sizeof(grpc_slice)); - memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice)); - } else { - sb->base_slices = (grpc_slice *)gpr_realloc( - sb->base_slices, sb->capacity * sizeof(grpc_slice)); - } - - sb->slices = sb->base_slices + slice_offset; - } - } -} - -void grpc_slice_buffer_init(grpc_slice_buffer *sb) { - sb->count = 0; - sb->length = 0; - sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS; - sb->base_slices = sb->slices = sb->inlined; -} - -void grpc_slice_buffer_destroy_internal(grpc_exec_ctx *exec_ctx, - grpc_slice_buffer *sb) { - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, sb); - if (sb->base_slices != sb->inlined) { - gpr_free(sb->base_slices); - } -} - -void grpc_slice_buffer_destroy(grpc_slice_buffer *sb) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_buffer_destroy_internal(&exec_ctx, sb); - grpc_exec_ctx_finish(&exec_ctx); -} - -uint8_t *grpc_slice_buffer_tiny_add(grpc_slice_buffer *sb, size_t n) { - grpc_slice *back; - uint8_t *out; - - sb->length += n; - - if (sb->count == 0) goto add_new; - back = &sb->slices[sb->count - 1]; - if (back->refcount) goto add_new; - if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes)) - goto add_new; - out = back->data.inlined.bytes + back->data.inlined.length; - back->data.inlined.length = (uint8_t)(back->data.inlined.length + n); - return out; - -add_new: - maybe_embiggen(sb); - back = &sb->slices[sb->count]; - sb->count++; - back->refcount = NULL; - back->data.inlined.length = (uint8_t)n; - return back->data.inlined.bytes; -} - -size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer *sb, grpc_slice s) { - size_t out = sb->count; - maybe_embiggen(sb); - sb->slices[out] = s; - sb->length += GRPC_SLICE_LENGTH(s); - sb->count = out + 1; - return out; -} - -void grpc_slice_buffer_add(grpc_slice_buffer *sb, grpc_slice s) { - size_t n = sb->count; - /* if both the last slice in the slice buffer and the slice being added - are inlined (that is, that they carry their data inside the slice data - structure), and the back slice is not full, then concatenate directly - into the back slice, preventing many small slices being passed into - writes */ - if (!s.refcount && n) { - grpc_slice *back = &sb->slices[n - 1]; - if (!back->refcount && - back->data.inlined.length < GRPC_SLICE_INLINED_SIZE) { - if (s.data.inlined.length + back->data.inlined.length <= - GRPC_SLICE_INLINED_SIZE) { - memcpy(back->data.inlined.bytes + back->data.inlined.length, - s.data.inlined.bytes, s.data.inlined.length); - back->data.inlined.length = - (uint8_t)(back->data.inlined.length + s.data.inlined.length); - } else { - size_t cp1 = GRPC_SLICE_INLINED_SIZE - back->data.inlined.length; - memcpy(back->data.inlined.bytes + back->data.inlined.length, - s.data.inlined.bytes, cp1); - back->data.inlined.length = GRPC_SLICE_INLINED_SIZE; - maybe_embiggen(sb); - back = &sb->slices[n]; - sb->count = n + 1; - back->refcount = NULL; - back->data.inlined.length = (uint8_t)(s.data.inlined.length - cp1); - memcpy(back->data.inlined.bytes, s.data.inlined.bytes + cp1, - s.data.inlined.length - cp1); - } - sb->length += s.data.inlined.length; - return; /* early out */ - } - } - grpc_slice_buffer_add_indexed(sb, s); -} - -void grpc_slice_buffer_addn(grpc_slice_buffer *sb, grpc_slice *s, size_t n) { - size_t i; - for (i = 0; i < n; i++) { - grpc_slice_buffer_add(sb, s[i]); - } -} - -void grpc_slice_buffer_pop(grpc_slice_buffer *sb) { - if (sb->count != 0) { - size_t count = --sb->count; - sb->length -= GRPC_SLICE_LENGTH(sb->slices[count]); - } -} - -void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx *exec_ctx, - grpc_slice_buffer *sb) { - size_t i; - for (i = 0; i < sb->count; i++) { - grpc_slice_unref_internal(exec_ctx, sb->slices[i]); - } - - sb->count = 0; - sb->length = 0; -} - -void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer *sb) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, sb); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_slice_buffer_swap(grpc_slice_buffer *a, grpc_slice_buffer *b) { - size_t a_offset = (size_t)(a->slices - a->base_slices); - size_t b_offset = (size_t)(b->slices - b->base_slices); - - size_t a_count = a->count + a_offset; - size_t b_count = b->count + b_offset; - - if (a->base_slices == a->inlined) { - if (b->base_slices == b->inlined) { - /* swap contents of inlined buffer */ - grpc_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS]; - memcpy(temp, a->base_slices, a_count * sizeof(grpc_slice)); - memcpy(a->base_slices, b->base_slices, b_count * sizeof(grpc_slice)); - memcpy(b->base_slices, temp, a_count * sizeof(grpc_slice)); - } else { - /* a is inlined, b is not - copy a inlined into b, fix pointers */ - a->base_slices = b->base_slices; - b->base_slices = b->inlined; - memcpy(b->base_slices, a->inlined, a_count * sizeof(grpc_slice)); - } - } else if (b->base_slices == b->inlined) { - /* b is inlined, a is not - copy b inlined int a, fix pointers */ - b->base_slices = a->base_slices; - a->base_slices = a->inlined; - memcpy(a->base_slices, b->inlined, b_count * sizeof(grpc_slice)); - } else { - /* no inlining: easy swap */ - GPR_SWAP(grpc_slice *, a->base_slices, b->base_slices); - } - - /* Update the slices pointers (cannot do a GPR_SWAP on slices fields here). - * Also note that since the base_slices pointers are already swapped we need - * use 'b_offset' for 'a->base_slices' and vice versa */ - a->slices = a->base_slices + b_offset; - b->slices = b->base_slices + a_offset; - - /* base_slices and slices fields are correctly set. Swap all other fields */ - GPR_SWAP(size_t, a->count, b->count); - GPR_SWAP(size_t, a->capacity, b->capacity); - GPR_SWAP(size_t, a->length, b->length); -} - -void grpc_slice_buffer_move_into(grpc_slice_buffer *src, - grpc_slice_buffer *dst) { - /* anything to move? */ - if (src->count == 0) { - return; - } - /* anything in dst? */ - if (dst->count == 0) { - grpc_slice_buffer_swap(src, dst); - return; - } - /* both buffers have data - copy, and reset src */ - grpc_slice_buffer_addn(dst, src->slices, src->count); - src->count = 0; - src->length = 0; -} - -static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer *src, size_t n, - grpc_slice_buffer *dst, - bool incref) { - GPR_ASSERT(src->length >= n); - if (src->length == n) { - grpc_slice_buffer_move_into(src, dst); - return; - } - - size_t output_len = dst->length + n; - size_t new_input_len = src->length - n; - - while (src->count > 0) { - grpc_slice slice = grpc_slice_buffer_take_first(src); - size_t slice_len = GRPC_SLICE_LENGTH(slice); - if (n > slice_len) { - grpc_slice_buffer_add(dst, slice); - n -= slice_len; - } else if (n == slice_len) { - grpc_slice_buffer_add(dst, slice); - break; - } else if (incref) { /* n < slice_len */ - grpc_slice_buffer_undo_take_first( - src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_BOTH)); - GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n); - grpc_slice_buffer_add(dst, slice); - break; - } else { /* n < slice_len */ - grpc_slice_buffer_undo_take_first( - src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_TAIL)); - GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n); - grpc_slice_buffer_add_indexed(dst, slice); - break; - } - } - GPR_ASSERT(dst->length == output_len); - GPR_ASSERT(src->length == new_input_len); - GPR_ASSERT(src->count > 0); -} - -void grpc_slice_buffer_move_first(grpc_slice_buffer *src, size_t n, - grpc_slice_buffer *dst) { - slice_buffer_move_first_maybe_ref(src, n, dst, true); -} - -void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer *src, size_t n, - grpc_slice_buffer *dst) { - slice_buffer_move_first_maybe_ref(src, n, dst, false); -} - -void grpc_slice_buffer_move_first_into_buffer(grpc_exec_ctx *exec_ctx, - grpc_slice_buffer *src, size_t n, - void *dst) { - char *dstp = (char *)dst; - GPR_ASSERT(src->length >= n); - - while (n > 0) { - grpc_slice slice = grpc_slice_buffer_take_first(src); - size_t slice_len = GRPC_SLICE_LENGTH(slice); - if (slice_len > n) { - memcpy(dstp, GRPC_SLICE_START_PTR(slice), n); - grpc_slice_buffer_undo_take_first( - src, grpc_slice_sub_no_ref(slice, n, slice_len)); - n = 0; - } else if (slice_len == n) { - memcpy(dstp, GRPC_SLICE_START_PTR(slice), n); - grpc_slice_unref_internal(exec_ctx, slice); - n = 0; - } else { - memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len); - dstp += slice_len; - n -= slice_len; - grpc_slice_unref_internal(exec_ctx, slice); - } - } -} - -void grpc_slice_buffer_trim_end(grpc_slice_buffer *sb, size_t n, - grpc_slice_buffer *garbage) { - GPR_ASSERT(n <= sb->length); - sb->length -= n; - for (;;) { - size_t idx = sb->count - 1; - grpc_slice slice = sb->slices[idx]; - size_t slice_len = GRPC_SLICE_LENGTH(slice); - if (slice_len > n) { - sb->slices[idx] = grpc_slice_split_head(&slice, slice_len - n); - grpc_slice_buffer_add_indexed(garbage, slice); - return; - } else if (slice_len == n) { - grpc_slice_buffer_add_indexed(garbage, slice); - sb->count = idx; - return; - } else { - grpc_slice_buffer_add_indexed(garbage, slice); - n -= slice_len; - sb->count = idx; - } - } -} - -grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer *sb) { - grpc_slice slice; - GPR_ASSERT(sb->count > 0); - slice = sb->slices[0]; - sb->slices++; - sb->count--; - sb->length -= GRPC_SLICE_LENGTH(slice); - - return slice; -} - -void grpc_slice_buffer_undo_take_first(grpc_slice_buffer *sb, - grpc_slice slice) { - sb->slices--; - sb->slices[0] = slice; - sb->count++; - sb->length += GRPC_SLICE_LENGTH(slice); -} diff --git a/src/core/lib/slice/slice_buffer.cc b/src/core/lib/slice/slice_buffer.cc new file mode 100644 index 0000000000..63ffc0b00d --- /dev/null +++ b/src/core/lib/slice/slice_buffer.cc @@ -0,0 +1,360 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include + +#include +#include +#include + +#include "src/core/lib/slice/slice_internal.h" + +/* grow a buffer; requires GRPC_SLICE_BUFFER_INLINE_ELEMENTS > 1 */ +#define GROW(x) (3 * (x) / 2) + +static void maybe_embiggen(grpc_slice_buffer *sb) { + /* How far away from sb->base_slices is sb->slices pointer */ + size_t slice_offset = (size_t)(sb->slices - sb->base_slices); + size_t slice_count = sb->count + slice_offset; + + if (slice_count == sb->capacity) { + if (sb->base_slices != sb->slices) { + /* Make room by moving elements if there's still space unused */ + memmove(sb->base_slices, sb->slices, sb->count * sizeof(grpc_slice)); + sb->slices = sb->base_slices; + } else { + /* Allocate more memory if no more space is available */ + sb->capacity = GROW(sb->capacity); + GPR_ASSERT(sb->capacity > slice_count); + if (sb->base_slices == sb->inlined) { + sb->base_slices = + (grpc_slice *)gpr_malloc(sb->capacity * sizeof(grpc_slice)); + memcpy(sb->base_slices, sb->inlined, slice_count * sizeof(grpc_slice)); + } else { + sb->base_slices = (grpc_slice *)gpr_realloc( + sb->base_slices, sb->capacity * sizeof(grpc_slice)); + } + + sb->slices = sb->base_slices + slice_offset; + } + } +} + +void grpc_slice_buffer_init(grpc_slice_buffer *sb) { + sb->count = 0; + sb->length = 0; + sb->capacity = GRPC_SLICE_BUFFER_INLINE_ELEMENTS; + sb->base_slices = sb->slices = sb->inlined; +} + +void grpc_slice_buffer_destroy_internal(grpc_exec_ctx *exec_ctx, + grpc_slice_buffer *sb) { + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, sb); + if (sb->base_slices != sb->inlined) { + gpr_free(sb->base_slices); + } +} + +void grpc_slice_buffer_destroy(grpc_slice_buffer *sb) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_buffer_destroy_internal(&exec_ctx, sb); + grpc_exec_ctx_finish(&exec_ctx); +} + +uint8_t *grpc_slice_buffer_tiny_add(grpc_slice_buffer *sb, size_t n) { + grpc_slice *back; + uint8_t *out; + + sb->length += n; + + if (sb->count == 0) goto add_new; + back = &sb->slices[sb->count - 1]; + if (back->refcount) goto add_new; + if ((back->data.inlined.length + n) > sizeof(back->data.inlined.bytes)) + goto add_new; + out = back->data.inlined.bytes + back->data.inlined.length; + back->data.inlined.length = (uint8_t)(back->data.inlined.length + n); + return out; + +add_new: + maybe_embiggen(sb); + back = &sb->slices[sb->count]; + sb->count++; + back->refcount = NULL; + back->data.inlined.length = (uint8_t)n; + return back->data.inlined.bytes; +} + +size_t grpc_slice_buffer_add_indexed(grpc_slice_buffer *sb, grpc_slice s) { + size_t out = sb->count; + maybe_embiggen(sb); + sb->slices[out] = s; + sb->length += GRPC_SLICE_LENGTH(s); + sb->count = out + 1; + return out; +} + +void grpc_slice_buffer_add(grpc_slice_buffer *sb, grpc_slice s) { + size_t n = sb->count; + /* if both the last slice in the slice buffer and the slice being added + are inlined (that is, that they carry their data inside the slice data + structure), and the back slice is not full, then concatenate directly + into the back slice, preventing many small slices being passed into + writes */ + if (!s.refcount && n) { + grpc_slice *back = &sb->slices[n - 1]; + if (!back->refcount && + back->data.inlined.length < GRPC_SLICE_INLINED_SIZE) { + if (s.data.inlined.length + back->data.inlined.length <= + GRPC_SLICE_INLINED_SIZE) { + memcpy(back->data.inlined.bytes + back->data.inlined.length, + s.data.inlined.bytes, s.data.inlined.length); + back->data.inlined.length = + (uint8_t)(back->data.inlined.length + s.data.inlined.length); + } else { + size_t cp1 = GRPC_SLICE_INLINED_SIZE - back->data.inlined.length; + memcpy(back->data.inlined.bytes + back->data.inlined.length, + s.data.inlined.bytes, cp1); + back->data.inlined.length = GRPC_SLICE_INLINED_SIZE; + maybe_embiggen(sb); + back = &sb->slices[n]; + sb->count = n + 1; + back->refcount = NULL; + back->data.inlined.length = (uint8_t)(s.data.inlined.length - cp1); + memcpy(back->data.inlined.bytes, s.data.inlined.bytes + cp1, + s.data.inlined.length - cp1); + } + sb->length += s.data.inlined.length; + return; /* early out */ + } + } + grpc_slice_buffer_add_indexed(sb, s); +} + +void grpc_slice_buffer_addn(grpc_slice_buffer *sb, grpc_slice *s, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + grpc_slice_buffer_add(sb, s[i]); + } +} + +void grpc_slice_buffer_pop(grpc_slice_buffer *sb) { + if (sb->count != 0) { + size_t count = --sb->count; + sb->length -= GRPC_SLICE_LENGTH(sb->slices[count]); + } +} + +void grpc_slice_buffer_reset_and_unref_internal(grpc_exec_ctx *exec_ctx, + grpc_slice_buffer *sb) { + size_t i; + for (i = 0; i < sb->count; i++) { + grpc_slice_unref_internal(exec_ctx, sb->slices[i]); + } + + sb->count = 0; + sb->length = 0; +} + +void grpc_slice_buffer_reset_and_unref(grpc_slice_buffer *sb) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_buffer_reset_and_unref_internal(&exec_ctx, sb); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_slice_buffer_swap(grpc_slice_buffer *a, grpc_slice_buffer *b) { + size_t a_offset = (size_t)(a->slices - a->base_slices); + size_t b_offset = (size_t)(b->slices - b->base_slices); + + size_t a_count = a->count + a_offset; + size_t b_count = b->count + b_offset; + + if (a->base_slices == a->inlined) { + if (b->base_slices == b->inlined) { + /* swap contents of inlined buffer */ + grpc_slice temp[GRPC_SLICE_BUFFER_INLINE_ELEMENTS]; + memcpy(temp, a->base_slices, a_count * sizeof(grpc_slice)); + memcpy(a->base_slices, b->base_slices, b_count * sizeof(grpc_slice)); + memcpy(b->base_slices, temp, a_count * sizeof(grpc_slice)); + } else { + /* a is inlined, b is not - copy a inlined into b, fix pointers */ + a->base_slices = b->base_slices; + b->base_slices = b->inlined; + memcpy(b->base_slices, a->inlined, a_count * sizeof(grpc_slice)); + } + } else if (b->base_slices == b->inlined) { + /* b is inlined, a is not - copy b inlined int a, fix pointers */ + b->base_slices = a->base_slices; + a->base_slices = a->inlined; + memcpy(a->base_slices, b->inlined, b_count * sizeof(grpc_slice)); + } else { + /* no inlining: easy swap */ + GPR_SWAP(grpc_slice *, a->base_slices, b->base_slices); + } + + /* Update the slices pointers (cannot do a GPR_SWAP on slices fields here). + * Also note that since the base_slices pointers are already swapped we need + * use 'b_offset' for 'a->base_slices' and vice versa */ + a->slices = a->base_slices + b_offset; + b->slices = b->base_slices + a_offset; + + /* base_slices and slices fields are correctly set. Swap all other fields */ + GPR_SWAP(size_t, a->count, b->count); + GPR_SWAP(size_t, a->capacity, b->capacity); + GPR_SWAP(size_t, a->length, b->length); +} + +void grpc_slice_buffer_move_into(grpc_slice_buffer *src, + grpc_slice_buffer *dst) { + /* anything to move? */ + if (src->count == 0) { + return; + } + /* anything in dst? */ + if (dst->count == 0) { + grpc_slice_buffer_swap(src, dst); + return; + } + /* both buffers have data - copy, and reset src */ + grpc_slice_buffer_addn(dst, src->slices, src->count); + src->count = 0; + src->length = 0; +} + +static void slice_buffer_move_first_maybe_ref(grpc_slice_buffer *src, size_t n, + grpc_slice_buffer *dst, + bool incref) { + GPR_ASSERT(src->length >= n); + if (src->length == n) { + grpc_slice_buffer_move_into(src, dst); + return; + } + + size_t output_len = dst->length + n; + size_t new_input_len = src->length - n; + + while (src->count > 0) { + grpc_slice slice = grpc_slice_buffer_take_first(src); + size_t slice_len = GRPC_SLICE_LENGTH(slice); + if (n > slice_len) { + grpc_slice_buffer_add(dst, slice); + n -= slice_len; + } else if (n == slice_len) { + grpc_slice_buffer_add(dst, slice); + break; + } else if (incref) { /* n < slice_len */ + grpc_slice_buffer_undo_take_first( + src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_BOTH)); + GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n); + grpc_slice_buffer_add(dst, slice); + break; + } else { /* n < slice_len */ + grpc_slice_buffer_undo_take_first( + src, grpc_slice_split_tail_maybe_ref(&slice, n, GRPC_SLICE_REF_TAIL)); + GPR_ASSERT(GRPC_SLICE_LENGTH(slice) == n); + grpc_slice_buffer_add_indexed(dst, slice); + break; + } + } + GPR_ASSERT(dst->length == output_len); + GPR_ASSERT(src->length == new_input_len); + GPR_ASSERT(src->count > 0); +} + +void grpc_slice_buffer_move_first(grpc_slice_buffer *src, size_t n, + grpc_slice_buffer *dst) { + slice_buffer_move_first_maybe_ref(src, n, dst, true); +} + +void grpc_slice_buffer_move_first_no_ref(grpc_slice_buffer *src, size_t n, + grpc_slice_buffer *dst) { + slice_buffer_move_first_maybe_ref(src, n, dst, false); +} + +void grpc_slice_buffer_move_first_into_buffer(grpc_exec_ctx *exec_ctx, + grpc_slice_buffer *src, size_t n, + void *dst) { + char *dstp = (char *)dst; + GPR_ASSERT(src->length >= n); + + while (n > 0) { + grpc_slice slice = grpc_slice_buffer_take_first(src); + size_t slice_len = GRPC_SLICE_LENGTH(slice); + if (slice_len > n) { + memcpy(dstp, GRPC_SLICE_START_PTR(slice), n); + grpc_slice_buffer_undo_take_first( + src, grpc_slice_sub_no_ref(slice, n, slice_len)); + n = 0; + } else if (slice_len == n) { + memcpy(dstp, GRPC_SLICE_START_PTR(slice), n); + grpc_slice_unref_internal(exec_ctx, slice); + n = 0; + } else { + memcpy(dstp, GRPC_SLICE_START_PTR(slice), slice_len); + dstp += slice_len; + n -= slice_len; + grpc_slice_unref_internal(exec_ctx, slice); + } + } +} + +void grpc_slice_buffer_trim_end(grpc_slice_buffer *sb, size_t n, + grpc_slice_buffer *garbage) { + GPR_ASSERT(n <= sb->length); + sb->length -= n; + for (;;) { + size_t idx = sb->count - 1; + grpc_slice slice = sb->slices[idx]; + size_t slice_len = GRPC_SLICE_LENGTH(slice); + if (slice_len > n) { + sb->slices[idx] = grpc_slice_split_head(&slice, slice_len - n); + grpc_slice_buffer_add_indexed(garbage, slice); + return; + } else if (slice_len == n) { + grpc_slice_buffer_add_indexed(garbage, slice); + sb->count = idx; + return; + } else { + grpc_slice_buffer_add_indexed(garbage, slice); + n -= slice_len; + sb->count = idx; + } + } +} + +grpc_slice grpc_slice_buffer_take_first(grpc_slice_buffer *sb) { + grpc_slice slice; + GPR_ASSERT(sb->count > 0); + slice = sb->slices[0]; + sb->slices++; + sb->count--; + sb->length -= GRPC_SLICE_LENGTH(slice); + + return slice; +} + +void grpc_slice_buffer_undo_take_first(grpc_slice_buffer *sb, + grpc_slice slice) { + sb->slices--; + sb->slices[0] = slice; + sb->count++; + sb->length += GRPC_SLICE_LENGTH(slice); +} diff --git a/src/core/lib/slice/slice_hash_table.c b/src/core/lib/slice/slice_hash_table.c deleted file mode 100644 index 6c2c9c201c..0000000000 --- a/src/core/lib/slice/slice_hash_table.c +++ /dev/null @@ -1,146 +0,0 @@ -// -// Copyright 2016 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/lib/slice/slice_hash_table.h" - -#include -#include - -#include -#include - -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/transport/metadata.h" - -struct grpc_slice_hash_table { - gpr_refcount refs; - void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value); - int (*value_cmp)(void* a, void* b); - size_t size; - size_t max_num_probes; - grpc_slice_hash_table_entry* entries; -}; - -static bool is_empty(grpc_slice_hash_table_entry* entry) { - return entry->value == NULL; -} - -static void grpc_slice_hash_table_add(grpc_slice_hash_table* table, - grpc_slice key, void* value) { - GPR_ASSERT(value != NULL); - const size_t hash = grpc_slice_hash(key); - for (size_t offset = 0; offset < table->size; ++offset) { - const size_t idx = (hash + offset) % table->size; - if (is_empty(&table->entries[idx])) { - table->entries[idx].key = key; - table->entries[idx].value = value; - // Keep track of the maximum number of probes needed, since this - // provides an upper bound for lookups. - if (offset > table->max_num_probes) table->max_num_probes = offset; - return; - } - } - GPR_ASSERT(false); // Table should never be full. -} - -grpc_slice_hash_table* grpc_slice_hash_table_create( - size_t num_entries, grpc_slice_hash_table_entry* entries, - void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value), - int (*value_cmp)(void* a, void* b)) { - grpc_slice_hash_table* table = - (grpc_slice_hash_table*)gpr_zalloc(sizeof(*table)); - gpr_ref_init(&table->refs, 1); - table->destroy_value = destroy_value; - table->value_cmp = value_cmp; - // Keep load factor low to improve performance of lookups. - table->size = num_entries * 2; - const size_t entry_size = sizeof(grpc_slice_hash_table_entry) * table->size; - table->entries = (grpc_slice_hash_table_entry*)gpr_zalloc(entry_size); - for (size_t i = 0; i < num_entries; ++i) { - grpc_slice_hash_table_entry* entry = &entries[i]; - grpc_slice_hash_table_add(table, entry->key, entry->value); - } - return table; -} - -grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table) { - if (table != NULL) gpr_ref(&table->refs); - return table; -} - -void grpc_slice_hash_table_unref(grpc_exec_ctx* exec_ctx, - grpc_slice_hash_table* table) { - if (table != NULL && gpr_unref(&table->refs)) { - for (size_t i = 0; i < table->size; ++i) { - grpc_slice_hash_table_entry* entry = &table->entries[i]; - if (!is_empty(entry)) { - grpc_slice_unref_internal(exec_ctx, entry->key); - table->destroy_value(exec_ctx, entry->value); - } - } - gpr_free(table->entries); - gpr_free(table); - } -} - -void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table, - const grpc_slice key) { - const size_t hash = grpc_slice_hash(key); - // We cap the number of probes at the max number recorded when - // populating the table. - for (size_t offset = 0; offset <= table->max_num_probes; ++offset) { - const size_t idx = (hash + offset) % table->size; - if (is_empty(&table->entries[idx])) break; - if (grpc_slice_eq(table->entries[idx].key, key)) { - return table->entries[idx].value; - } - } - return NULL; // Not found. -} - -static int pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); } -int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a, - const grpc_slice_hash_table* b) { - int (*const value_cmp_fn_a)(void* a, void* b) = - a->value_cmp != NULL ? a->value_cmp : pointer_cmp; - int (*const value_cmp_fn_b)(void* a, void* b) = - b->value_cmp != NULL ? b->value_cmp : pointer_cmp; - // Compare value_fns - const int value_fns_cmp = - GPR_ICMP((void*)value_cmp_fn_a, (void*)value_cmp_fn_b); - if (value_fns_cmp != 0) return value_fns_cmp; - // Compare sizes - if (a->size < b->size) return -1; - if (a->size > b->size) return 1; - // Compare rows. - for (size_t i = 0; i < a->size; ++i) { - if (is_empty(&a->entries[i])) { - if (!is_empty(&b->entries[i])) { - return -1; // a empty but b non-empty - } - continue; // both empty, no need to check key or value - } else if (is_empty(&b->entries[i])) { - return 1; // a non-empty but b empty - } - // neither entry is empty - const int key_cmp = grpc_slice_cmp(a->entries[i].key, b->entries[i].key); - if (key_cmp != 0) return key_cmp; - const int value_cmp = - value_cmp_fn_a(a->entries[i].value, b->entries[i].value); - if (value_cmp != 0) return value_cmp; - } - return 0; -} diff --git a/src/core/lib/slice/slice_hash_table.cc b/src/core/lib/slice/slice_hash_table.cc new file mode 100644 index 0000000000..6c2c9c201c --- /dev/null +++ b/src/core/lib/slice/slice_hash_table.cc @@ -0,0 +1,146 @@ +// +// Copyright 2016 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/lib/slice/slice_hash_table.h" + +#include +#include + +#include +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/transport/metadata.h" + +struct grpc_slice_hash_table { + gpr_refcount refs; + void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value); + int (*value_cmp)(void* a, void* b); + size_t size; + size_t max_num_probes; + grpc_slice_hash_table_entry* entries; +}; + +static bool is_empty(grpc_slice_hash_table_entry* entry) { + return entry->value == NULL; +} + +static void grpc_slice_hash_table_add(grpc_slice_hash_table* table, + grpc_slice key, void* value) { + GPR_ASSERT(value != NULL); + const size_t hash = grpc_slice_hash(key); + for (size_t offset = 0; offset < table->size; ++offset) { + const size_t idx = (hash + offset) % table->size; + if (is_empty(&table->entries[idx])) { + table->entries[idx].key = key; + table->entries[idx].value = value; + // Keep track of the maximum number of probes needed, since this + // provides an upper bound for lookups. + if (offset > table->max_num_probes) table->max_num_probes = offset; + return; + } + } + GPR_ASSERT(false); // Table should never be full. +} + +grpc_slice_hash_table* grpc_slice_hash_table_create( + size_t num_entries, grpc_slice_hash_table_entry* entries, + void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value), + int (*value_cmp)(void* a, void* b)) { + grpc_slice_hash_table* table = + (grpc_slice_hash_table*)gpr_zalloc(sizeof(*table)); + gpr_ref_init(&table->refs, 1); + table->destroy_value = destroy_value; + table->value_cmp = value_cmp; + // Keep load factor low to improve performance of lookups. + table->size = num_entries * 2; + const size_t entry_size = sizeof(grpc_slice_hash_table_entry) * table->size; + table->entries = (grpc_slice_hash_table_entry*)gpr_zalloc(entry_size); + for (size_t i = 0; i < num_entries; ++i) { + grpc_slice_hash_table_entry* entry = &entries[i]; + grpc_slice_hash_table_add(table, entry->key, entry->value); + } + return table; +} + +grpc_slice_hash_table* grpc_slice_hash_table_ref(grpc_slice_hash_table* table) { + if (table != NULL) gpr_ref(&table->refs); + return table; +} + +void grpc_slice_hash_table_unref(grpc_exec_ctx* exec_ctx, + grpc_slice_hash_table* table) { + if (table != NULL && gpr_unref(&table->refs)) { + for (size_t i = 0; i < table->size; ++i) { + grpc_slice_hash_table_entry* entry = &table->entries[i]; + if (!is_empty(entry)) { + grpc_slice_unref_internal(exec_ctx, entry->key); + table->destroy_value(exec_ctx, entry->value); + } + } + gpr_free(table->entries); + gpr_free(table); + } +} + +void* grpc_slice_hash_table_get(const grpc_slice_hash_table* table, + const grpc_slice key) { + const size_t hash = grpc_slice_hash(key); + // We cap the number of probes at the max number recorded when + // populating the table. + for (size_t offset = 0; offset <= table->max_num_probes; ++offset) { + const size_t idx = (hash + offset) % table->size; + if (is_empty(&table->entries[idx])) break; + if (grpc_slice_eq(table->entries[idx].key, key)) { + return table->entries[idx].value; + } + } + return NULL; // Not found. +} + +static int pointer_cmp(void* a, void* b) { return GPR_ICMP(a, b); } +int grpc_slice_hash_table_cmp(const grpc_slice_hash_table* a, + const grpc_slice_hash_table* b) { + int (*const value_cmp_fn_a)(void* a, void* b) = + a->value_cmp != NULL ? a->value_cmp : pointer_cmp; + int (*const value_cmp_fn_b)(void* a, void* b) = + b->value_cmp != NULL ? b->value_cmp : pointer_cmp; + // Compare value_fns + const int value_fns_cmp = + GPR_ICMP((void*)value_cmp_fn_a, (void*)value_cmp_fn_b); + if (value_fns_cmp != 0) return value_fns_cmp; + // Compare sizes + if (a->size < b->size) return -1; + if (a->size > b->size) return 1; + // Compare rows. + for (size_t i = 0; i < a->size; ++i) { + if (is_empty(&a->entries[i])) { + if (!is_empty(&b->entries[i])) { + return -1; // a empty but b non-empty + } + continue; // both empty, no need to check key or value + } else if (is_empty(&b->entries[i])) { + return 1; // a non-empty but b empty + } + // neither entry is empty + const int key_cmp = grpc_slice_cmp(a->entries[i].key, b->entries[i].key); + if (key_cmp != 0) return key_cmp; + const int value_cmp = + value_cmp_fn_a(a->entries[i].value, b->entries[i].value); + if (value_cmp != 0) return value_cmp; + } + return 0; +} diff --git a/src/core/lib/slice/slice_intern.c b/src/core/lib/slice/slice_intern.c deleted file mode 100644 index ec71b3ca1d..0000000000 --- a/src/core/lib/slice/slice_intern.c +++ /dev/null @@ -1,334 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/slice/slice_internal.h" - -#include - -#include -#include - -#include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */ -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/murmur_hash.h" -#include "src/core/lib/transport/static_metadata.h" - -#define LOG2_SHARD_COUNT 5 -#define SHARD_COUNT (1 << LOG2_SHARD_COUNT) -#define INITIAL_SHARD_CAPACITY 8 - -#define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity)) -#define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1)) - -typedef struct interned_slice_refcount { - grpc_slice_refcount base; - grpc_slice_refcount sub; - size_t length; - gpr_atm refcnt; - uint32_t hash; - struct interned_slice_refcount *bucket_next; -} interned_slice_refcount; - -typedef struct slice_shard { - gpr_mu mu; - interned_slice_refcount **strs; - size_t count; - size_t capacity; -} slice_shard; - -/* hash seed: decided at initialization time */ -static uint32_t g_hash_seed; -static int g_forced_hash_seed = 0; - -static slice_shard g_shards[SHARD_COUNT]; - -typedef struct { - uint32_t hash; - uint32_t idx; -} static_metadata_hash_ent; - -static static_metadata_hash_ent - static_metadata_hash[4 * GRPC_STATIC_MDSTR_COUNT]; -static uint32_t max_static_metadata_hash_probe; -static uint32_t static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT]; - -static void interned_slice_ref(void *p) { - interned_slice_refcount *s = (interned_slice_refcount *)p; - GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) > 0); -} - -static void interned_slice_destroy(interned_slice_refcount *s) { - slice_shard *shard = &g_shards[SHARD_IDX(s->hash)]; - gpr_mu_lock(&shard->mu); - GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt)); - interned_slice_refcount **prev_next; - interned_slice_refcount *cur; - for (prev_next = &shard->strs[TABLE_IDX(s->hash, shard->capacity)], - cur = *prev_next; - cur != s; prev_next = &cur->bucket_next, cur = cur->bucket_next) - ; - *prev_next = cur->bucket_next; - shard->count--; - gpr_free(s); - gpr_mu_unlock(&shard->mu); -} - -static void interned_slice_unref(grpc_exec_ctx *exec_ctx, void *p) { - interned_slice_refcount *s = (interned_slice_refcount *)p; - if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) { - interned_slice_destroy(s); - } -} - -static void interned_slice_sub_ref(void *p) { - interned_slice_ref(((char *)p) - offsetof(interned_slice_refcount, sub)); -} - -static void interned_slice_sub_unref(grpc_exec_ctx *exec_ctx, void *p) { - interned_slice_unref(exec_ctx, - ((char *)p) - offsetof(interned_slice_refcount, sub)); -} - -static uint32_t interned_slice_hash(grpc_slice slice) { - interned_slice_refcount *s = (interned_slice_refcount *)slice.refcount; - return s->hash; -} - -static int interned_slice_eq(grpc_slice a, grpc_slice b) { - return a.refcount == b.refcount; -} - -static const grpc_slice_refcount_vtable interned_slice_vtable = { - interned_slice_ref, interned_slice_unref, interned_slice_eq, - interned_slice_hash}; -static const grpc_slice_refcount_vtable interned_slice_sub_vtable = { - interned_slice_sub_ref, interned_slice_sub_unref, - grpc_slice_default_eq_impl, grpc_slice_default_hash_impl}; - -static void grow_shard(slice_shard *shard) { - size_t capacity = shard->capacity * 2; - size_t i; - interned_slice_refcount **strtab; - interned_slice_refcount *s, *next; - - GPR_TIMER_BEGIN("grow_strtab", 0); - - strtab = (interned_slice_refcount **)gpr_zalloc( - sizeof(interned_slice_refcount *) * capacity); - - for (i = 0; i < shard->capacity; i++) { - for (s = shard->strs[i]; s; s = next) { - size_t idx = TABLE_IDX(s->hash, capacity); - next = s->bucket_next; - s->bucket_next = strtab[idx]; - strtab[idx] = s; - } - } - - gpr_free(shard->strs); - shard->strs = strtab; - shard->capacity = capacity; - - GPR_TIMER_END("grow_strtab", 0); -} - -static grpc_slice materialize(interned_slice_refcount *s) { - grpc_slice slice; - slice.refcount = &s->base; - slice.data.refcounted.bytes = (uint8_t *)(s + 1); - slice.data.refcounted.length = s->length; - return slice; -} - -uint32_t grpc_slice_default_hash_impl(grpc_slice s) { - return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), - g_hash_seed); -} - -uint32_t grpc_static_slice_hash(grpc_slice s) { - return static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(s)]; -} - -int grpc_static_slice_eq(grpc_slice a, grpc_slice b) { - return GRPC_STATIC_METADATA_INDEX(a) == GRPC_STATIC_METADATA_INDEX(b); -} - -uint32_t grpc_slice_hash(grpc_slice s) { - return s.refcount == NULL ? grpc_slice_default_hash_impl(s) - : s.refcount->vtable->hash(s); -} - -grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice, - bool *returned_slice_is_different) { - if (GRPC_IS_STATIC_METADATA_STRING(slice)) { - return slice; - } - - uint32_t hash = grpc_slice_hash(slice); - for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { - static_metadata_hash_ent ent = - static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; - if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && - grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) { - *returned_slice_is_different = true; - return grpc_static_slice_table[ent.idx]; - } - } - - return slice; -} - -bool grpc_slice_is_interned(grpc_slice slice) { - return (slice.refcount && slice.refcount->vtable == &interned_slice_vtable) || - GRPC_IS_STATIC_METADATA_STRING(slice); -} - -grpc_slice grpc_slice_intern(grpc_slice slice) { - GPR_TIMER_BEGIN("grpc_slice_intern", 0); - if (GRPC_IS_STATIC_METADATA_STRING(slice)) { - GPR_TIMER_END("grpc_slice_intern", 0); - return slice; - } - - uint32_t hash = grpc_slice_hash(slice); - for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { - static_metadata_hash_ent ent = - static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; - if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && - grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) { - GPR_TIMER_END("grpc_slice_intern", 0); - return grpc_static_slice_table[ent.idx]; - } - } - - interned_slice_refcount *s; - slice_shard *shard = &g_shards[SHARD_IDX(hash)]; - - gpr_mu_lock(&shard->mu); - - /* search for an existing string */ - size_t idx = TABLE_IDX(hash, shard->capacity); - for (s = shard->strs[idx]; s; s = s->bucket_next) { - if (s->hash == hash && grpc_slice_eq(slice, materialize(s))) { - if (gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) == 0) { - /* If we get here, we've added a ref to something that was about to - * die - drop it immediately. - * The *only* possible path here (given the shard mutex) should be to - * drop from one ref back to zero - assert that with a CAS */ - GPR_ASSERT(gpr_atm_rel_cas(&s->refcnt, 1, 0)); - /* and treat this as if we were never here... sshhh */ - } else { - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_slice_intern", 0); - return materialize(s); - } - } - } - - /* not found: create a new string */ - /* string data goes after the internal_string header */ - s = (interned_slice_refcount *)gpr_malloc(sizeof(*s) + - GRPC_SLICE_LENGTH(slice)); - gpr_atm_rel_store(&s->refcnt, 1); - s->length = GRPC_SLICE_LENGTH(slice); - s->hash = hash; - s->base.vtable = &interned_slice_vtable; - s->base.sub_refcount = &s->sub; - s->sub.vtable = &interned_slice_sub_vtable; - s->sub.sub_refcount = &s->sub; - s->bucket_next = shard->strs[idx]; - shard->strs[idx] = s; - memcpy(s + 1, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice)); - - shard->count++; - - if (shard->count > shard->capacity * 2) { - grow_shard(shard); - } - - gpr_mu_unlock(&shard->mu); - - GPR_TIMER_END("grpc_slice_intern", 0); - return materialize(s); -} - -void grpc_test_only_set_slice_hash_seed(uint32_t seed) { - g_hash_seed = seed; - g_forced_hash_seed = 1; -} - -void grpc_slice_intern_init(void) { - if (!g_forced_hash_seed) { - g_hash_seed = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; - } - for (size_t i = 0; i < SHARD_COUNT; i++) { - slice_shard *shard = &g_shards[i]; - gpr_mu_init(&shard->mu); - shard->count = 0; - shard->capacity = INITIAL_SHARD_CAPACITY; - shard->strs = (interned_slice_refcount **)gpr_zalloc(sizeof(*shard->strs) * - shard->capacity); - } - for (size_t i = 0; i < GPR_ARRAY_SIZE(static_metadata_hash); i++) { - static_metadata_hash[i].hash = 0; - static_metadata_hash[i].idx = GRPC_STATIC_MDSTR_COUNT; - } - max_static_metadata_hash_probe = 0; - for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) { - static_metadata_hash_values[i] = - grpc_slice_default_hash_impl(grpc_static_slice_table[i]); - for (size_t j = 0; j < GPR_ARRAY_SIZE(static_metadata_hash); j++) { - size_t slot = (static_metadata_hash_values[i] + j) % - GPR_ARRAY_SIZE(static_metadata_hash); - if (static_metadata_hash[slot].idx == GRPC_STATIC_MDSTR_COUNT) { - static_metadata_hash[slot].hash = static_metadata_hash_values[i]; - static_metadata_hash[slot].idx = (uint32_t)i; - if (j > max_static_metadata_hash_probe) { - max_static_metadata_hash_probe = (uint32_t)j; - } - break; - } - } - } -} - -void grpc_slice_intern_shutdown(void) { - for (size_t i = 0; i < SHARD_COUNT; i++) { - slice_shard *shard = &g_shards[i]; - gpr_mu_destroy(&shard->mu); - /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ - if (shard->count != 0) { - gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked", - shard->count); - for (size_t j = 0; j < shard->capacity; j++) { - for (interned_slice_refcount *s = shard->strs[j]; s; - s = s->bucket_next) { - char *text = - grpc_dump_slice(materialize(s), GPR_DUMP_HEX | GPR_DUMP_ASCII); - gpr_log(GPR_DEBUG, "LEAKED: %s", text); - gpr_free(text); - } - } - if (grpc_iomgr_abort_on_leaks()) { - abort(); - } - } - gpr_free(shard->strs); - } -} diff --git a/src/core/lib/slice/slice_intern.cc b/src/core/lib/slice/slice_intern.cc new file mode 100644 index 0000000000..ec71b3ca1d --- /dev/null +++ b/src/core/lib/slice/slice_intern.cc @@ -0,0 +1,334 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/slice/slice_internal.h" + +#include + +#include +#include + +#include "src/core/lib/iomgr/iomgr_internal.h" /* for iomgr_abort_on_leaks() */ +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/murmur_hash.h" +#include "src/core/lib/transport/static_metadata.h" + +#define LOG2_SHARD_COUNT 5 +#define SHARD_COUNT (1 << LOG2_SHARD_COUNT) +#define INITIAL_SHARD_CAPACITY 8 + +#define TABLE_IDX(hash, capacity) (((hash) >> LOG2_SHARD_COUNT) % (capacity)) +#define SHARD_IDX(hash) ((hash) & ((1 << LOG2_SHARD_COUNT) - 1)) + +typedef struct interned_slice_refcount { + grpc_slice_refcount base; + grpc_slice_refcount sub; + size_t length; + gpr_atm refcnt; + uint32_t hash; + struct interned_slice_refcount *bucket_next; +} interned_slice_refcount; + +typedef struct slice_shard { + gpr_mu mu; + interned_slice_refcount **strs; + size_t count; + size_t capacity; +} slice_shard; + +/* hash seed: decided at initialization time */ +static uint32_t g_hash_seed; +static int g_forced_hash_seed = 0; + +static slice_shard g_shards[SHARD_COUNT]; + +typedef struct { + uint32_t hash; + uint32_t idx; +} static_metadata_hash_ent; + +static static_metadata_hash_ent + static_metadata_hash[4 * GRPC_STATIC_MDSTR_COUNT]; +static uint32_t max_static_metadata_hash_probe; +static uint32_t static_metadata_hash_values[GRPC_STATIC_MDSTR_COUNT]; + +static void interned_slice_ref(void *p) { + interned_slice_refcount *s = (interned_slice_refcount *)p; + GPR_ASSERT(gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) > 0); +} + +static void interned_slice_destroy(interned_slice_refcount *s) { + slice_shard *shard = &g_shards[SHARD_IDX(s->hash)]; + gpr_mu_lock(&shard->mu); + GPR_ASSERT(0 == gpr_atm_no_barrier_load(&s->refcnt)); + interned_slice_refcount **prev_next; + interned_slice_refcount *cur; + for (prev_next = &shard->strs[TABLE_IDX(s->hash, shard->capacity)], + cur = *prev_next; + cur != s; prev_next = &cur->bucket_next, cur = cur->bucket_next) + ; + *prev_next = cur->bucket_next; + shard->count--; + gpr_free(s); + gpr_mu_unlock(&shard->mu); +} + +static void interned_slice_unref(grpc_exec_ctx *exec_ctx, void *p) { + interned_slice_refcount *s = (interned_slice_refcount *)p; + if (1 == gpr_atm_full_fetch_add(&s->refcnt, -1)) { + interned_slice_destroy(s); + } +} + +static void interned_slice_sub_ref(void *p) { + interned_slice_ref(((char *)p) - offsetof(interned_slice_refcount, sub)); +} + +static void interned_slice_sub_unref(grpc_exec_ctx *exec_ctx, void *p) { + interned_slice_unref(exec_ctx, + ((char *)p) - offsetof(interned_slice_refcount, sub)); +} + +static uint32_t interned_slice_hash(grpc_slice slice) { + interned_slice_refcount *s = (interned_slice_refcount *)slice.refcount; + return s->hash; +} + +static int interned_slice_eq(grpc_slice a, grpc_slice b) { + return a.refcount == b.refcount; +} + +static const grpc_slice_refcount_vtable interned_slice_vtable = { + interned_slice_ref, interned_slice_unref, interned_slice_eq, + interned_slice_hash}; +static const grpc_slice_refcount_vtable interned_slice_sub_vtable = { + interned_slice_sub_ref, interned_slice_sub_unref, + grpc_slice_default_eq_impl, grpc_slice_default_hash_impl}; + +static void grow_shard(slice_shard *shard) { + size_t capacity = shard->capacity * 2; + size_t i; + interned_slice_refcount **strtab; + interned_slice_refcount *s, *next; + + GPR_TIMER_BEGIN("grow_strtab", 0); + + strtab = (interned_slice_refcount **)gpr_zalloc( + sizeof(interned_slice_refcount *) * capacity); + + for (i = 0; i < shard->capacity; i++) { + for (s = shard->strs[i]; s; s = next) { + size_t idx = TABLE_IDX(s->hash, capacity); + next = s->bucket_next; + s->bucket_next = strtab[idx]; + strtab[idx] = s; + } + } + + gpr_free(shard->strs); + shard->strs = strtab; + shard->capacity = capacity; + + GPR_TIMER_END("grow_strtab", 0); +} + +static grpc_slice materialize(interned_slice_refcount *s) { + grpc_slice slice; + slice.refcount = &s->base; + slice.data.refcounted.bytes = (uint8_t *)(s + 1); + slice.data.refcounted.length = s->length; + return slice; +} + +uint32_t grpc_slice_default_hash_impl(grpc_slice s) { + return gpr_murmur_hash3(GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), + g_hash_seed); +} + +uint32_t grpc_static_slice_hash(grpc_slice s) { + return static_metadata_hash_values[GRPC_STATIC_METADATA_INDEX(s)]; +} + +int grpc_static_slice_eq(grpc_slice a, grpc_slice b) { + return GRPC_STATIC_METADATA_INDEX(a) == GRPC_STATIC_METADATA_INDEX(b); +} + +uint32_t grpc_slice_hash(grpc_slice s) { + return s.refcount == NULL ? grpc_slice_default_hash_impl(s) + : s.refcount->vtable->hash(s); +} + +grpc_slice grpc_slice_maybe_static_intern(grpc_slice slice, + bool *returned_slice_is_different) { + if (GRPC_IS_STATIC_METADATA_STRING(slice)) { + return slice; + } + + uint32_t hash = grpc_slice_hash(slice); + for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { + static_metadata_hash_ent ent = + static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; + if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && + grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) { + *returned_slice_is_different = true; + return grpc_static_slice_table[ent.idx]; + } + } + + return slice; +} + +bool grpc_slice_is_interned(grpc_slice slice) { + return (slice.refcount && slice.refcount->vtable == &interned_slice_vtable) || + GRPC_IS_STATIC_METADATA_STRING(slice); +} + +grpc_slice grpc_slice_intern(grpc_slice slice) { + GPR_TIMER_BEGIN("grpc_slice_intern", 0); + if (GRPC_IS_STATIC_METADATA_STRING(slice)) { + GPR_TIMER_END("grpc_slice_intern", 0); + return slice; + } + + uint32_t hash = grpc_slice_hash(slice); + for (uint32_t i = 0; i <= max_static_metadata_hash_probe; i++) { + static_metadata_hash_ent ent = + static_metadata_hash[(hash + i) % GPR_ARRAY_SIZE(static_metadata_hash)]; + if (ent.hash == hash && ent.idx < GRPC_STATIC_MDSTR_COUNT && + grpc_slice_eq(grpc_static_slice_table[ent.idx], slice)) { + GPR_TIMER_END("grpc_slice_intern", 0); + return grpc_static_slice_table[ent.idx]; + } + } + + interned_slice_refcount *s; + slice_shard *shard = &g_shards[SHARD_IDX(hash)]; + + gpr_mu_lock(&shard->mu); + + /* search for an existing string */ + size_t idx = TABLE_IDX(hash, shard->capacity); + for (s = shard->strs[idx]; s; s = s->bucket_next) { + if (s->hash == hash && grpc_slice_eq(slice, materialize(s))) { + if (gpr_atm_no_barrier_fetch_add(&s->refcnt, 1) == 0) { + /* If we get here, we've added a ref to something that was about to + * die - drop it immediately. + * The *only* possible path here (given the shard mutex) should be to + * drop from one ref back to zero - assert that with a CAS */ + GPR_ASSERT(gpr_atm_rel_cas(&s->refcnt, 1, 0)); + /* and treat this as if we were never here... sshhh */ + } else { + gpr_mu_unlock(&shard->mu); + GPR_TIMER_END("grpc_slice_intern", 0); + return materialize(s); + } + } + } + + /* not found: create a new string */ + /* string data goes after the internal_string header */ + s = (interned_slice_refcount *)gpr_malloc(sizeof(*s) + + GRPC_SLICE_LENGTH(slice)); + gpr_atm_rel_store(&s->refcnt, 1); + s->length = GRPC_SLICE_LENGTH(slice); + s->hash = hash; + s->base.vtable = &interned_slice_vtable; + s->base.sub_refcount = &s->sub; + s->sub.vtable = &interned_slice_sub_vtable; + s->sub.sub_refcount = &s->sub; + s->bucket_next = shard->strs[idx]; + shard->strs[idx] = s; + memcpy(s + 1, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice)); + + shard->count++; + + if (shard->count > shard->capacity * 2) { + grow_shard(shard); + } + + gpr_mu_unlock(&shard->mu); + + GPR_TIMER_END("grpc_slice_intern", 0); + return materialize(s); +} + +void grpc_test_only_set_slice_hash_seed(uint32_t seed) { + g_hash_seed = seed; + g_forced_hash_seed = 1; +} + +void grpc_slice_intern_init(void) { + if (!g_forced_hash_seed) { + g_hash_seed = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; + } + for (size_t i = 0; i < SHARD_COUNT; i++) { + slice_shard *shard = &g_shards[i]; + gpr_mu_init(&shard->mu); + shard->count = 0; + shard->capacity = INITIAL_SHARD_CAPACITY; + shard->strs = (interned_slice_refcount **)gpr_zalloc(sizeof(*shard->strs) * + shard->capacity); + } + for (size_t i = 0; i < GPR_ARRAY_SIZE(static_metadata_hash); i++) { + static_metadata_hash[i].hash = 0; + static_metadata_hash[i].idx = GRPC_STATIC_MDSTR_COUNT; + } + max_static_metadata_hash_probe = 0; + for (size_t i = 0; i < GRPC_STATIC_MDSTR_COUNT; i++) { + static_metadata_hash_values[i] = + grpc_slice_default_hash_impl(grpc_static_slice_table[i]); + for (size_t j = 0; j < GPR_ARRAY_SIZE(static_metadata_hash); j++) { + size_t slot = (static_metadata_hash_values[i] + j) % + GPR_ARRAY_SIZE(static_metadata_hash); + if (static_metadata_hash[slot].idx == GRPC_STATIC_MDSTR_COUNT) { + static_metadata_hash[slot].hash = static_metadata_hash_values[i]; + static_metadata_hash[slot].idx = (uint32_t)i; + if (j > max_static_metadata_hash_probe) { + max_static_metadata_hash_probe = (uint32_t)j; + } + break; + } + } + } +} + +void grpc_slice_intern_shutdown(void) { + for (size_t i = 0; i < SHARD_COUNT; i++) { + slice_shard *shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ + if (shard->count != 0) { + gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata strings were leaked", + shard->count); + for (size_t j = 0; j < shard->capacity; j++) { + for (interned_slice_refcount *s = shard->strs[j]; s; + s = s->bucket_next) { + char *text = + grpc_dump_slice(materialize(s), GPR_DUMP_HEX | GPR_DUMP_ASCII); + gpr_log(GPR_DEBUG, "LEAKED: %s", text); + gpr_free(text); + } + } + if (grpc_iomgr_abort_on_leaks()) { + abort(); + } + } + gpr_free(shard->strs); + } +} diff --git a/src/core/lib/slice/slice_string_helpers.c b/src/core/lib/slice/slice_string_helpers.c deleted file mode 100644 index d461c474d2..0000000000 --- a/src/core/lib/slice/slice_string_helpers.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/slice/slice_string_helpers.h" - -#include - -#include - -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" - -char *grpc_dump_slice(grpc_slice s, uint32_t flags) { - return gpr_dump((const char *)GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), - flags); -} - -/** Finds the initial (\a begin) and final (\a end) offsets of the next - * substring from \a str + \a read_offset until the next \a sep or the end of \a - * str. - * - * Returns 1 and updates \a begin and \a end. Returns 0 otherwise. */ -static int slice_find_separator_offset(const grpc_slice str, const char *sep, - const size_t read_offset, size_t *begin, - size_t *end) { - size_t i; - const uint8_t *str_ptr = GRPC_SLICE_START_PTR(str) + read_offset; - const size_t str_len = GRPC_SLICE_LENGTH(str) - read_offset; - const size_t sep_len = strlen(sep); - if (str_len < sep_len) { - return 0; - } - - for (i = 0; i <= str_len - sep_len; i++) { - if (memcmp(str_ptr + i, sep, sep_len) == 0) { - *begin = read_offset; - *end = read_offset + i; - return 1; - } - } - return 0; -} - -void grpc_slice_split(grpc_slice str, const char *sep, grpc_slice_buffer *dst) { - const size_t sep_len = strlen(sep); - size_t begin, end; - - GPR_ASSERT(sep_len > 0); - - if (slice_find_separator_offset(str, sep, 0, &begin, &end) != 0) { - do { - grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end)); - } while (slice_find_separator_offset(str, sep, end + sep_len, &begin, - &end) != 0); - grpc_slice_buffer_add_indexed( - dst, grpc_slice_sub(str, end + sep_len, GRPC_SLICE_LENGTH(str))); - } else { /* no sep found, add whole input */ - grpc_slice_buffer_add_indexed(dst, grpc_slice_ref_internal(str)); - } -} - -bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t *result) { - return gpr_parse_bytes_to_uint32((const char *)GRPC_SLICE_START_PTR(str), - GRPC_SLICE_LENGTH(str), result) != 0; -} diff --git a/src/core/lib/slice/slice_string_helpers.cc b/src/core/lib/slice/slice_string_helpers.cc new file mode 100644 index 0000000000..d461c474d2 --- /dev/null +++ b/src/core/lib/slice/slice_string_helpers.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/slice/slice_string_helpers.h" + +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" + +char *grpc_dump_slice(grpc_slice s, uint32_t flags) { + return gpr_dump((const char *)GRPC_SLICE_START_PTR(s), GRPC_SLICE_LENGTH(s), + flags); +} + +/** Finds the initial (\a begin) and final (\a end) offsets of the next + * substring from \a str + \a read_offset until the next \a sep or the end of \a + * str. + * + * Returns 1 and updates \a begin and \a end. Returns 0 otherwise. */ +static int slice_find_separator_offset(const grpc_slice str, const char *sep, + const size_t read_offset, size_t *begin, + size_t *end) { + size_t i; + const uint8_t *str_ptr = GRPC_SLICE_START_PTR(str) + read_offset; + const size_t str_len = GRPC_SLICE_LENGTH(str) - read_offset; + const size_t sep_len = strlen(sep); + if (str_len < sep_len) { + return 0; + } + + for (i = 0; i <= str_len - sep_len; i++) { + if (memcmp(str_ptr + i, sep, sep_len) == 0) { + *begin = read_offset; + *end = read_offset + i; + return 1; + } + } + return 0; +} + +void grpc_slice_split(grpc_slice str, const char *sep, grpc_slice_buffer *dst) { + const size_t sep_len = strlen(sep); + size_t begin, end; + + GPR_ASSERT(sep_len > 0); + + if (slice_find_separator_offset(str, sep, 0, &begin, &end) != 0) { + do { + grpc_slice_buffer_add_indexed(dst, grpc_slice_sub(str, begin, end)); + } while (slice_find_separator_offset(str, sep, end + sep_len, &begin, + &end) != 0); + grpc_slice_buffer_add_indexed( + dst, grpc_slice_sub(str, end + sep_len, GRPC_SLICE_LENGTH(str))); + } else { /* no sep found, add whole input */ + grpc_slice_buffer_add_indexed(dst, grpc_slice_ref_internal(str)); + } +} + +bool grpc_parse_slice_to_uint32(grpc_slice str, uint32_t *result) { + return gpr_parse_bytes_to_uint32((const char *)GRPC_SLICE_START_PTR(str), + GRPC_SLICE_LENGTH(str), result) != 0; +} diff --git a/src/core/lib/support/alloc.c b/src/core/lib/support/alloc.c deleted file mode 100644 index 886d69d64c..0000000000 --- a/src/core/lib/support/alloc.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include -#include -#include "src/core/lib/profiling/timers.h" - -static void *zalloc_with_calloc(size_t sz) { return calloc(sz, 1); } - -static void *zalloc_with_gpr_malloc(size_t sz) { - void *p = gpr_malloc(sz); - memset(p, 0, sz); - return p; -} - -static gpr_allocation_functions g_alloc_functions = {malloc, zalloc_with_calloc, - realloc, free}; - -gpr_allocation_functions gpr_get_allocation_functions() { - return g_alloc_functions; -} - -void gpr_set_allocation_functions(gpr_allocation_functions functions) { - GPR_ASSERT(functions.malloc_fn != NULL); - GPR_ASSERT(functions.realloc_fn != NULL); - GPR_ASSERT(functions.free_fn != NULL); - if (functions.zalloc_fn == NULL) { - functions.zalloc_fn = zalloc_with_gpr_malloc; - } - g_alloc_functions = functions; -} - -void *gpr_malloc(size_t size) { - void *p; - if (size == 0) return NULL; - GPR_TIMER_BEGIN("gpr_malloc", 0); - p = g_alloc_functions.malloc_fn(size); - if (!p) { - abort(); - } - GPR_TIMER_END("gpr_malloc", 0); - return p; -} - -void *gpr_zalloc(size_t size) { - void *p; - if (size == 0) return NULL; - GPR_TIMER_BEGIN("gpr_zalloc", 0); - p = g_alloc_functions.zalloc_fn(size); - if (!p) { - abort(); - } - GPR_TIMER_END("gpr_zalloc", 0); - return p; -} - -void gpr_free(void *p) { - GPR_TIMER_BEGIN("gpr_free", 0); - g_alloc_functions.free_fn(p); - GPR_TIMER_END("gpr_free", 0); -} - -void *gpr_realloc(void *p, size_t size) { - if ((size == 0) && (p == NULL)) return NULL; - GPR_TIMER_BEGIN("gpr_realloc", 0); - p = g_alloc_functions.realloc_fn(p, size); - if (!p) { - abort(); - } - GPR_TIMER_END("gpr_realloc", 0); - return p; -} - -void *gpr_malloc_aligned(size_t size, size_t alignment_log) { - size_t alignment = ((size_t)1) << alignment_log; - size_t extra = alignment - 1 + sizeof(void *); - void *p = gpr_malloc(size + extra); - void **ret = (void **)(((uintptr_t)p + extra) & ~(alignment - 1)); - ret[-1] = p; - return (void *)ret; -} - -void gpr_free_aligned(void *ptr) { gpr_free(((void **)ptr)[-1]); } diff --git a/src/core/lib/support/alloc.cc b/src/core/lib/support/alloc.cc new file mode 100644 index 0000000000..886d69d64c --- /dev/null +++ b/src/core/lib/support/alloc.cc @@ -0,0 +1,102 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" + +static void *zalloc_with_calloc(size_t sz) { return calloc(sz, 1); } + +static void *zalloc_with_gpr_malloc(size_t sz) { + void *p = gpr_malloc(sz); + memset(p, 0, sz); + return p; +} + +static gpr_allocation_functions g_alloc_functions = {malloc, zalloc_with_calloc, + realloc, free}; + +gpr_allocation_functions gpr_get_allocation_functions() { + return g_alloc_functions; +} + +void gpr_set_allocation_functions(gpr_allocation_functions functions) { + GPR_ASSERT(functions.malloc_fn != NULL); + GPR_ASSERT(functions.realloc_fn != NULL); + GPR_ASSERT(functions.free_fn != NULL); + if (functions.zalloc_fn == NULL) { + functions.zalloc_fn = zalloc_with_gpr_malloc; + } + g_alloc_functions = functions; +} + +void *gpr_malloc(size_t size) { + void *p; + if (size == 0) return NULL; + GPR_TIMER_BEGIN("gpr_malloc", 0); + p = g_alloc_functions.malloc_fn(size); + if (!p) { + abort(); + } + GPR_TIMER_END("gpr_malloc", 0); + return p; +} + +void *gpr_zalloc(size_t size) { + void *p; + if (size == 0) return NULL; + GPR_TIMER_BEGIN("gpr_zalloc", 0); + p = g_alloc_functions.zalloc_fn(size); + if (!p) { + abort(); + } + GPR_TIMER_END("gpr_zalloc", 0); + return p; +} + +void gpr_free(void *p) { + GPR_TIMER_BEGIN("gpr_free", 0); + g_alloc_functions.free_fn(p); + GPR_TIMER_END("gpr_free", 0); +} + +void *gpr_realloc(void *p, size_t size) { + if ((size == 0) && (p == NULL)) return NULL; + GPR_TIMER_BEGIN("gpr_realloc", 0); + p = g_alloc_functions.realloc_fn(p, size); + if (!p) { + abort(); + } + GPR_TIMER_END("gpr_realloc", 0); + return p; +} + +void *gpr_malloc_aligned(size_t size, size_t alignment_log) { + size_t alignment = ((size_t)1) << alignment_log; + size_t extra = alignment - 1 + sizeof(void *); + void *p = gpr_malloc(size + extra); + void **ret = (void **)(((uintptr_t)p + extra) & ~(alignment - 1)); + ret[-1] = p; + return (void *)ret; +} + +void gpr_free_aligned(void *ptr) { gpr_free(((void **)ptr)[-1]); } diff --git a/src/core/lib/support/arena.c b/src/core/lib/support/arena.c deleted file mode 100644 index 9e0f73ae3d..0000000000 --- a/src/core/lib/support/arena.c +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/support/arena.h" -#include -#include -#include -#include - -#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ - (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) - -typedef struct zone { - size_t size_begin; - size_t size_end; - gpr_atm next_atm; -} zone; - -struct gpr_arena { - gpr_atm size_so_far; - zone initial_zone; -}; - -gpr_arena *gpr_arena_create(size_t initial_size) { - initial_size = ROUND_UP_TO_ALIGNMENT_SIZE(initial_size); - gpr_arena *a = (gpr_arena *)gpr_zalloc(sizeof(gpr_arena) + initial_size); - a->initial_zone.size_end = initial_size; - return a; -} - -size_t gpr_arena_destroy(gpr_arena *arena) { - gpr_atm size = gpr_atm_no_barrier_load(&arena->size_so_far); - zone *z = (zone *)gpr_atm_no_barrier_load(&arena->initial_zone.next_atm); - gpr_free(arena); - while (z) { - zone *next_z = (zone *)gpr_atm_no_barrier_load(&z->next_atm); - gpr_free(z); - z = next_z; - } - return (size_t)size; -} - -void *gpr_arena_alloc(gpr_arena *arena, size_t size) { - size = ROUND_UP_TO_ALIGNMENT_SIZE(size); - size_t start = - (size_t)gpr_atm_no_barrier_fetch_add(&arena->size_so_far, size); - zone *z = &arena->initial_zone; - while (start > z->size_end) { - zone *next_z = (zone *)gpr_atm_acq_load(&z->next_atm); - if (next_z == NULL) { - size_t next_z_size = (size_t)gpr_atm_no_barrier_load(&arena->size_so_far); - next_z = (zone *)gpr_zalloc(sizeof(zone) + next_z_size); - next_z->size_begin = z->size_end; - next_z->size_end = z->size_end + next_z_size; - if (!gpr_atm_rel_cas(&z->next_atm, (gpr_atm)NULL, (gpr_atm)next_z)) { - gpr_free(next_z); - next_z = (zone *)gpr_atm_acq_load(&z->next_atm); - } - } - z = next_z; - } - if (start + size > z->size_end) { - return gpr_arena_alloc(arena, size); - } - GPR_ASSERT(start >= z->size_begin); - GPR_ASSERT(start + size <= z->size_end); - return ((char *)(z + 1)) + start - z->size_begin; -} diff --git a/src/core/lib/support/arena.cc b/src/core/lib/support/arena.cc new file mode 100644 index 0000000000..9e0f73ae3d --- /dev/null +++ b/src/core/lib/support/arena.cc @@ -0,0 +1,83 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/support/arena.h" +#include +#include +#include +#include + +#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \ + (((x) + GPR_MAX_ALIGNMENT - 1u) & ~(GPR_MAX_ALIGNMENT - 1u)) + +typedef struct zone { + size_t size_begin; + size_t size_end; + gpr_atm next_atm; +} zone; + +struct gpr_arena { + gpr_atm size_so_far; + zone initial_zone; +}; + +gpr_arena *gpr_arena_create(size_t initial_size) { + initial_size = ROUND_UP_TO_ALIGNMENT_SIZE(initial_size); + gpr_arena *a = (gpr_arena *)gpr_zalloc(sizeof(gpr_arena) + initial_size); + a->initial_zone.size_end = initial_size; + return a; +} + +size_t gpr_arena_destroy(gpr_arena *arena) { + gpr_atm size = gpr_atm_no_barrier_load(&arena->size_so_far); + zone *z = (zone *)gpr_atm_no_barrier_load(&arena->initial_zone.next_atm); + gpr_free(arena); + while (z) { + zone *next_z = (zone *)gpr_atm_no_barrier_load(&z->next_atm); + gpr_free(z); + z = next_z; + } + return (size_t)size; +} + +void *gpr_arena_alloc(gpr_arena *arena, size_t size) { + size = ROUND_UP_TO_ALIGNMENT_SIZE(size); + size_t start = + (size_t)gpr_atm_no_barrier_fetch_add(&arena->size_so_far, size); + zone *z = &arena->initial_zone; + while (start > z->size_end) { + zone *next_z = (zone *)gpr_atm_acq_load(&z->next_atm); + if (next_z == NULL) { + size_t next_z_size = (size_t)gpr_atm_no_barrier_load(&arena->size_so_far); + next_z = (zone *)gpr_zalloc(sizeof(zone) + next_z_size); + next_z->size_begin = z->size_end; + next_z->size_end = z->size_end + next_z_size; + if (!gpr_atm_rel_cas(&z->next_atm, (gpr_atm)NULL, (gpr_atm)next_z)) { + gpr_free(next_z); + next_z = (zone *)gpr_atm_acq_load(&z->next_atm); + } + } + z = next_z; + } + if (start + size > z->size_end) { + return gpr_arena_alloc(arena, size); + } + GPR_ASSERT(start >= z->size_begin); + GPR_ASSERT(start + size <= z->size_end); + return ((char *)(z + 1)) + start - z->size_begin; +} diff --git a/src/core/lib/support/atm.c b/src/core/lib/support/atm.c deleted file mode 100644 index 2f37d62f76..0000000000 --- a/src/core/lib/support/atm.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm *value, gpr_atm delta, - gpr_atm min, gpr_atm max) { - gpr_atm current_value; - gpr_atm new_value; - do { - current_value = gpr_atm_no_barrier_load(value); - new_value = GPR_CLAMP(current_value + delta, min, max); - if (new_value == current_value) break; - } while (!gpr_atm_no_barrier_cas(value, current_value, new_value)); - return new_value; -} diff --git a/src/core/lib/support/atm.cc b/src/core/lib/support/atm.cc new file mode 100644 index 0000000000..2f37d62f76 --- /dev/null +++ b/src/core/lib/support/atm.cc @@ -0,0 +1,32 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +gpr_atm gpr_atm_no_barrier_clamped_add(gpr_atm *value, gpr_atm delta, + gpr_atm min, gpr_atm max) { + gpr_atm current_value; + gpr_atm new_value; + do { + current_value = gpr_atm_no_barrier_load(value); + new_value = GPR_CLAMP(current_value + delta, min, max); + if (new_value == current_value) break; + } while (!gpr_atm_no_barrier_cas(value, current_value, new_value)); + return new_value; +} diff --git a/src/core/lib/support/avl.c b/src/core/lib/support/avl.c deleted file mode 100644 index 0e28b24c98..0000000000 --- a/src/core/lib/support/avl.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include -#include -#include - -gpr_avl gpr_avl_create(const gpr_avl_vtable *vtable) { - gpr_avl out; - out.vtable = vtable; - out.root = NULL; - return out; -} - -static gpr_avl_node *ref_node(gpr_avl_node *node) { - if (node) { - gpr_ref(&node->refs); - } - return node; -} - -static void unref_node(const gpr_avl_vtable *vtable, gpr_avl_node *node, - void *user_data) { - if (node == NULL) { - return; - } - if (gpr_unref(&node->refs)) { - vtable->destroy_key(node->key, user_data); - vtable->destroy_value(node->value, user_data); - unref_node(vtable, node->left, user_data); - unref_node(vtable, node->right, user_data); - gpr_free(node); - } -} - -static long node_height(gpr_avl_node *node) { - return node == NULL ? 0 : node->height; -} - -#ifndef NDEBUG -static long calculate_height(gpr_avl_node *node) { - return node == NULL ? 0 : 1 + GPR_MAX(calculate_height(node->left), - calculate_height(node->right)); -} - -static gpr_avl_node *assert_invariants(gpr_avl_node *n) { - if (n == NULL) return NULL; - assert_invariants(n->left); - assert_invariants(n->right); - assert(calculate_height(n) == n->height); - assert(labs(node_height(n->left) - node_height(n->right)) <= 1); - return n; -} -#else -static gpr_avl_node *assert_invariants(gpr_avl_node *n) { return n; } -#endif - -gpr_avl_node *new_node(void *key, void *value, gpr_avl_node *left, - gpr_avl_node *right) { - gpr_avl_node *node = (gpr_avl_node *)gpr_malloc(sizeof(*node)); - gpr_ref_init(&node->refs, 1); - node->key = key; - node->value = value; - node->left = assert_invariants(left); - node->right = assert_invariants(right); - node->height = 1 + GPR_MAX(node_height(left), node_height(right)); - return node; -} - -static gpr_avl_node *get(const gpr_avl_vtable *vtable, gpr_avl_node *node, - void *key, void *user_data) { - long cmp; - - if (node == NULL) { - return NULL; - } - - cmp = vtable->compare_keys(node->key, key, user_data); - if (cmp == 0) { - return node; - } else if (cmp > 0) { - return get(vtable, node->left, key, user_data); - } else { - return get(vtable, node->right, key, user_data); - } -} - -void *gpr_avl_get(gpr_avl avl, void *key, void *user_data) { - gpr_avl_node *node = get(avl.vtable, avl.root, key, user_data); - return node ? node->value : NULL; -} - -int gpr_avl_maybe_get(gpr_avl avl, void *key, void **value, void *user_data) { - gpr_avl_node *node = get(avl.vtable, avl.root, key, user_data); - if (node != NULL) { - *value = node->value; - return 1; - } - return 0; -} - -static gpr_avl_node *rotate_left(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right, void *user_data) { - gpr_avl_node *n = new_node(vtable->copy_key(right->key, user_data), - vtable->copy_value(right->value, user_data), - new_node(key, value, left, ref_node(right->left)), - ref_node(right->right)); - unref_node(vtable, right, user_data); - return n; -} - -static gpr_avl_node *rotate_right(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right, void *user_data) { - gpr_avl_node *n = - new_node(vtable->copy_key(left->key, user_data), - vtable->copy_value(left->value, user_data), ref_node(left->left), - new_node(key, value, ref_node(left->right), right)); - unref_node(vtable, left, user_data); - return n; -} - -static gpr_avl_node *rotate_left_right(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right, void *user_data) { - /* rotate_right(..., rotate_left(left), right) */ - gpr_avl_node *n = - new_node(vtable->copy_key(left->right->key, user_data), - vtable->copy_value(left->right->value, user_data), - new_node(vtable->copy_key(left->key, user_data), - vtable->copy_value(left->value, user_data), - ref_node(left->left), ref_node(left->right->left)), - new_node(key, value, ref_node(left->right->right), right)); - unref_node(vtable, left, user_data); - return n; -} - -static gpr_avl_node *rotate_right_left(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right, void *user_data) { - /* rotate_left(..., left, rotate_right(right)) */ - gpr_avl_node *n = - new_node(vtable->copy_key(right->left->key, user_data), - vtable->copy_value(right->left->value, user_data), - new_node(key, value, left, ref_node(right->left->left)), - new_node(vtable->copy_key(right->key, user_data), - vtable->copy_value(right->value, user_data), - ref_node(right->left->right), ref_node(right->right))); - unref_node(vtable, right, user_data); - return n; -} - -static gpr_avl_node *rebalance(const gpr_avl_vtable *vtable, void *key, - void *value, gpr_avl_node *left, - gpr_avl_node *right, void *user_data) { - switch (node_height(left) - node_height(right)) { - case 2: - if (node_height(left->left) - node_height(left->right) == -1) { - return assert_invariants( - rotate_left_right(vtable, key, value, left, right, user_data)); - } else { - return assert_invariants( - rotate_right(vtable, key, value, left, right, user_data)); - } - case -2: - if (node_height(right->left) - node_height(right->right) == 1) { - return assert_invariants( - rotate_right_left(vtable, key, value, left, right, user_data)); - } else { - return assert_invariants( - rotate_left(vtable, key, value, left, right, user_data)); - } - default: - return assert_invariants(new_node(key, value, left, right)); - } -} - -static gpr_avl_node *add_key(const gpr_avl_vtable *vtable, gpr_avl_node *node, - void *key, void *value, void *user_data) { - long cmp; - if (node == NULL) { - return new_node(key, value, NULL, NULL); - } - cmp = vtable->compare_keys(node->key, key, user_data); - if (cmp == 0) { - return new_node(key, value, ref_node(node->left), ref_node(node->right)); - } else if (cmp > 0) { - return rebalance(vtable, vtable->copy_key(node->key, user_data), - vtable->copy_value(node->value, user_data), - add_key(vtable, node->left, key, value, user_data), - ref_node(node->right), user_data); - } else { - return rebalance( - vtable, vtable->copy_key(node->key, user_data), - vtable->copy_value(node->value, user_data), ref_node(node->left), - add_key(vtable, node->right, key, value, user_data), user_data); - } -} - -gpr_avl gpr_avl_add(gpr_avl avl, void *key, void *value, void *user_data) { - gpr_avl_node *old_root = avl.root; - avl.root = add_key(avl.vtable, avl.root, key, value, user_data); - assert_invariants(avl.root); - unref_node(avl.vtable, old_root, user_data); - return avl; -} - -static gpr_avl_node *in_order_head(gpr_avl_node *node) { - while (node->left != NULL) { - node = node->left; - } - return node; -} - -static gpr_avl_node *in_order_tail(gpr_avl_node *node) { - while (node->right != NULL) { - node = node->right; - } - return node; -} - -static gpr_avl_node *remove_key(const gpr_avl_vtable *vtable, - gpr_avl_node *node, void *key, - void *user_data) { - long cmp; - if (node == NULL) { - return NULL; - } - cmp = vtable->compare_keys(node->key, key, user_data); - if (cmp == 0) { - if (node->left == NULL) { - return ref_node(node->right); - } else if (node->right == NULL) { - return ref_node(node->left); - } else if (node->left->height < node->right->height) { - gpr_avl_node *h = in_order_head(node->right); - return rebalance( - vtable, vtable->copy_key(h->key, user_data), - vtable->copy_value(h->value, user_data), ref_node(node->left), - remove_key(vtable, node->right, h->key, user_data), user_data); - } else { - gpr_avl_node *h = in_order_tail(node->left); - return rebalance(vtable, vtable->copy_key(h->key, user_data), - vtable->copy_value(h->value, user_data), - remove_key(vtable, node->left, h->key, user_data), - ref_node(node->right), user_data); - } - } else if (cmp > 0) { - return rebalance(vtable, vtable->copy_key(node->key, user_data), - vtable->copy_value(node->value, user_data), - remove_key(vtable, node->left, key, user_data), - ref_node(node->right), user_data); - } else { - return rebalance( - vtable, vtable->copy_key(node->key, user_data), - vtable->copy_value(node->value, user_data), ref_node(node->left), - remove_key(vtable, node->right, key, user_data), user_data); - } -} - -gpr_avl gpr_avl_remove(gpr_avl avl, void *key, void *user_data) { - gpr_avl_node *old_root = avl.root; - avl.root = remove_key(avl.vtable, avl.root, key, user_data); - assert_invariants(avl.root); - unref_node(avl.vtable, old_root, user_data); - return avl; -} - -gpr_avl gpr_avl_ref(gpr_avl avl, void *user_data) { - ref_node(avl.root); - return avl; -} - -void gpr_avl_unref(gpr_avl avl, void *user_data) { - unref_node(avl.vtable, avl.root, user_data); -} - -int gpr_avl_is_empty(gpr_avl avl) { return avl.root == NULL; } diff --git a/src/core/lib/support/avl.cc b/src/core/lib/support/avl.cc new file mode 100644 index 0000000000..0e28b24c98 --- /dev/null +++ b/src/core/lib/support/avl.cc @@ -0,0 +1,299 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include + +gpr_avl gpr_avl_create(const gpr_avl_vtable *vtable) { + gpr_avl out; + out.vtable = vtable; + out.root = NULL; + return out; +} + +static gpr_avl_node *ref_node(gpr_avl_node *node) { + if (node) { + gpr_ref(&node->refs); + } + return node; +} + +static void unref_node(const gpr_avl_vtable *vtable, gpr_avl_node *node, + void *user_data) { + if (node == NULL) { + return; + } + if (gpr_unref(&node->refs)) { + vtable->destroy_key(node->key, user_data); + vtable->destroy_value(node->value, user_data); + unref_node(vtable, node->left, user_data); + unref_node(vtable, node->right, user_data); + gpr_free(node); + } +} + +static long node_height(gpr_avl_node *node) { + return node == NULL ? 0 : node->height; +} + +#ifndef NDEBUG +static long calculate_height(gpr_avl_node *node) { + return node == NULL ? 0 : 1 + GPR_MAX(calculate_height(node->left), + calculate_height(node->right)); +} + +static gpr_avl_node *assert_invariants(gpr_avl_node *n) { + if (n == NULL) return NULL; + assert_invariants(n->left); + assert_invariants(n->right); + assert(calculate_height(n) == n->height); + assert(labs(node_height(n->left) - node_height(n->right)) <= 1); + return n; +} +#else +static gpr_avl_node *assert_invariants(gpr_avl_node *n) { return n; } +#endif + +gpr_avl_node *new_node(void *key, void *value, gpr_avl_node *left, + gpr_avl_node *right) { + gpr_avl_node *node = (gpr_avl_node *)gpr_malloc(sizeof(*node)); + gpr_ref_init(&node->refs, 1); + node->key = key; + node->value = value; + node->left = assert_invariants(left); + node->right = assert_invariants(right); + node->height = 1 + GPR_MAX(node_height(left), node_height(right)); + return node; +} + +static gpr_avl_node *get(const gpr_avl_vtable *vtable, gpr_avl_node *node, + void *key, void *user_data) { + long cmp; + + if (node == NULL) { + return NULL; + } + + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + return node; + } else if (cmp > 0) { + return get(vtable, node->left, key, user_data); + } else { + return get(vtable, node->right, key, user_data); + } +} + +void *gpr_avl_get(gpr_avl avl, void *key, void *user_data) { + gpr_avl_node *node = get(avl.vtable, avl.root, key, user_data); + return node ? node->value : NULL; +} + +int gpr_avl_maybe_get(gpr_avl avl, void *key, void **value, void *user_data) { + gpr_avl_node *node = get(avl.vtable, avl.root, key, user_data); + if (node != NULL) { + *value = node->value; + return 1; + } + return 0; +} + +static gpr_avl_node *rotate_left(const gpr_avl_vtable *vtable, void *key, + void *value, gpr_avl_node *left, + gpr_avl_node *right, void *user_data) { + gpr_avl_node *n = new_node(vtable->copy_key(right->key, user_data), + vtable->copy_value(right->value, user_data), + new_node(key, value, left, ref_node(right->left)), + ref_node(right->right)); + unref_node(vtable, right, user_data); + return n; +} + +static gpr_avl_node *rotate_right(const gpr_avl_vtable *vtable, void *key, + void *value, gpr_avl_node *left, + gpr_avl_node *right, void *user_data) { + gpr_avl_node *n = + new_node(vtable->copy_key(left->key, user_data), + vtable->copy_value(left->value, user_data), ref_node(left->left), + new_node(key, value, ref_node(left->right), right)); + unref_node(vtable, left, user_data); + return n; +} + +static gpr_avl_node *rotate_left_right(const gpr_avl_vtable *vtable, void *key, + void *value, gpr_avl_node *left, + gpr_avl_node *right, void *user_data) { + /* rotate_right(..., rotate_left(left), right) */ + gpr_avl_node *n = + new_node(vtable->copy_key(left->right->key, user_data), + vtable->copy_value(left->right->value, user_data), + new_node(vtable->copy_key(left->key, user_data), + vtable->copy_value(left->value, user_data), + ref_node(left->left), ref_node(left->right->left)), + new_node(key, value, ref_node(left->right->right), right)); + unref_node(vtable, left, user_data); + return n; +} + +static gpr_avl_node *rotate_right_left(const gpr_avl_vtable *vtable, void *key, + void *value, gpr_avl_node *left, + gpr_avl_node *right, void *user_data) { + /* rotate_left(..., left, rotate_right(right)) */ + gpr_avl_node *n = + new_node(vtable->copy_key(right->left->key, user_data), + vtable->copy_value(right->left->value, user_data), + new_node(key, value, left, ref_node(right->left->left)), + new_node(vtable->copy_key(right->key, user_data), + vtable->copy_value(right->value, user_data), + ref_node(right->left->right), ref_node(right->right))); + unref_node(vtable, right, user_data); + return n; +} + +static gpr_avl_node *rebalance(const gpr_avl_vtable *vtable, void *key, + void *value, gpr_avl_node *left, + gpr_avl_node *right, void *user_data) { + switch (node_height(left) - node_height(right)) { + case 2: + if (node_height(left->left) - node_height(left->right) == -1) { + return assert_invariants( + rotate_left_right(vtable, key, value, left, right, user_data)); + } else { + return assert_invariants( + rotate_right(vtable, key, value, left, right, user_data)); + } + case -2: + if (node_height(right->left) - node_height(right->right) == 1) { + return assert_invariants( + rotate_right_left(vtable, key, value, left, right, user_data)); + } else { + return assert_invariants( + rotate_left(vtable, key, value, left, right, user_data)); + } + default: + return assert_invariants(new_node(key, value, left, right)); + } +} + +static gpr_avl_node *add_key(const gpr_avl_vtable *vtable, gpr_avl_node *node, + void *key, void *value, void *user_data) { + long cmp; + if (node == NULL) { + return new_node(key, value, NULL, NULL); + } + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + return new_node(key, value, ref_node(node->left), ref_node(node->right)); + } else if (cmp > 0) { + return rebalance(vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), + add_key(vtable, node->left, key, value, user_data), + ref_node(node->right), user_data); + } else { + return rebalance( + vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), ref_node(node->left), + add_key(vtable, node->right, key, value, user_data), user_data); + } +} + +gpr_avl gpr_avl_add(gpr_avl avl, void *key, void *value, void *user_data) { + gpr_avl_node *old_root = avl.root; + avl.root = add_key(avl.vtable, avl.root, key, value, user_data); + assert_invariants(avl.root); + unref_node(avl.vtable, old_root, user_data); + return avl; +} + +static gpr_avl_node *in_order_head(gpr_avl_node *node) { + while (node->left != NULL) { + node = node->left; + } + return node; +} + +static gpr_avl_node *in_order_tail(gpr_avl_node *node) { + while (node->right != NULL) { + node = node->right; + } + return node; +} + +static gpr_avl_node *remove_key(const gpr_avl_vtable *vtable, + gpr_avl_node *node, void *key, + void *user_data) { + long cmp; + if (node == NULL) { + return NULL; + } + cmp = vtable->compare_keys(node->key, key, user_data); + if (cmp == 0) { + if (node->left == NULL) { + return ref_node(node->right); + } else if (node->right == NULL) { + return ref_node(node->left); + } else if (node->left->height < node->right->height) { + gpr_avl_node *h = in_order_head(node->right); + return rebalance( + vtable, vtable->copy_key(h->key, user_data), + vtable->copy_value(h->value, user_data), ref_node(node->left), + remove_key(vtable, node->right, h->key, user_data), user_data); + } else { + gpr_avl_node *h = in_order_tail(node->left); + return rebalance(vtable, vtable->copy_key(h->key, user_data), + vtable->copy_value(h->value, user_data), + remove_key(vtable, node->left, h->key, user_data), + ref_node(node->right), user_data); + } + } else if (cmp > 0) { + return rebalance(vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), + remove_key(vtable, node->left, key, user_data), + ref_node(node->right), user_data); + } else { + return rebalance( + vtable, vtable->copy_key(node->key, user_data), + vtable->copy_value(node->value, user_data), ref_node(node->left), + remove_key(vtable, node->right, key, user_data), user_data); + } +} + +gpr_avl gpr_avl_remove(gpr_avl avl, void *key, void *user_data) { + gpr_avl_node *old_root = avl.root; + avl.root = remove_key(avl.vtable, avl.root, key, user_data); + assert_invariants(avl.root); + unref_node(avl.vtable, old_root, user_data); + return avl; +} + +gpr_avl gpr_avl_ref(gpr_avl avl, void *user_data) { + ref_node(avl.root); + return avl; +} + +void gpr_avl_unref(gpr_avl avl, void *user_data) { + unref_node(avl.vtable, avl.root, user_data); +} + +int gpr_avl_is_empty(gpr_avl avl) { return avl.root == NULL; } diff --git a/src/core/lib/support/backoff.c b/src/core/lib/support/backoff.c deleted file mode 100644 index 6dc0df473b..0000000000 --- a/src/core/lib/support/backoff.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/support/backoff.h" - -#include - -void gpr_backoff_init(gpr_backoff *backoff, int64_t initial_connect_timeout, - double multiplier, double jitter, - int64_t min_timeout_millis, int64_t max_timeout_millis) { - backoff->initial_connect_timeout = initial_connect_timeout; - backoff->multiplier = multiplier; - backoff->jitter = jitter; - backoff->min_timeout_millis = min_timeout_millis; - backoff->max_timeout_millis = max_timeout_millis; - backoff->rng_state = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; -} - -gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now) { - backoff->current_timeout_millis = backoff->initial_connect_timeout; - const int64_t first_timeout = - GPR_MAX(backoff->current_timeout_millis, backoff->min_timeout_millis); - return gpr_time_add(now, gpr_time_from_millis(first_timeout, GPR_TIMESPAN)); -} - -/* Generate a random number between 0 and 1. */ -static double generate_uniform_random_number(uint32_t *rng_state) { - *rng_state = (1103515245 * *rng_state + 12345) % ((uint32_t)1 << 31); - return *rng_state / (double)((uint32_t)1 << 31); -} - -gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now) { - const double new_timeout_millis = - backoff->multiplier * (double)backoff->current_timeout_millis; - backoff->current_timeout_millis = - GPR_MIN((int64_t)new_timeout_millis, backoff->max_timeout_millis); - - const double jitter_range_width = backoff->jitter * new_timeout_millis; - const double jitter = - (2 * generate_uniform_random_number(&backoff->rng_state) - 1) * - jitter_range_width; - - backoff->current_timeout_millis = - (int64_t)((double)(backoff->current_timeout_millis) + jitter); - - const gpr_timespec current_deadline = gpr_time_add( - now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN)); - - const gpr_timespec min_deadline = gpr_time_add( - now, gpr_time_from_millis(backoff->min_timeout_millis, GPR_TIMESPAN)); - - return gpr_time_max(current_deadline, min_deadline); -} - -void gpr_backoff_reset(gpr_backoff *backoff) { - backoff->current_timeout_millis = backoff->initial_connect_timeout; -} diff --git a/src/core/lib/support/backoff.cc b/src/core/lib/support/backoff.cc new file mode 100644 index 0000000000..6dc0df473b --- /dev/null +++ b/src/core/lib/support/backoff.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/support/backoff.h" + +#include + +void gpr_backoff_init(gpr_backoff *backoff, int64_t initial_connect_timeout, + double multiplier, double jitter, + int64_t min_timeout_millis, int64_t max_timeout_millis) { + backoff->initial_connect_timeout = initial_connect_timeout; + backoff->multiplier = multiplier; + backoff->jitter = jitter; + backoff->min_timeout_millis = min_timeout_millis; + backoff->max_timeout_millis = max_timeout_millis; + backoff->rng_state = (uint32_t)gpr_now(GPR_CLOCK_REALTIME).tv_nsec; +} + +gpr_timespec gpr_backoff_begin(gpr_backoff *backoff, gpr_timespec now) { + backoff->current_timeout_millis = backoff->initial_connect_timeout; + const int64_t first_timeout = + GPR_MAX(backoff->current_timeout_millis, backoff->min_timeout_millis); + return gpr_time_add(now, gpr_time_from_millis(first_timeout, GPR_TIMESPAN)); +} + +/* Generate a random number between 0 and 1. */ +static double generate_uniform_random_number(uint32_t *rng_state) { + *rng_state = (1103515245 * *rng_state + 12345) % ((uint32_t)1 << 31); + return *rng_state / (double)((uint32_t)1 << 31); +} + +gpr_timespec gpr_backoff_step(gpr_backoff *backoff, gpr_timespec now) { + const double new_timeout_millis = + backoff->multiplier * (double)backoff->current_timeout_millis; + backoff->current_timeout_millis = + GPR_MIN((int64_t)new_timeout_millis, backoff->max_timeout_millis); + + const double jitter_range_width = backoff->jitter * new_timeout_millis; + const double jitter = + (2 * generate_uniform_random_number(&backoff->rng_state) - 1) * + jitter_range_width; + + backoff->current_timeout_millis = + (int64_t)((double)(backoff->current_timeout_millis) + jitter); + + const gpr_timespec current_deadline = gpr_time_add( + now, gpr_time_from_millis(backoff->current_timeout_millis, GPR_TIMESPAN)); + + const gpr_timespec min_deadline = gpr_time_add( + now, gpr_time_from_millis(backoff->min_timeout_millis, GPR_TIMESPAN)); + + return gpr_time_max(current_deadline, min_deadline); +} + +void gpr_backoff_reset(gpr_backoff *backoff) { + backoff->current_timeout_millis = backoff->initial_connect_timeout; +} diff --git a/src/core/lib/support/cmdline.c b/src/core/lib/support/cmdline.c deleted file mode 100644 index 9fb80d4460..0000000000 --- a/src/core/lib/support/cmdline.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include "src/core/lib/support/string.h" - -typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype; - -typedef struct arg { - const char *name; - const char *help; - argtype type; - void *value; - struct arg *next; -} arg; - -struct gpr_cmdline { - const char *description; - arg *args; - const char *argv0; - - const char *extra_arg_name; - const char *extra_arg_help; - void (*extra_arg)(void *user_data, const char *arg); - void *extra_arg_user_data; - - int (*state)(gpr_cmdline *cl, char *arg); - arg *cur_arg; - - int survive_failure; -}; - -static int normal_state(gpr_cmdline *cl, char *arg); - -gpr_cmdline *gpr_cmdline_create(const char *description) { - gpr_cmdline *cl = (gpr_cmdline *)gpr_zalloc(sizeof(gpr_cmdline)); - - cl->description = description; - cl->state = normal_state; - - return cl; -} - -void gpr_cmdline_set_survive_failure(gpr_cmdline *cl) { - cl->survive_failure = 1; -} - -void gpr_cmdline_destroy(gpr_cmdline *cl) { - while (cl->args) { - arg *a = cl->args; - cl->args = a->next; - gpr_free(a); - } - gpr_free(cl); -} - -static void add_arg(gpr_cmdline *cl, const char *name, const char *help, - argtype type, void *value) { - arg *a; - - for (a = cl->args; a; a = a->next) { - GPR_ASSERT(0 != strcmp(a->name, name)); - } - - a = (arg *)gpr_zalloc(sizeof(arg)); - a->name = name; - a->help = help; - a->type = type; - a->value = value; - a->next = cl->args; - cl->args = a; -} - -void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, const char *help, - int *value) { - add_arg(cl, name, help, ARGTYPE_INT, value); -} - -void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, const char *help, - int *value) { - add_arg(cl, name, help, ARGTYPE_BOOL, value); -} - -void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, const char *help, - char **value) { - add_arg(cl, name, help, ARGTYPE_STRING, value); -} - -void gpr_cmdline_on_extra_arg( - gpr_cmdline *cl, const char *name, const char *help, - void (*on_extra_arg)(void *user_data, const char *arg), void *user_data) { - GPR_ASSERT(!cl->extra_arg); - GPR_ASSERT(on_extra_arg); - - cl->extra_arg = on_extra_arg; - cl->extra_arg_user_data = user_data; - cl->extra_arg_name = name; - cl->extra_arg_help = help; -} - -/* recursively descend argument list, adding the last element - to s first - so that arguments are added in the order they were - added to the list by api calls */ -static void add_args_to_usage(gpr_strvec *s, arg *a) { - char *tmp; - - if (!a) return; - add_args_to_usage(s, a->next); - - switch (a->type) { - case ARGTYPE_BOOL: - gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name); - gpr_strvec_add(s, tmp); - break; - case ARGTYPE_STRING: - gpr_asprintf(&tmp, " [--%s=string]", a->name); - gpr_strvec_add(s, tmp); - break; - case ARGTYPE_INT: - gpr_asprintf(&tmp, " [--%s=int]", a->name); - gpr_strvec_add(s, tmp); - break; - } -} - -char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0) { - /* TODO(ctiller): make this prettier */ - gpr_strvec s; - char *tmp; - const char *name = strrchr(argv0, '/'); - - if (name) { - name++; - } else { - name = argv0; - } - - gpr_strvec_init(&s); - - gpr_asprintf(&tmp, "Usage: %s", name); - gpr_strvec_add(&s, tmp); - add_args_to_usage(&s, cl->args); - if (cl->extra_arg) { - gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name); - gpr_strvec_add(&s, tmp); - } - gpr_strvec_add(&s, gpr_strdup("\n")); - - tmp = gpr_strvec_flatten(&s, NULL); - gpr_strvec_destroy(&s); - return tmp; -} - -static int print_usage_and_die(gpr_cmdline *cl) { - char *usage = gpr_cmdline_usage_string(cl, cl->argv0); - fprintf(stderr, "%s", usage); - gpr_free(usage); - if (!cl->survive_failure) { - exit(1); - } - return 0; -} - -static int extra_state(gpr_cmdline *cl, char *str) { - if (!cl->extra_arg) { - return print_usage_and_die(cl); - } - cl->extra_arg(cl->extra_arg_user_data, str); - return 1; -} - -static arg *find_arg(gpr_cmdline *cl, char *name) { - arg *a; - - for (a = cl->args; a; a = a->next) { - if (0 == strcmp(a->name, name)) { - break; - } - } - - if (!a) { - fprintf(stderr, "Unknown argument: %s\n", name); - return NULL; - } - - return a; -} - -static int value_state(gpr_cmdline *cl, char *str) { - long intval; - char *end; - - GPR_ASSERT(cl->cur_arg); - - switch (cl->cur_arg->type) { - case ARGTYPE_INT: - intval = strtol(str, &end, 0); - if (*end || intval < INT_MIN || intval > INT_MAX) { - fprintf(stderr, "expected integer, got '%s' for %s\n", str, - cl->cur_arg->name); - return print_usage_and_die(cl); - } - *(int *)cl->cur_arg->value = (int)intval; - break; - case ARGTYPE_BOOL: - if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) { - *(int *)cl->cur_arg->value = 1; - } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) { - *(int *)cl->cur_arg->value = 0; - } else { - fprintf(stderr, "expected boolean, got '%s' for %s\n", str, - cl->cur_arg->name); - return print_usage_and_die(cl); - } - break; - case ARGTYPE_STRING: - *(char **)cl->cur_arg->value = str; - break; - } - - cl->state = normal_state; - return 1; -} - -static int normal_state(gpr_cmdline *cl, char *str) { - char *eq = NULL; - char *tmp = NULL; - char *arg_name = NULL; - int r = 1; - - if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") || - 0 == strcmp(str, "-h")) { - return print_usage_and_die(cl); - } - - cl->cur_arg = NULL; - - if (str[0] == '-') { - if (str[1] == '-') { - if (str[2] == 0) { - /* handle '--' to move to just extra args */ - cl->state = extra_state; - return 1; - } - str += 2; - } else { - str += 1; - } - /* first byte of str is now past the leading '-' or '--' */ - if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') { - /* str is of the form '--no-foo' - it's a flag disable */ - str += 3; - cl->cur_arg = find_arg(cl, str); - if (cl->cur_arg == NULL) { - return print_usage_and_die(cl); - } - if (cl->cur_arg->type != ARGTYPE_BOOL) { - fprintf(stderr, "%s is not a flag argument\n", str); - return print_usage_and_die(cl); - } - *(int *)cl->cur_arg->value = 0; - return 1; /* early out */ - } - eq = strchr(str, '='); - if (eq != NULL) { - /* copy the string into a temp buffer and extract the name */ - tmp = arg_name = (char *)gpr_malloc((size_t)(eq - str + 1)); - memcpy(arg_name, str, (size_t)(eq - str)); - arg_name[eq - str] = 0; - } else { - arg_name = str; - } - cl->cur_arg = find_arg(cl, arg_name); - if (cl->cur_arg == NULL) { - return print_usage_and_die(cl); - } - if (eq != NULL) { - /* str was of the type --foo=value, parse the value */ - r = value_state(cl, eq + 1); - } else if (cl->cur_arg->type != ARGTYPE_BOOL) { - /* flag types don't have a '--foo value' variant, other types do */ - cl->state = value_state; - } else { - /* flag parameter: just set the value */ - *(int *)cl->cur_arg->value = 1; - } - } else { - r = extra_state(cl, str); - } - - gpr_free(tmp); - return r; -} - -int gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) { - int i; - - GPR_ASSERT(argc >= 1); - cl->argv0 = argv[0]; - - for (i = 1; i < argc; i++) { - if (!cl->state(cl, argv[i])) { - return 0; - } - } - return 1; -} diff --git a/src/core/lib/support/cmdline.cc b/src/core/lib/support/cmdline.cc new file mode 100644 index 0000000000..9fb80d4460 --- /dev/null +++ b/src/core/lib/support/cmdline.cc @@ -0,0 +1,330 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/support/string.h" + +typedef enum { ARGTYPE_INT, ARGTYPE_BOOL, ARGTYPE_STRING } argtype; + +typedef struct arg { + const char *name; + const char *help; + argtype type; + void *value; + struct arg *next; +} arg; + +struct gpr_cmdline { + const char *description; + arg *args; + const char *argv0; + + const char *extra_arg_name; + const char *extra_arg_help; + void (*extra_arg)(void *user_data, const char *arg); + void *extra_arg_user_data; + + int (*state)(gpr_cmdline *cl, char *arg); + arg *cur_arg; + + int survive_failure; +}; + +static int normal_state(gpr_cmdline *cl, char *arg); + +gpr_cmdline *gpr_cmdline_create(const char *description) { + gpr_cmdline *cl = (gpr_cmdline *)gpr_zalloc(sizeof(gpr_cmdline)); + + cl->description = description; + cl->state = normal_state; + + return cl; +} + +void gpr_cmdline_set_survive_failure(gpr_cmdline *cl) { + cl->survive_failure = 1; +} + +void gpr_cmdline_destroy(gpr_cmdline *cl) { + while (cl->args) { + arg *a = cl->args; + cl->args = a->next; + gpr_free(a); + } + gpr_free(cl); +} + +static void add_arg(gpr_cmdline *cl, const char *name, const char *help, + argtype type, void *value) { + arg *a; + + for (a = cl->args; a; a = a->next) { + GPR_ASSERT(0 != strcmp(a->name, name)); + } + + a = (arg *)gpr_zalloc(sizeof(arg)); + a->name = name; + a->help = help; + a->type = type; + a->value = value; + a->next = cl->args; + cl->args = a; +} + +void gpr_cmdline_add_int(gpr_cmdline *cl, const char *name, const char *help, + int *value) { + add_arg(cl, name, help, ARGTYPE_INT, value); +} + +void gpr_cmdline_add_flag(gpr_cmdline *cl, const char *name, const char *help, + int *value) { + add_arg(cl, name, help, ARGTYPE_BOOL, value); +} + +void gpr_cmdline_add_string(gpr_cmdline *cl, const char *name, const char *help, + char **value) { + add_arg(cl, name, help, ARGTYPE_STRING, value); +} + +void gpr_cmdline_on_extra_arg( + gpr_cmdline *cl, const char *name, const char *help, + void (*on_extra_arg)(void *user_data, const char *arg), void *user_data) { + GPR_ASSERT(!cl->extra_arg); + GPR_ASSERT(on_extra_arg); + + cl->extra_arg = on_extra_arg; + cl->extra_arg_user_data = user_data; + cl->extra_arg_name = name; + cl->extra_arg_help = help; +} + +/* recursively descend argument list, adding the last element + to s first - so that arguments are added in the order they were + added to the list by api calls */ +static void add_args_to_usage(gpr_strvec *s, arg *a) { + char *tmp; + + if (!a) return; + add_args_to_usage(s, a->next); + + switch (a->type) { + case ARGTYPE_BOOL: + gpr_asprintf(&tmp, " [--%s|--no-%s]", a->name, a->name); + gpr_strvec_add(s, tmp); + break; + case ARGTYPE_STRING: + gpr_asprintf(&tmp, " [--%s=string]", a->name); + gpr_strvec_add(s, tmp); + break; + case ARGTYPE_INT: + gpr_asprintf(&tmp, " [--%s=int]", a->name); + gpr_strvec_add(s, tmp); + break; + } +} + +char *gpr_cmdline_usage_string(gpr_cmdline *cl, const char *argv0) { + /* TODO(ctiller): make this prettier */ + gpr_strvec s; + char *tmp; + const char *name = strrchr(argv0, '/'); + + if (name) { + name++; + } else { + name = argv0; + } + + gpr_strvec_init(&s); + + gpr_asprintf(&tmp, "Usage: %s", name); + gpr_strvec_add(&s, tmp); + add_args_to_usage(&s, cl->args); + if (cl->extra_arg) { + gpr_asprintf(&tmp, " [%s...]", cl->extra_arg_name); + gpr_strvec_add(&s, tmp); + } + gpr_strvec_add(&s, gpr_strdup("\n")); + + tmp = gpr_strvec_flatten(&s, NULL); + gpr_strvec_destroy(&s); + return tmp; +} + +static int print_usage_and_die(gpr_cmdline *cl) { + char *usage = gpr_cmdline_usage_string(cl, cl->argv0); + fprintf(stderr, "%s", usage); + gpr_free(usage); + if (!cl->survive_failure) { + exit(1); + } + return 0; +} + +static int extra_state(gpr_cmdline *cl, char *str) { + if (!cl->extra_arg) { + return print_usage_and_die(cl); + } + cl->extra_arg(cl->extra_arg_user_data, str); + return 1; +} + +static arg *find_arg(gpr_cmdline *cl, char *name) { + arg *a; + + for (a = cl->args; a; a = a->next) { + if (0 == strcmp(a->name, name)) { + break; + } + } + + if (!a) { + fprintf(stderr, "Unknown argument: %s\n", name); + return NULL; + } + + return a; +} + +static int value_state(gpr_cmdline *cl, char *str) { + long intval; + char *end; + + GPR_ASSERT(cl->cur_arg); + + switch (cl->cur_arg->type) { + case ARGTYPE_INT: + intval = strtol(str, &end, 0); + if (*end || intval < INT_MIN || intval > INT_MAX) { + fprintf(stderr, "expected integer, got '%s' for %s\n", str, + cl->cur_arg->name); + return print_usage_and_die(cl); + } + *(int *)cl->cur_arg->value = (int)intval; + break; + case ARGTYPE_BOOL: + if (0 == strcmp(str, "1") || 0 == strcmp(str, "true")) { + *(int *)cl->cur_arg->value = 1; + } else if (0 == strcmp(str, "0") || 0 == strcmp(str, "false")) { + *(int *)cl->cur_arg->value = 0; + } else { + fprintf(stderr, "expected boolean, got '%s' for %s\n", str, + cl->cur_arg->name); + return print_usage_and_die(cl); + } + break; + case ARGTYPE_STRING: + *(char **)cl->cur_arg->value = str; + break; + } + + cl->state = normal_state; + return 1; +} + +static int normal_state(gpr_cmdline *cl, char *str) { + char *eq = NULL; + char *tmp = NULL; + char *arg_name = NULL; + int r = 1; + + if (0 == strcmp(str, "-help") || 0 == strcmp(str, "--help") || + 0 == strcmp(str, "-h")) { + return print_usage_and_die(cl); + } + + cl->cur_arg = NULL; + + if (str[0] == '-') { + if (str[1] == '-') { + if (str[2] == 0) { + /* handle '--' to move to just extra args */ + cl->state = extra_state; + return 1; + } + str += 2; + } else { + str += 1; + } + /* first byte of str is now past the leading '-' or '--' */ + if (str[0] == 'n' && str[1] == 'o' && str[2] == '-') { + /* str is of the form '--no-foo' - it's a flag disable */ + str += 3; + cl->cur_arg = find_arg(cl, str); + if (cl->cur_arg == NULL) { + return print_usage_and_die(cl); + } + if (cl->cur_arg->type != ARGTYPE_BOOL) { + fprintf(stderr, "%s is not a flag argument\n", str); + return print_usage_and_die(cl); + } + *(int *)cl->cur_arg->value = 0; + return 1; /* early out */ + } + eq = strchr(str, '='); + if (eq != NULL) { + /* copy the string into a temp buffer and extract the name */ + tmp = arg_name = (char *)gpr_malloc((size_t)(eq - str + 1)); + memcpy(arg_name, str, (size_t)(eq - str)); + arg_name[eq - str] = 0; + } else { + arg_name = str; + } + cl->cur_arg = find_arg(cl, arg_name); + if (cl->cur_arg == NULL) { + return print_usage_and_die(cl); + } + if (eq != NULL) { + /* str was of the type --foo=value, parse the value */ + r = value_state(cl, eq + 1); + } else if (cl->cur_arg->type != ARGTYPE_BOOL) { + /* flag types don't have a '--foo value' variant, other types do */ + cl->state = value_state; + } else { + /* flag parameter: just set the value */ + *(int *)cl->cur_arg->value = 1; + } + } else { + r = extra_state(cl, str); + } + + gpr_free(tmp); + return r; +} + +int gpr_cmdline_parse(gpr_cmdline *cl, int argc, char **argv) { + int i; + + GPR_ASSERT(argc >= 1); + cl->argv0 = argv[0]; + + for (i = 1; i < argc; i++) { + if (!cl->state(cl, argv[i])) { + return 0; + } + } + return 1; +} diff --git a/src/core/lib/support/cpu_iphone.c b/src/core/lib/support/cpu_iphone.c deleted file mode 100644 index dfd69b9fd8..0000000000 --- a/src/core/lib/support/cpu_iphone.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_CPU_IPHONE - -/* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */ -unsigned gpr_cpu_num_cores(void) { return 1; } - -/* Most code that's using this is using it to shard across work queues. So - unless profiling shows it's a problem or there appears a way to detect the - currently running CPU core, let's have it shard the default way. - Note that the interface in cpu.h lets gpr_cpu_num_cores return 0, but doing - it makes it impossible for gpr_cpu_current_cpu to satisfy its stated range, - and some code might be relying on it. */ -unsigned gpr_cpu_current_cpu(void) { return 0; } - -#endif /* GPR_CPU_IPHONE */ diff --git a/src/core/lib/support/cpu_iphone.cc b/src/core/lib/support/cpu_iphone.cc new file mode 100644 index 0000000000..dfd69b9fd8 --- /dev/null +++ b/src/core/lib/support/cpu_iphone.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_CPU_IPHONE + +/* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */ +unsigned gpr_cpu_num_cores(void) { return 1; } + +/* Most code that's using this is using it to shard across work queues. So + unless profiling shows it's a problem or there appears a way to detect the + currently running CPU core, let's have it shard the default way. + Note that the interface in cpu.h lets gpr_cpu_num_cores return 0, but doing + it makes it impossible for gpr_cpu_current_cpu to satisfy its stated range, + and some code might be relying on it. */ +unsigned gpr_cpu_current_cpu(void) { return 0; } + +#endif /* GPR_CPU_IPHONE */ diff --git a/src/core/lib/support/cpu_linux.c b/src/core/lib/support/cpu_linux.c deleted file mode 100644 index 2280668442..0000000000 --- a/src/core/lib/support/cpu_linux.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif /* _GNU_SOURCE */ - -#include - -#ifdef GPR_CPU_LINUX - -#include -#include -#include -#include - -#include -#include -#include - -static int ncpus = 0; - -static void init_num_cpus() { - /* This must be signed. sysconf returns -1 when the number cannot be - determined */ - ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN); - if (ncpus < 1) { - gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); - ncpus = 1; - } -} - -unsigned gpr_cpu_num_cores(void) { - static gpr_once once = GPR_ONCE_INIT; - gpr_once_init(&once, init_num_cpus); - return (unsigned)ncpus; -} - -unsigned gpr_cpu_current_cpu(void) { -#ifdef GPR_MUSL_LIBC_COMPAT - // sched_getcpu() is undefined on musl - return 0; -#else - int cpu = sched_getcpu(); - if (cpu < 0) { - gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); - return 0; - } - return (unsigned)cpu; -#endif -} - -#endif /* GPR_CPU_LINUX */ diff --git a/src/core/lib/support/cpu_linux.cc b/src/core/lib/support/cpu_linux.cc new file mode 100644 index 0000000000..2280668442 --- /dev/null +++ b/src/core/lib/support/cpu_linux.cc @@ -0,0 +1,68 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif /* _GNU_SOURCE */ + +#include + +#ifdef GPR_CPU_LINUX + +#include +#include +#include +#include + +#include +#include +#include + +static int ncpus = 0; + +static void init_num_cpus() { + /* This must be signed. sysconf returns -1 when the number cannot be + determined */ + ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 1) { + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + ncpus = 1; + } +} + +unsigned gpr_cpu_num_cores(void) { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_num_cpus); + return (unsigned)ncpus; +} + +unsigned gpr_cpu_current_cpu(void) { +#ifdef GPR_MUSL_LIBC_COMPAT + // sched_getcpu() is undefined on musl + return 0; +#else + int cpu = sched_getcpu(); + if (cpu < 0) { + gpr_log(GPR_ERROR, "Error determining current CPU: %s\n", strerror(errno)); + return 0; + } + return (unsigned)cpu; +#endif +} + +#endif /* GPR_CPU_LINUX */ diff --git a/src/core/lib/support/cpu_posix.c b/src/core/lib/support/cpu_posix.c deleted file mode 100644 index a1ba8202a8..0000000000 --- a/src/core/lib/support/cpu_posix.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_CPU_POSIX - -#include -#include -#include - -#include -#include -#include - -static __thread char magic_thread_local; - -static long ncpus = 0; - -static void init_ncpus() { - ncpus = sysconf(_SC_NPROCESSORS_ONLN); - if (ncpus < 1 || ncpus > INT32_MAX) { - gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); - ncpus = 1; - } -} - -unsigned gpr_cpu_num_cores(void) { - static gpr_once once = GPR_ONCE_INIT; - gpr_once_init(&once, init_ncpus); - return (unsigned)ncpus; -} - -unsigned gpr_cpu_current_cpu(void) { - /* NOTE: there's no way I know to return the actual cpu index portably... - most code that's using this is using it to shard across work queues though, - so here we use thread identity instead to achieve a similar though not - identical effect */ - return (unsigned)GPR_HASH_POINTER(&magic_thread_local, gpr_cpu_num_cores()); -} - -#endif /* GPR_CPU_POSIX */ diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc new file mode 100644 index 0000000000..a1ba8202a8 --- /dev/null +++ b/src/core/lib/support/cpu_posix.cc @@ -0,0 +1,57 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_CPU_POSIX + +#include +#include +#include + +#include +#include +#include + +static __thread char magic_thread_local; + +static long ncpus = 0; + +static void init_ncpus() { + ncpus = sysconf(_SC_NPROCESSORS_ONLN); + if (ncpus < 1 || ncpus > INT32_MAX) { + gpr_log(GPR_ERROR, "Cannot determine number of CPUs: assuming 1"); + ncpus = 1; + } +} + +unsigned gpr_cpu_num_cores(void) { + static gpr_once once = GPR_ONCE_INIT; + gpr_once_init(&once, init_ncpus); + return (unsigned)ncpus; +} + +unsigned gpr_cpu_current_cpu(void) { + /* NOTE: there's no way I know to return the actual cpu index portably... + most code that's using this is using it to shard across work queues though, + so here we use thread identity instead to achieve a similar though not + identical effect */ + return (unsigned)GPR_HASH_POINTER(&magic_thread_local, gpr_cpu_num_cores()); +} + +#endif /* GPR_CPU_POSIX */ diff --git a/src/core/lib/support/cpu_windows.c b/src/core/lib/support/cpu_windows.c deleted file mode 100644 index af26ff3679..0000000000 --- a/src/core/lib/support/cpu_windows.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_WINDOWS -#include - -unsigned gpr_cpu_num_cores(void) { - SYSTEM_INFO si; - GetSystemInfo(&si); - return si.dwNumberOfProcessors; -} - -unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); } - -#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/cpu_windows.cc b/src/core/lib/support/cpu_windows.cc new file mode 100644 index 0000000000..af26ff3679 --- /dev/null +++ b/src/core/lib/support/cpu_windows.cc @@ -0,0 +1,32 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS +#include + +unsigned gpr_cpu_num_cores(void) { + SYSTEM_INFO si; + GetSystemInfo(&si); + return si.dwNumberOfProcessors; +} + +unsigned gpr_cpu_current_cpu(void) { return GetCurrentProcessorNumber(); } + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/env_linux.c b/src/core/lib/support/env_linux.c deleted file mode 100644 index 4c45a977ca..0000000000 --- a/src/core/lib/support/env_linux.c +++ /dev/null @@ -1,82 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* for secure_getenv. */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include - -#ifdef GPR_LINUX_ENV - -#include "src/core/lib/support/env.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/support/string.h" - -const char *gpr_getenv_silent(const char *name, char **dst) { - const char *insecure_func_used = NULL; - char *result = NULL; -#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) - typedef char *(*getenv_type)(const char *); - static getenv_type getenv_func = NULL; - /* Check to see which getenv variant is supported (go from most - * to least secure) */ - const char *names[] = {"secure_getenv", "__secure_getenv", "getenv"}; - for (size_t i = 0; getenv_func == NULL && i < GPR_ARRAY_SIZE(names); i++) { - getenv_func = (getenv_type)dlsym(RTLD_DEFAULT, names[i]); - if (getenv_func != NULL && strstr(names[i], "secure") == NULL) { - insecure_func_used = names[i]; - } - } - result = getenv_func(name); -#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) - result = secure_getenv(name); -#else - result = getenv(name); - insecure_func_used = "getenv"; -#endif - *dst = result == NULL ? result : gpr_strdup(result); - return insecure_func_used; -} - -char *gpr_getenv(const char *name) { - char *result = NULL; - const char *insecure_func_used = gpr_getenv_silent(name, &result); - if (insecure_func_used != NULL) { - gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", - insecure_func_used); - } - return result; -} - -void gpr_setenv(const char *name, const char *value) { - int res = setenv(name, value, 1); - GPR_ASSERT(res == 0); -} - -#endif /* GPR_LINUX_ENV */ diff --git a/src/core/lib/support/env_linux.cc b/src/core/lib/support/env_linux.cc new file mode 100644 index 0000000000..4c45a977ca --- /dev/null +++ b/src/core/lib/support/env_linux.cc @@ -0,0 +1,82 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* for secure_getenv. */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_ENV + +#include "src/core/lib/support/env.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/support/string.h" + +const char *gpr_getenv_silent(const char *name, char **dst) { + const char *insecure_func_used = NULL; + char *result = NULL; +#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) + typedef char *(*getenv_type)(const char *); + static getenv_type getenv_func = NULL; + /* Check to see which getenv variant is supported (go from most + * to least secure) */ + const char *names[] = {"secure_getenv", "__secure_getenv", "getenv"}; + for (size_t i = 0; getenv_func == NULL && i < GPR_ARRAY_SIZE(names); i++) { + getenv_func = (getenv_type)dlsym(RTLD_DEFAULT, names[i]); + if (getenv_func != NULL && strstr(names[i], "secure") == NULL) { + insecure_func_used = names[i]; + } + } + result = getenv_func(name); +#elif __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) + result = secure_getenv(name); +#else + result = getenv(name); + insecure_func_used = "getenv"; +#endif + *dst = result == NULL ? result : gpr_strdup(result); + return insecure_func_used; +} + +char *gpr_getenv(const char *name) { + char *result = NULL; + const char *insecure_func_used = gpr_getenv_silent(name, &result); + if (insecure_func_used != NULL) { + gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", + insecure_func_used); + } + return result; +} + +void gpr_setenv(const char *name, const char *value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_LINUX_ENV */ diff --git a/src/core/lib/support/env_posix.c b/src/core/lib/support/env_posix.c deleted file mode 100644 index b88822ca02..0000000000 --- a/src/core/lib/support/env_posix.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_POSIX_ENV - -#include "src/core/lib/support/env.h" - -#include - -#include - -#include -#include "src/core/lib/support/string.h" - -const char *gpr_getenv_silent(const char *name, char **dst) { - *dst = gpr_getenv(name); - return NULL; -} - -char *gpr_getenv(const char *name) { - char *result = getenv(name); - return result == NULL ? result : gpr_strdup(result); -} - -void gpr_setenv(const char *name, const char *value) { - int res = setenv(name, value, 1); - GPR_ASSERT(res == 0); -} - -#endif /* GPR_POSIX_ENV */ diff --git a/src/core/lib/support/env_posix.cc b/src/core/lib/support/env_posix.cc new file mode 100644 index 0000000000..b88822ca02 --- /dev/null +++ b/src/core/lib/support/env_posix.cc @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_ENV + +#include "src/core/lib/support/env.h" + +#include + +#include + +#include +#include "src/core/lib/support/string.h" + +const char *gpr_getenv_silent(const char *name, char **dst) { + *dst = gpr_getenv(name); + return NULL; +} + +char *gpr_getenv(const char *name) { + char *result = getenv(name); + return result == NULL ? result : gpr_strdup(result); +} + +void gpr_setenv(const char *name, const char *value) { + int res = setenv(name, value, 1); + GPR_ASSERT(res == 0); +} + +#endif /* GPR_POSIX_ENV */ diff --git a/src/core/lib/support/env_windows.c b/src/core/lib/support/env_windows.c deleted file mode 100644 index 652eeb61c6..0000000000 --- a/src/core/lib/support/env_windows.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_WINDOWS_ENV - -#include - -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/support/string_windows.h" - -#include -#include -#include - -const char *gpr_getenv_silent(const char *name, char **dst) { - *dst = gpr_getenv(name); - return NULL; -} - -char *gpr_getenv(const char *name) { - char *result = NULL; - DWORD size; - LPTSTR tresult = NULL; - LPTSTR tname = gpr_char_to_tchar(name); - DWORD ret; - - ret = GetEnvironmentVariable(tname, NULL, 0); - if (ret == 0) return NULL; - size = ret * (DWORD)sizeof(TCHAR); - tresult = gpr_malloc(size); - ret = GetEnvironmentVariable(tname, tresult, size); - gpr_free(tname); - if (ret == 0) { - gpr_free(tresult); - return NULL; - } - result = gpr_tchar_to_char(tresult); - gpr_free(tresult); - return result; -} - -void gpr_setenv(const char *name, const char *value) { - LPTSTR tname = gpr_char_to_tchar(name); - LPTSTR tvalue = gpr_char_to_tchar(value); - BOOL res = SetEnvironmentVariable(tname, tvalue); - gpr_free(tname); - gpr_free(tvalue); - GPR_ASSERT(res); -} - -#endif /* GPR_WINDOWS_ENV */ diff --git a/src/core/lib/support/env_windows.cc b/src/core/lib/support/env_windows.cc new file mode 100644 index 0000000000..652eeb61c6 --- /dev/null +++ b/src/core/lib/support/env_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_ENV + +#include + +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/support/string_windows.h" + +#include +#include +#include + +const char *gpr_getenv_silent(const char *name, char **dst) { + *dst = gpr_getenv(name); + return NULL; +} + +char *gpr_getenv(const char *name) { + char *result = NULL; + DWORD size; + LPTSTR tresult = NULL; + LPTSTR tname = gpr_char_to_tchar(name); + DWORD ret; + + ret = GetEnvironmentVariable(tname, NULL, 0); + if (ret == 0) return NULL; + size = ret * (DWORD)sizeof(TCHAR); + tresult = gpr_malloc(size); + ret = GetEnvironmentVariable(tname, tresult, size); + gpr_free(tname); + if (ret == 0) { + gpr_free(tresult); + return NULL; + } + result = gpr_tchar_to_char(tresult); + gpr_free(tresult); + return result; +} + +void gpr_setenv(const char *name, const char *value) { + LPTSTR tname = gpr_char_to_tchar(name); + LPTSTR tvalue = gpr_char_to_tchar(value); + BOOL res = SetEnvironmentVariable(tname, tvalue); + gpr_free(tname); + gpr_free(tvalue); + GPR_ASSERT(res); +} + +#endif /* GPR_WINDOWS_ENV */ diff --git a/src/core/lib/support/histogram.c b/src/core/lib/support/histogram.c deleted file mode 100644 index 6d5ead9aa6..0000000000 --- a/src/core/lib/support/histogram.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include - -/* Histograms are stored with exponentially increasing bucket sizes. - The first bucket is [0, m) where m = 1 + resolution - Bucket n (n>=1) contains [m**n, m**(n+1)) - There are sufficient buckets to reach max_bucket_start */ - -struct gpr_histogram { - /* Sum of all values seen so far */ - double sum; - /* Sum of squares of all values seen so far */ - double sum_of_squares; - /* number of values seen so far */ - double count; - /* m in the description */ - double multiplier; - double one_on_log_multiplier; - /* minimum value seen */ - double min_seen; - /* maximum value seen */ - double max_seen; - /* maximum representable value */ - double max_possible; - /* number of buckets */ - size_t num_buckets; - /* the buckets themselves */ - uint32_t *buckets; -}; - -/* determine a bucket index given a value - does no bounds checking */ -static size_t bucket_for_unchecked(gpr_histogram *h, double x) { - return (size_t)(log(x) * h->one_on_log_multiplier); -} - -/* bounds checked version of the above */ -static size_t bucket_for(gpr_histogram *h, double x) { - size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 1.0, h->max_possible)); - GPR_ASSERT(bucket < h->num_buckets); - return bucket; -} - -/* at what value does a bucket start? */ -static double bucket_start(gpr_histogram *h, double x) { - return pow(h->multiplier, x); -} - -gpr_histogram *gpr_histogram_create(double resolution, - double max_bucket_start) { - gpr_histogram *h = (gpr_histogram *)gpr_malloc(sizeof(gpr_histogram)); - GPR_ASSERT(resolution > 0.0); - GPR_ASSERT(max_bucket_start > resolution); - h->sum = 0.0; - h->sum_of_squares = 0.0; - h->multiplier = 1.0 + resolution; - h->one_on_log_multiplier = 1.0 / log(1.0 + resolution); - h->max_possible = max_bucket_start; - h->count = 0.0; - h->min_seen = max_bucket_start; - h->max_seen = 0.0; - h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1; - GPR_ASSERT(h->num_buckets > 1); - GPR_ASSERT(h->num_buckets < 100000000); - h->buckets = (uint32_t *)gpr_zalloc(sizeof(uint32_t) * h->num_buckets); - return h; -} - -void gpr_histogram_destroy(gpr_histogram *h) { - gpr_free(h->buckets); - gpr_free(h); -} - -void gpr_histogram_add(gpr_histogram *h, double x) { - h->sum += x; - h->sum_of_squares += x * x; - h->count++; - if (x < h->min_seen) { - h->min_seen = x; - } - if (x > h->max_seen) { - h->max_seen = x; - } - h->buckets[bucket_for(h, x)]++; -} - -int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src) { - if ((dst->num_buckets != src->num_buckets) || - (dst->multiplier != src->multiplier)) { - /* Fail because these histograms don't match */ - return 0; - } - gpr_histogram_merge_contents(dst, src->buckets, src->num_buckets, - src->min_seen, src->max_seen, src->sum, - src->sum_of_squares, src->count); - return 1; -} - -void gpr_histogram_merge_contents(gpr_histogram *dst, const uint32_t *data, - size_t data_count, double min_seen, - double max_seen, double sum, - double sum_of_squares, double count) { - size_t i; - GPR_ASSERT(dst->num_buckets == data_count); - dst->sum += sum; - dst->sum_of_squares += sum_of_squares; - dst->count += count; - if (min_seen < dst->min_seen) { - dst->min_seen = min_seen; - } - if (max_seen > dst->max_seen) { - dst->max_seen = max_seen; - } - for (i = 0; i < dst->num_buckets; i++) { - dst->buckets[i] += data[i]; - } -} - -static double threshold_for_count_below(gpr_histogram *h, double count_below) { - double count_so_far; - double lower_bound; - double upper_bound; - size_t lower_idx; - size_t upper_idx; - - if (h->count == 0) { - return 0.0; - } - - if (count_below <= 0) { - return h->min_seen; - } - if (count_below >= h->count) { - return h->max_seen; - } - - /* find the lowest bucket that gets us above count_below */ - count_so_far = 0.0; - for (lower_idx = 0; lower_idx < h->num_buckets; lower_idx++) { - count_so_far += h->buckets[lower_idx]; - if (count_so_far >= count_below) { - break; - } - } - if (count_so_far == count_below) { - /* this bucket hits the threshold exactly... we should be midway through - any run of zero values following the bucket */ - for (upper_idx = lower_idx + 1; upper_idx < h->num_buckets; upper_idx++) { - if (h->buckets[upper_idx]) { - break; - } - } - return (bucket_start(h, (double)lower_idx) + - bucket_start(h, (double)upper_idx)) / - 2.0; - } else { - /* treat values as uniform throughout the bucket, and find where this value - should lie */ - lower_bound = bucket_start(h, (double)lower_idx); - upper_bound = bucket_start(h, (double)(lower_idx + 1)); - return GPR_CLAMP(upper_bound - - (upper_bound - lower_bound) * - (count_so_far - count_below) / - h->buckets[lower_idx], - h->min_seen, h->max_seen); - } -} - -double gpr_histogram_percentile(gpr_histogram *h, double percentile) { - return threshold_for_count_below(h, h->count * percentile / 100.0); -} - -double gpr_histogram_mean(gpr_histogram *h) { - GPR_ASSERT(h->count != 0); - return h->sum / h->count; -} - -double gpr_histogram_stddev(gpr_histogram *h) { - return sqrt(gpr_histogram_variance(h)); -} - -double gpr_histogram_variance(gpr_histogram *h) { - if (h->count == 0) return 0.0; - return (h->sum_of_squares * h->count - h->sum * h->sum) / - (h->count * h->count); -} - -double gpr_histogram_maximum(gpr_histogram *h) { return h->max_seen; } - -double gpr_histogram_minimum(gpr_histogram *h) { return h->min_seen; } - -double gpr_histogram_count(gpr_histogram *h) { return h->count; } - -double gpr_histogram_sum(gpr_histogram *h) { return h->sum; } - -double gpr_histogram_sum_of_squares(gpr_histogram *h) { - return h->sum_of_squares; -} - -const uint32_t *gpr_histogram_get_contents(gpr_histogram *h, size_t *size) { - *size = h->num_buckets; - return h->buckets; -} diff --git a/src/core/lib/support/histogram.cc b/src/core/lib/support/histogram.cc new file mode 100644 index 0000000000..6d5ead9aa6 --- /dev/null +++ b/src/core/lib/support/histogram.cc @@ -0,0 +1,228 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +/* Histograms are stored with exponentially increasing bucket sizes. + The first bucket is [0, m) where m = 1 + resolution + Bucket n (n>=1) contains [m**n, m**(n+1)) + There are sufficient buckets to reach max_bucket_start */ + +struct gpr_histogram { + /* Sum of all values seen so far */ + double sum; + /* Sum of squares of all values seen so far */ + double sum_of_squares; + /* number of values seen so far */ + double count; + /* m in the description */ + double multiplier; + double one_on_log_multiplier; + /* minimum value seen */ + double min_seen; + /* maximum value seen */ + double max_seen; + /* maximum representable value */ + double max_possible; + /* number of buckets */ + size_t num_buckets; + /* the buckets themselves */ + uint32_t *buckets; +}; + +/* determine a bucket index given a value - does no bounds checking */ +static size_t bucket_for_unchecked(gpr_histogram *h, double x) { + return (size_t)(log(x) * h->one_on_log_multiplier); +} + +/* bounds checked version of the above */ +static size_t bucket_for(gpr_histogram *h, double x) { + size_t bucket = bucket_for_unchecked(h, GPR_CLAMP(x, 1.0, h->max_possible)); + GPR_ASSERT(bucket < h->num_buckets); + return bucket; +} + +/* at what value does a bucket start? */ +static double bucket_start(gpr_histogram *h, double x) { + return pow(h->multiplier, x); +} + +gpr_histogram *gpr_histogram_create(double resolution, + double max_bucket_start) { + gpr_histogram *h = (gpr_histogram *)gpr_malloc(sizeof(gpr_histogram)); + GPR_ASSERT(resolution > 0.0); + GPR_ASSERT(max_bucket_start > resolution); + h->sum = 0.0; + h->sum_of_squares = 0.0; + h->multiplier = 1.0 + resolution; + h->one_on_log_multiplier = 1.0 / log(1.0 + resolution); + h->max_possible = max_bucket_start; + h->count = 0.0; + h->min_seen = max_bucket_start; + h->max_seen = 0.0; + h->num_buckets = bucket_for_unchecked(h, max_bucket_start) + 1; + GPR_ASSERT(h->num_buckets > 1); + GPR_ASSERT(h->num_buckets < 100000000); + h->buckets = (uint32_t *)gpr_zalloc(sizeof(uint32_t) * h->num_buckets); + return h; +} + +void gpr_histogram_destroy(gpr_histogram *h) { + gpr_free(h->buckets); + gpr_free(h); +} + +void gpr_histogram_add(gpr_histogram *h, double x) { + h->sum += x; + h->sum_of_squares += x * x; + h->count++; + if (x < h->min_seen) { + h->min_seen = x; + } + if (x > h->max_seen) { + h->max_seen = x; + } + h->buckets[bucket_for(h, x)]++; +} + +int gpr_histogram_merge(gpr_histogram *dst, const gpr_histogram *src) { + if ((dst->num_buckets != src->num_buckets) || + (dst->multiplier != src->multiplier)) { + /* Fail because these histograms don't match */ + return 0; + } + gpr_histogram_merge_contents(dst, src->buckets, src->num_buckets, + src->min_seen, src->max_seen, src->sum, + src->sum_of_squares, src->count); + return 1; +} + +void gpr_histogram_merge_contents(gpr_histogram *dst, const uint32_t *data, + size_t data_count, double min_seen, + double max_seen, double sum, + double sum_of_squares, double count) { + size_t i; + GPR_ASSERT(dst->num_buckets == data_count); + dst->sum += sum; + dst->sum_of_squares += sum_of_squares; + dst->count += count; + if (min_seen < dst->min_seen) { + dst->min_seen = min_seen; + } + if (max_seen > dst->max_seen) { + dst->max_seen = max_seen; + } + for (i = 0; i < dst->num_buckets; i++) { + dst->buckets[i] += data[i]; + } +} + +static double threshold_for_count_below(gpr_histogram *h, double count_below) { + double count_so_far; + double lower_bound; + double upper_bound; + size_t lower_idx; + size_t upper_idx; + + if (h->count == 0) { + return 0.0; + } + + if (count_below <= 0) { + return h->min_seen; + } + if (count_below >= h->count) { + return h->max_seen; + } + + /* find the lowest bucket that gets us above count_below */ + count_so_far = 0.0; + for (lower_idx = 0; lower_idx < h->num_buckets; lower_idx++) { + count_so_far += h->buckets[lower_idx]; + if (count_so_far >= count_below) { + break; + } + } + if (count_so_far == count_below) { + /* this bucket hits the threshold exactly... we should be midway through + any run of zero values following the bucket */ + for (upper_idx = lower_idx + 1; upper_idx < h->num_buckets; upper_idx++) { + if (h->buckets[upper_idx]) { + break; + } + } + return (bucket_start(h, (double)lower_idx) + + bucket_start(h, (double)upper_idx)) / + 2.0; + } else { + /* treat values as uniform throughout the bucket, and find where this value + should lie */ + lower_bound = bucket_start(h, (double)lower_idx); + upper_bound = bucket_start(h, (double)(lower_idx + 1)); + return GPR_CLAMP(upper_bound - + (upper_bound - lower_bound) * + (count_so_far - count_below) / + h->buckets[lower_idx], + h->min_seen, h->max_seen); + } +} + +double gpr_histogram_percentile(gpr_histogram *h, double percentile) { + return threshold_for_count_below(h, h->count * percentile / 100.0); +} + +double gpr_histogram_mean(gpr_histogram *h) { + GPR_ASSERT(h->count != 0); + return h->sum / h->count; +} + +double gpr_histogram_stddev(gpr_histogram *h) { + return sqrt(gpr_histogram_variance(h)); +} + +double gpr_histogram_variance(gpr_histogram *h) { + if (h->count == 0) return 0.0; + return (h->sum_of_squares * h->count - h->sum * h->sum) / + (h->count * h->count); +} + +double gpr_histogram_maximum(gpr_histogram *h) { return h->max_seen; } + +double gpr_histogram_minimum(gpr_histogram *h) { return h->min_seen; } + +double gpr_histogram_count(gpr_histogram *h) { return h->count; } + +double gpr_histogram_sum(gpr_histogram *h) { return h->sum; } + +double gpr_histogram_sum_of_squares(gpr_histogram *h) { + return h->sum_of_squares; +} + +const uint32_t *gpr_histogram_get_contents(gpr_histogram *h, size_t *size) { + *size = h->num_buckets; + return h->buckets; +} diff --git a/src/core/lib/support/host_port.c b/src/core/lib/support/host_port.c deleted file mode 100644 index 3302e574ab..0000000000 --- a/src/core/lib/support/host_port.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -#include -#include -#include -#include "src/core/lib/support/string.h" - -int gpr_join_host_port(char **out, const char *host, int port) { - if (host[0] != '[' && strchr(host, ':') != NULL) { - /* IPv6 literals must be enclosed in brackets. */ - return gpr_asprintf(out, "[%s]:%d", host, port); - } else { - /* Ordinary non-bracketed host:port. */ - return gpr_asprintf(out, "%s:%d", host, port); - } -} - -int gpr_split_host_port(const char *name, char **host, char **port) { - const char *host_start; - size_t host_len; - const char *port_start; - - *host = NULL; - *port = NULL; - - if (name[0] == '[') { - /* Parse a bracketed host, typically an IPv6 literal. */ - const char *rbracket = strchr(name, ']'); - if (rbracket == NULL) { - /* Unmatched [ */ - return 0; - } - if (rbracket[1] == '\0') { - /* ] */ - port_start = NULL; - } else if (rbracket[1] == ':') { - /* ]: */ - port_start = rbracket + 2; - } else { - /* ] */ - return 0; - } - host_start = name + 1; - host_len = (size_t)(rbracket - host_start); - if (memchr(host_start, ':', host_len) == NULL) { - /* Require all bracketed hosts to contain a colon, because a hostname or - IPv4 address should never use brackets. */ - return 0; - } - } else { - const char *colon = strchr(name, ':'); - if (colon != NULL && strchr(colon + 1, ':') == NULL) { - /* Exactly 1 colon. Split into host:port. */ - host_start = name; - host_len = (size_t)(colon - name); - port_start = colon + 1; - } else { - /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ - host_start = name; - host_len = strlen(name); - port_start = NULL; - } - } - - /* Allocate return values. */ - *host = (char *)gpr_malloc(host_len + 1); - memcpy(*host, host_start, host_len); - (*host)[host_len] = '\0'; - - if (port_start != NULL) { - *port = gpr_strdup(port_start); - } - - return 1; -} diff --git a/src/core/lib/support/host_port.cc b/src/core/lib/support/host_port.cc new file mode 100644 index 0000000000..3302e574ab --- /dev/null +++ b/src/core/lib/support/host_port.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +#include +#include +#include +#include "src/core/lib/support/string.h" + +int gpr_join_host_port(char **out, const char *host, int port) { + if (host[0] != '[' && strchr(host, ':') != NULL) { + /* IPv6 literals must be enclosed in brackets. */ + return gpr_asprintf(out, "[%s]:%d", host, port); + } else { + /* Ordinary non-bracketed host:port. */ + return gpr_asprintf(out, "%s:%d", host, port); + } +} + +int gpr_split_host_port(const char *name, char **host, char **port) { + const char *host_start; + size_t host_len; + const char *port_start; + + *host = NULL; + *port = NULL; + + if (name[0] == '[') { + /* Parse a bracketed host, typically an IPv6 literal. */ + const char *rbracket = strchr(name, ']'); + if (rbracket == NULL) { + /* Unmatched [ */ + return 0; + } + if (rbracket[1] == '\0') { + /* ] */ + port_start = NULL; + } else if (rbracket[1] == ':') { + /* ]: */ + port_start = rbracket + 2; + } else { + /* ] */ + return 0; + } + host_start = name + 1; + host_len = (size_t)(rbracket - host_start); + if (memchr(host_start, ':', host_len) == NULL) { + /* Require all bracketed hosts to contain a colon, because a hostname or + IPv4 address should never use brackets. */ + return 0; + } + } else { + const char *colon = strchr(name, ':'); + if (colon != NULL && strchr(colon + 1, ':') == NULL) { + /* Exactly 1 colon. Split into host:port. */ + host_start = name; + host_len = (size_t)(colon - name); + port_start = colon + 1; + } else { + /* 0 or 2+ colons. Bare hostname or IPv6 litearal. */ + host_start = name; + host_len = strlen(name); + port_start = NULL; + } + } + + /* Allocate return values. */ + *host = (char *)gpr_malloc(host_len + 1); + memcpy(*host, host_start, host_len); + (*host)[host_len] = '\0'; + + if (port_start != NULL) { + *port = gpr_strdup(port_start); + } + + return 1; +} diff --git a/src/core/lib/support/log.c b/src/core/lib/support/log.c deleted file mode 100644 index 69f92e001c..0000000000 --- a/src/core/lib/support/log.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include - -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" - -#include -#include - -extern "C" void gpr_default_log(gpr_log_func_args *args); -static gpr_atm g_log_func = (gpr_atm)gpr_default_log; -static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET; - -const char *gpr_log_severity_string(gpr_log_severity severity) { - switch (severity) { - case GPR_LOG_SEVERITY_DEBUG: - return "D"; - case GPR_LOG_SEVERITY_INFO: - return "I"; - case GPR_LOG_SEVERITY_ERROR: - return "E"; - } - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -void gpr_log_message(const char *file, int line, gpr_log_severity severity, - const char *message) { - if ((gpr_atm)severity < gpr_atm_no_barrier_load(&g_min_severity_to_print)) { - return; - } - - gpr_log_func_args lfargs; - memset(&lfargs, 0, sizeof(lfargs)); - lfargs.file = file; - lfargs.line = line; - lfargs.severity = severity; - lfargs.message = message; - ((gpr_log_func)gpr_atm_no_barrier_load(&g_log_func))(&lfargs); -} - -void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print) { - gpr_atm_no_barrier_store(&g_min_severity_to_print, - (gpr_atm)min_severity_to_print); -} - -void gpr_log_verbosity_init() { - char *verbosity = NULL; - const char *insecure_getenv = gpr_getenv_silent("GRPC_VERBOSITY", &verbosity); - - gpr_atm min_severity_to_print = GPR_LOG_SEVERITY_ERROR; - if (verbosity != NULL) { - if (gpr_stricmp(verbosity, "DEBUG") == 0) { - min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_DEBUG; - } else if (gpr_stricmp(verbosity, "INFO") == 0) { - min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_INFO; - } else if (gpr_stricmp(verbosity, "ERROR") == 0) { - min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_ERROR; - } - gpr_free(verbosity); - } - if ((gpr_atm_no_barrier_load(&g_min_severity_to_print)) == - GPR_LOG_VERBOSITY_UNSET) { - gpr_atm_no_barrier_store(&g_min_severity_to_print, min_severity_to_print); - } - - if (insecure_getenv != NULL) { - gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", - insecure_getenv); - } -} - -void gpr_set_log_function(gpr_log_func f) { - gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)(f ? f : gpr_default_log)); -} diff --git a/src/core/lib/support/log.cc b/src/core/lib/support/log.cc new file mode 100644 index 0000000000..69f92e001c --- /dev/null +++ b/src/core/lib/support/log.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include + +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" + +#include +#include + +extern "C" void gpr_default_log(gpr_log_func_args *args); +static gpr_atm g_log_func = (gpr_atm)gpr_default_log; +static gpr_atm g_min_severity_to_print = GPR_LOG_VERBOSITY_UNSET; + +const char *gpr_log_severity_string(gpr_log_severity severity) { + switch (severity) { + case GPR_LOG_SEVERITY_DEBUG: + return "D"; + case GPR_LOG_SEVERITY_INFO: + return "I"; + case GPR_LOG_SEVERITY_ERROR: + return "E"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +void gpr_log_message(const char *file, int line, gpr_log_severity severity, + const char *message) { + if ((gpr_atm)severity < gpr_atm_no_barrier_load(&g_min_severity_to_print)) { + return; + } + + gpr_log_func_args lfargs; + memset(&lfargs, 0, sizeof(lfargs)); + lfargs.file = file; + lfargs.line = line; + lfargs.severity = severity; + lfargs.message = message; + ((gpr_log_func)gpr_atm_no_barrier_load(&g_log_func))(&lfargs); +} + +void gpr_set_log_verbosity(gpr_log_severity min_severity_to_print) { + gpr_atm_no_barrier_store(&g_min_severity_to_print, + (gpr_atm)min_severity_to_print); +} + +void gpr_log_verbosity_init() { + char *verbosity = NULL; + const char *insecure_getenv = gpr_getenv_silent("GRPC_VERBOSITY", &verbosity); + + gpr_atm min_severity_to_print = GPR_LOG_SEVERITY_ERROR; + if (verbosity != NULL) { + if (gpr_stricmp(verbosity, "DEBUG") == 0) { + min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_DEBUG; + } else if (gpr_stricmp(verbosity, "INFO") == 0) { + min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_INFO; + } else if (gpr_stricmp(verbosity, "ERROR") == 0) { + min_severity_to_print = (gpr_atm)GPR_LOG_SEVERITY_ERROR; + } + gpr_free(verbosity); + } + if ((gpr_atm_no_barrier_load(&g_min_severity_to_print)) == + GPR_LOG_VERBOSITY_UNSET) { + gpr_atm_no_barrier_store(&g_min_severity_to_print, min_severity_to_print); + } + + if (insecure_getenv != NULL) { + gpr_log(GPR_DEBUG, "Warning: insecure environment read function '%s' used", + insecure_getenv); + } +} + +void gpr_set_log_function(gpr_log_func f) { + gpr_atm_no_barrier_store(&g_log_func, (gpr_atm)(f ? f : gpr_default_log)); +} diff --git a/src/core/lib/support/log_android.c b/src/core/lib/support/log_android.c deleted file mode 100644 index 6f1cec51f1..0000000000 --- a/src/core/lib/support/log_android.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_ANDROID - -#include -#include -#include -#include -#include -#include - -static android_LogPriority severity_to_log_priority(gpr_log_severity severity) { - switch (severity) { - case GPR_LOG_SEVERITY_DEBUG: - return ANDROID_LOG_DEBUG; - case GPR_LOG_SEVERITY_INFO: - return ANDROID_LOG_INFO; - case GPR_LOG_SEVERITY_ERROR: - return ANDROID_LOG_ERROR; - } - return ANDROID_LOG_DEFAULT; -} - -void gpr_log(const char *file, int line, gpr_log_severity severity, - const char *format, ...) { - char *message = NULL; - va_list args; - va_start(args, format); - vasprintf(&message, format, args); - va_end(args); - gpr_log_message(file, line, severity, message); - free(message); -} - -void gpr_default_log(gpr_log_func_args *args) { - char *final_slash; - const char *display_file; - char *output = NULL; - - final_slash = strrchr(args->file, '/'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - asprintf(&output, "%s:%d] %s", display_file, args->line, args->message); - - __android_log_write(severity_to_log_priority(args->severity), "GRPC", output); - - /* allocated by asprintf => use free, not gpr_free */ - free(output); -} - -#endif /* GPR_ANDROID */ diff --git a/src/core/lib/support/log_android.cc b/src/core/lib/support/log_android.cc new file mode 100644 index 0000000000..6f1cec51f1 --- /dev/null +++ b/src/core/lib/support/log_android.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_ANDROID + +#include +#include +#include +#include +#include +#include + +static android_LogPriority severity_to_log_priority(gpr_log_severity severity) { + switch (severity) { + case GPR_LOG_SEVERITY_DEBUG: + return ANDROID_LOG_DEBUG; + case GPR_LOG_SEVERITY_INFO: + return ANDROID_LOG_INFO; + case GPR_LOG_SEVERITY_ERROR: + return ANDROID_LOG_ERROR; + } + return ANDROID_LOG_DEFAULT; +} + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + va_start(args, format); + vasprintf(&message, format, args); + va_end(args); + gpr_log_message(file, line, severity, message); + free(message); +} + +void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + const char *display_file; + char *output = NULL; + + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + asprintf(&output, "%s:%d] %s", display_file, args->line, args->message); + + __android_log_write(severity_to_log_priority(args->severity), "GRPC", output); + + /* allocated by asprintf => use free, not gpr_free */ + free(output); +} + +#endif /* GPR_ANDROID */ diff --git a/src/core/lib/support/log_linux.c b/src/core/lib/support/log_linux.c deleted file mode 100644 index 0914acedf4..0000000000 --- a/src/core/lib/support/log_linux.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef _POSIX_SOURCE -#define _POSIX_SOURCE -#endif - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif - -#include - -#ifdef GPR_LINUX_LOG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static long gettid(void) { return syscall(__NR_gettid); } - -void gpr_log(const char *file, int line, gpr_log_severity severity, - const char *format, ...) { - char *message = NULL; - va_list args; - va_start(args, format); - if (vasprintf(&message, format, args) == -1) { - va_end(args); - return; - } - va_end(args); - gpr_log_message(file, line, severity, message); - /* message has been allocated by vasprintf above, and needs free */ - free(message); -} - -extern "C" void gpr_default_log(gpr_log_func_args *args) { - const char *final_slash; - char *prefix; - const char *display_file; - char time_buffer[64]; - time_t timer; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - struct tm tm; - static __thread long tid = 0; - if (tid == 0) tid = gettid(); - - timer = (time_t)now.tv_sec; - final_slash = strrchr(args->file, '/'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - if (!localtime_r(&timer, &tm)) { - strcpy(time_buffer, "error:localtime"); - } else if (0 == - strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { - strcpy(time_buffer, "error:strftime"); - } - - gpr_asprintf(&prefix, "%s%s.%09" PRId32 " %7ld %s:%d]", - gpr_log_severity_string(args->severity), time_buffer, - now.tv_nsec, tid, display_file, args->line); - - fprintf(stderr, "%-60s %s\n", prefix, args->message); - gpr_free(prefix); -} - -#endif /* GPR_LINUX_LOG */ diff --git a/src/core/lib/support/log_linux.cc b/src/core/lib/support/log_linux.cc new file mode 100644 index 0000000000..0914acedf4 --- /dev/null +++ b/src/core/lib/support/log_linux.cc @@ -0,0 +1,92 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include + +#ifdef GPR_LINUX_LOG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static long gettid(void) { return syscall(__NR_gettid); } + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + va_start(args, format); + if (vasprintf(&message, format, args) == -1) { + va_end(args); + return; + } + va_end(args); + gpr_log_message(file, line, severity, message); + /* message has been allocated by vasprintf above, and needs free */ + free(message); +} + +extern "C" void gpr_default_log(gpr_log_func_args *args) { + const char *final_slash; + char *prefix; + const char *display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + static __thread long tid = 0; + if (tid == 0) tid = gettid(); + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&timer, &tm)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + gpr_asprintf(&prefix, "%s%s.%09" PRId32 " %7ld %s:%d]", + gpr_log_severity_string(args->severity), time_buffer, + now.tv_nsec, tid, display_file, args->line); + + fprintf(stderr, "%-60s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif /* GPR_LINUX_LOG */ diff --git a/src/core/lib/support/log_posix.c b/src/core/lib/support/log_posix.c deleted file mode 100644 index 855e8e7107..0000000000 --- a/src/core/lib/support/log_posix.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_POSIX_LOG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static intptr_t gettid(void) { return (intptr_t)pthread_self(); } - -void gpr_log(const char *file, int line, gpr_log_severity severity, - const char *format, ...) { - char buf[64]; - char *allocated = NULL; - char *message = NULL; - int ret; - va_list args; - va_start(args, format); - ret = vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - if (ret < 0) { - message = NULL; - } else if ((size_t)ret <= sizeof(buf) - 1) { - message = buf; - } else { - message = allocated = gpr_malloc((size_t)ret + 1); - va_start(args, format); - vsnprintf(message, (size_t)(ret + 1), format, args); - va_end(args); - } - gpr_log_message(file, line, severity, message); - gpr_free(allocated); -} - -extern "C" void gpr_default_log(gpr_log_func_args *args) { - char *final_slash; - const char *display_file; - char time_buffer[64]; - time_t timer; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - struct tm tm; - - timer = (time_t)now.tv_sec; - final_slash = strrchr(args->file, '/'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - if (!localtime_r(&timer, &tm)) { - strcpy(time_buffer, "error:localtime"); - } else if (0 == - strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { - strcpy(time_buffer, "error:strftime"); - } - - char *prefix; - gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]", - gpr_log_severity_string(args->severity), time_buffer, - (int)(now.tv_nsec), gettid(), display_file, args->line); - - fprintf(stderr, "%-70s %s\n", prefix, args->message); - gpr_free(prefix); -} - -#endif /* defined(GPR_POSIX_LOG) */ diff --git a/src/core/lib/support/log_posix.cc b/src/core/lib/support/log_posix.cc new file mode 100644 index 0000000000..855e8e7107 --- /dev/null +++ b/src/core/lib/support/log_posix.cc @@ -0,0 +1,91 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_LOG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static intptr_t gettid(void) { return (intptr_t)pthread_self(); } + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char buf[64]; + char *allocated = NULL; + char *message = NULL; + int ret; + va_list args; + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + message = NULL; + } else if ((size_t)ret <= sizeof(buf) - 1) { + message = buf; + } else { + message = allocated = gpr_malloc((size_t)ret + 1); + va_start(args, format); + vsnprintf(message, (size_t)(ret + 1), format, args); + va_end(args); + } + gpr_log_message(file, line, severity, message); + gpr_free(allocated); +} + +extern "C" void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + const char *display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '/'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (!localtime_r(&timer, &tm)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + char *prefix; + gpr_asprintf(&prefix, "%s%s.%09d %7tu %s:%d]", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), gettid(), display_file, args->line); + + fprintf(stderr, "%-70s %s\n", prefix, args->message); + gpr_free(prefix); +} + +#endif /* defined(GPR_POSIX_LOG) */ diff --git a/src/core/lib/support/log_windows.c b/src/core/lib/support/log_windows.c deleted file mode 100644 index b71dacd80a..0000000000 --- a/src/core/lib/support/log_windows.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_WINDOWS_LOG - -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/support/string.h" -#include "src/core/lib/support/string_windows.h" - -void gpr_log(const char *file, int line, gpr_log_severity severity, - const char *format, ...) { - char *message = NULL; - va_list args; - int ret; - - /* Determine the length. */ - va_start(args, format); - ret = _vscprintf(format, args); - va_end(args); - if (ret < 0) { - message = NULL; - } else { - /* Allocate a new buffer, with space for the NUL terminator. */ - size_t strp_buflen = (size_t)ret + 1; - message = gpr_malloc(strp_buflen); - - /* Print to the buffer. */ - va_start(args, format); - ret = vsnprintf_s(message, strp_buflen, _TRUNCATE, format, args); - va_end(args); - if ((size_t)ret != strp_buflen - 1) { - /* This should never happen. */ - gpr_free(message); - message = NULL; - } - } - - gpr_log_message(file, line, severity, message); - gpr_free(message); -} - -/* Simple starter implementation */ -extern "C" void gpr_default_log(gpr_log_func_args *args) { - char *final_slash; - const char *display_file; - char time_buffer[64]; - time_t timer; - gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); - struct tm tm; - - timer = (time_t)now.tv_sec; - final_slash = strrchr(args->file, '\\'); - if (final_slash == NULL) - display_file = args->file; - else - display_file = final_slash + 1; - - if (localtime_s(&tm, &timer)) { - strcpy(time_buffer, "error:localtime"); - } else if (0 == - strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { - strcpy(time_buffer, "error:strftime"); - } - - fprintf(stderr, "%s%s.%09u %5lu %s:%d] %s\n", - gpr_log_severity_string(args->severity), time_buffer, - (int)(now.tv_nsec), GetCurrentThreadId(), display_file, args->line, - args->message); - fflush(stderr); -} - -#endif /* GPR_WINDOWS_LOG */ diff --git a/src/core/lib/support/log_windows.cc b/src/core/lib/support/log_windows.cc new file mode 100644 index 0000000000..b71dacd80a --- /dev/null +++ b/src/core/lib/support/log_windows.cc @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_LOG + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/support/string.h" +#include "src/core/lib/support/string_windows.h" + +void gpr_log(const char *file, int line, gpr_log_severity severity, + const char *format, ...) { + char *message = NULL; + va_list args; + int ret; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (ret < 0) { + message = NULL; + } else { + /* Allocate a new buffer, with space for the NUL terminator. */ + size_t strp_buflen = (size_t)ret + 1; + message = gpr_malloc(strp_buflen); + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(message, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if ((size_t)ret != strp_buflen - 1) { + /* This should never happen. */ + gpr_free(message); + message = NULL; + } + } + + gpr_log_message(file, line, severity, message); + gpr_free(message); +} + +/* Simple starter implementation */ +extern "C" void gpr_default_log(gpr_log_func_args *args) { + char *final_slash; + const char *display_file; + char time_buffer[64]; + time_t timer; + gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME); + struct tm tm; + + timer = (time_t)now.tv_sec; + final_slash = strrchr(args->file, '\\'); + if (final_slash == NULL) + display_file = args->file; + else + display_file = final_slash + 1; + + if (localtime_s(&tm, &timer)) { + strcpy(time_buffer, "error:localtime"); + } else if (0 == + strftime(time_buffer, sizeof(time_buffer), "%m%d %H:%M:%S", &tm)) { + strcpy(time_buffer, "error:strftime"); + } + + fprintf(stderr, "%s%s.%09u %5lu %s:%d] %s\n", + gpr_log_severity_string(args->severity), time_buffer, + (int)(now.tv_nsec), GetCurrentThreadId(), display_file, args->line, + args->message); + fflush(stderr); +} + +#endif /* GPR_WINDOWS_LOG */ diff --git a/src/core/lib/support/mpscq.c b/src/core/lib/support/mpscq.c deleted file mode 100644 index e9f893988d..0000000000 --- a/src/core/lib/support/mpscq.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/support/mpscq.h" - -#include - -void gpr_mpscq_init(gpr_mpscq *q) { - gpr_atm_no_barrier_store(&q->head, (gpr_atm)&q->stub); - q->tail = &q->stub; - gpr_atm_no_barrier_store(&q->stub.next, (gpr_atm)NULL); -} - -void gpr_mpscq_destroy(gpr_mpscq *q) { - GPR_ASSERT(gpr_atm_no_barrier_load(&q->head) == (gpr_atm)&q->stub); - GPR_ASSERT(q->tail == &q->stub); -} - -void gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n) { - gpr_atm_no_barrier_store(&n->next, (gpr_atm)NULL); - gpr_mpscq_node *prev = - (gpr_mpscq_node *)gpr_atm_full_xchg(&q->head, (gpr_atm)n); - gpr_atm_rel_store(&prev->next, (gpr_atm)n); -} - -gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q) { - bool empty; - return gpr_mpscq_pop_and_check_end(q, &empty); -} - -gpr_mpscq_node *gpr_mpscq_pop_and_check_end(gpr_mpscq *q, bool *empty) { - gpr_mpscq_node *tail = q->tail; - gpr_mpscq_node *next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next); - if (tail == &q->stub) { - // indicates the list is actually (ephemerally) empty - if (next == NULL) { - *empty = true; - return NULL; - } - q->tail = next; - tail = next; - next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next); - } - if (next != NULL) { - *empty = false; - q->tail = next; - return tail; - } - gpr_mpscq_node *head = (gpr_mpscq_node *)gpr_atm_acq_load(&q->head); - if (tail != head) { - *empty = false; - // indicates a retry is in order: we're still adding - return NULL; - } - gpr_mpscq_push(q, &q->stub); - next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next); - if (next != NULL) { - q->tail = next; - return tail; - } - // indicates a retry is in order: we're still adding - *empty = false; - return NULL; -} diff --git a/src/core/lib/support/mpscq.cc b/src/core/lib/support/mpscq.cc new file mode 100644 index 0000000000..e9f893988d --- /dev/null +++ b/src/core/lib/support/mpscq.cc @@ -0,0 +1,79 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/support/mpscq.h" + +#include + +void gpr_mpscq_init(gpr_mpscq *q) { + gpr_atm_no_barrier_store(&q->head, (gpr_atm)&q->stub); + q->tail = &q->stub; + gpr_atm_no_barrier_store(&q->stub.next, (gpr_atm)NULL); +} + +void gpr_mpscq_destroy(gpr_mpscq *q) { + GPR_ASSERT(gpr_atm_no_barrier_load(&q->head) == (gpr_atm)&q->stub); + GPR_ASSERT(q->tail == &q->stub); +} + +void gpr_mpscq_push(gpr_mpscq *q, gpr_mpscq_node *n) { + gpr_atm_no_barrier_store(&n->next, (gpr_atm)NULL); + gpr_mpscq_node *prev = + (gpr_mpscq_node *)gpr_atm_full_xchg(&q->head, (gpr_atm)n); + gpr_atm_rel_store(&prev->next, (gpr_atm)n); +} + +gpr_mpscq_node *gpr_mpscq_pop(gpr_mpscq *q) { + bool empty; + return gpr_mpscq_pop_and_check_end(q, &empty); +} + +gpr_mpscq_node *gpr_mpscq_pop_and_check_end(gpr_mpscq *q, bool *empty) { + gpr_mpscq_node *tail = q->tail; + gpr_mpscq_node *next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next); + if (tail == &q->stub) { + // indicates the list is actually (ephemerally) empty + if (next == NULL) { + *empty = true; + return NULL; + } + q->tail = next; + tail = next; + next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next); + } + if (next != NULL) { + *empty = false; + q->tail = next; + return tail; + } + gpr_mpscq_node *head = (gpr_mpscq_node *)gpr_atm_acq_load(&q->head); + if (tail != head) { + *empty = false; + // indicates a retry is in order: we're still adding + return NULL; + } + gpr_mpscq_push(q, &q->stub); + next = (gpr_mpscq_node *)gpr_atm_acq_load(&tail->next); + if (next != NULL) { + q->tail = next; + return tail; + } + // indicates a retry is in order: we're still adding + *empty = false; + return NULL; +} diff --git a/src/core/lib/support/murmur_hash.c b/src/core/lib/support/murmur_hash.c deleted file mode 100644 index f06b970de7..0000000000 --- a/src/core/lib/support/murmur_hash.c +++ /dev/null @@ -1,81 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/support/murmur_hash.h" - -#include - -#define ROTL32(x, r) ((x) << (r)) | ((x) >> (32 - (r))) - -#define FMIX32(h) \ - (h) ^= (h) >> 16; \ - (h) *= 0x85ebca6b; \ - (h) ^= (h) >> 13; \ - (h) *= 0xc2b2ae35; \ - (h) ^= (h) >> 16; - -uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed) { - const uint8_t *data = (const uint8_t *)key; - const size_t nblocks = len / 4; - int i; - - uint32_t h1 = seed; - uint32_t k1; - - const uint32_t c1 = 0xcc9e2d51; - const uint32_t c2 = 0x1b873593; - - const uint32_t *blocks = ((const uint32_t *)key) + nblocks; - const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); - - /* body */ - for (i = -(int)nblocks; i; i++) { - memcpy(&k1, blocks + i, sizeof(uint32_t)); - - k1 *= c1; - k1 = ROTL32(k1, 15); - k1 *= c2; - - h1 ^= k1; - h1 = ROTL32(h1, 13); - h1 = h1 * 5 + 0xe6546b64; - } - - k1 = 0; - - /* tail */ - switch (len & 3) { - case 3: - k1 ^= ((uint32_t)tail[2]) << 16; - /* fallthrough */ - case 2: - k1 ^= ((uint32_t)tail[1]) << 8; - /* fallthrough */ - case 1: - k1 ^= tail[0]; - k1 *= c1; - k1 = ROTL32(k1, 15); - k1 *= c2; - h1 ^= k1; - }; - - /* finalization */ - h1 ^= (uint32_t)len; - FMIX32(h1); - return h1; -} diff --git a/src/core/lib/support/murmur_hash.cc b/src/core/lib/support/murmur_hash.cc new file mode 100644 index 0000000000..f06b970de7 --- /dev/null +++ b/src/core/lib/support/murmur_hash.cc @@ -0,0 +1,81 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/support/murmur_hash.h" + +#include + +#define ROTL32(x, r) ((x) << (r)) | ((x) >> (32 - (r))) + +#define FMIX32(h) \ + (h) ^= (h) >> 16; \ + (h) *= 0x85ebca6b; \ + (h) ^= (h) >> 13; \ + (h) *= 0xc2b2ae35; \ + (h) ^= (h) >> 16; + +uint32_t gpr_murmur_hash3(const void *key, size_t len, uint32_t seed) { + const uint8_t *data = (const uint8_t *)key; + const size_t nblocks = len / 4; + int i; + + uint32_t h1 = seed; + uint32_t k1; + + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + const uint32_t *blocks = ((const uint32_t *)key) + nblocks; + const uint8_t *tail = (const uint8_t *)(data + nblocks * 4); + + /* body */ + for (i = -(int)nblocks; i; i++) { + memcpy(&k1, blocks + i, sizeof(uint32_t)); + + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1, 13); + h1 = h1 * 5 + 0xe6546b64; + } + + k1 = 0; + + /* tail */ + switch (len & 3) { + case 3: + k1 ^= ((uint32_t)tail[2]) << 16; + /* fallthrough */ + case 2: + k1 ^= ((uint32_t)tail[1]) << 8; + /* fallthrough */ + case 1: + k1 ^= tail[0]; + k1 *= c1; + k1 = ROTL32(k1, 15); + k1 *= c2; + h1 ^= k1; + }; + + /* finalization */ + h1 ^= (uint32_t)len; + FMIX32(h1); + return h1; +} diff --git a/src/core/lib/support/stack_lockfree.c b/src/core/lib/support/stack_lockfree.c deleted file mode 100644 index 0fb64ed001..0000000000 --- a/src/core/lib/support/stack_lockfree.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/support/stack_lockfree.h" - -#include -#include - -#include -#include -#include -#include - -/* The lockfree node structure is a single architecture-level - word that allows for an atomic CAS to set it up. */ -struct lockfree_node_contents { - /* next thing to look at. Actual index for head, next index otherwise */ - uint16_t index; -#ifdef GPR_ARCH_64 - uint16_t pad; - uint32_t aba_ctr; -#else -#ifdef GPR_ARCH_32 - uint16_t aba_ctr; -#else -#error Unsupported bit width architecture -#endif -#endif -}; - -/* Use a union to make sure that these are in the same bits as an atm word */ -typedef union lockfree_node { - gpr_atm atm; - struct lockfree_node_contents contents; -} lockfree_node; - -/* make sure that entries aligned to 8-bytes */ -#define ENTRY_ALIGNMENT_BITS 3 -/* reserve this entry as invalid */ -#define INVALID_ENTRY_INDEX ((1 << 16) - 1) - -struct gpr_stack_lockfree { - lockfree_node *entries; - lockfree_node head; /* An atomic entry describing curr head */ -}; - -gpr_stack_lockfree *gpr_stack_lockfree_create(size_t entries) { - gpr_stack_lockfree *stack; - stack = (gpr_stack_lockfree *)gpr_malloc(sizeof(*stack)); - /* Since we only allocate 16 bits to represent an entry number, - * make sure that we are within the desired range */ - /* Reserve the highest entry number as a dummy */ - GPR_ASSERT(entries < INVALID_ENTRY_INDEX); - stack->entries = (lockfree_node *)gpr_malloc_aligned( - entries * sizeof(stack->entries[0]), ENTRY_ALIGNMENT_BITS); - /* Clear out all entries */ - memset(stack->entries, 0, entries * sizeof(stack->entries[0])); - memset(&stack->head, 0, sizeof(stack->head)); - - GPR_ASSERT(sizeof(stack->entries->atm) == sizeof(stack->entries->contents)); - - /* Point the head at reserved dummy entry */ - stack->head.contents.index = INVALID_ENTRY_INDEX; -/* Fill in the pad and aba_ctr to avoid confusing memcheck tools */ -#ifdef GPR_ARCH_64 - stack->head.contents.pad = 0; -#endif - stack->head.contents.aba_ctr = 0; - return stack; -} - -void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack) { - gpr_free_aligned(stack->entries); - gpr_free(stack); -} - -int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) { - lockfree_node head; - lockfree_node newhead; - lockfree_node curent; - lockfree_node newent; - - /* First fill in the entry's index and aba ctr for new head */ - newhead.contents.index = (uint16_t)entry; -#ifdef GPR_ARCH_64 - /* Fill in the pad to avoid confusing memcheck tools */ - newhead.contents.pad = 0; -#endif - - /* Also post-increment the aba_ctr */ - curent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm); - newhead.contents.aba_ctr = ++curent.contents.aba_ctr; - gpr_atm_no_barrier_store(&stack->entries[entry].atm, curent.atm); - - do { - /* Atomically get the existing head value for use */ - head.atm = gpr_atm_no_barrier_load(&(stack->head.atm)); - /* Point to it */ - newent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm); - newent.contents.index = head.contents.index; - gpr_atm_no_barrier_store(&stack->entries[entry].atm, newent.atm); - } while (!gpr_atm_rel_cas(&(stack->head.atm), head.atm, newhead.atm)); - /* Use rel_cas above to make sure that entry index is set properly */ - return head.contents.index == INVALID_ENTRY_INDEX; -} - -int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) { - lockfree_node head; - lockfree_node newhead; - - do { - head.atm = gpr_atm_acq_load(&(stack->head.atm)); - if (head.contents.index == INVALID_ENTRY_INDEX) { - return -1; - } - newhead.atm = - gpr_atm_no_barrier_load(&(stack->entries[head.contents.index].atm)); - - } while (!gpr_atm_no_barrier_cas(&(stack->head.atm), head.atm, newhead.atm)); - - return head.contents.index; -} diff --git a/src/core/lib/support/stack_lockfree.cc b/src/core/lib/support/stack_lockfree.cc new file mode 100644 index 0000000000..0fb64ed001 --- /dev/null +++ b/src/core/lib/support/stack_lockfree.cc @@ -0,0 +1,137 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/support/stack_lockfree.h" + +#include +#include + +#include +#include +#include +#include + +/* The lockfree node structure is a single architecture-level + word that allows for an atomic CAS to set it up. */ +struct lockfree_node_contents { + /* next thing to look at. Actual index for head, next index otherwise */ + uint16_t index; +#ifdef GPR_ARCH_64 + uint16_t pad; + uint32_t aba_ctr; +#else +#ifdef GPR_ARCH_32 + uint16_t aba_ctr; +#else +#error Unsupported bit width architecture +#endif +#endif +}; + +/* Use a union to make sure that these are in the same bits as an atm word */ +typedef union lockfree_node { + gpr_atm atm; + struct lockfree_node_contents contents; +} lockfree_node; + +/* make sure that entries aligned to 8-bytes */ +#define ENTRY_ALIGNMENT_BITS 3 +/* reserve this entry as invalid */ +#define INVALID_ENTRY_INDEX ((1 << 16) - 1) + +struct gpr_stack_lockfree { + lockfree_node *entries; + lockfree_node head; /* An atomic entry describing curr head */ +}; + +gpr_stack_lockfree *gpr_stack_lockfree_create(size_t entries) { + gpr_stack_lockfree *stack; + stack = (gpr_stack_lockfree *)gpr_malloc(sizeof(*stack)); + /* Since we only allocate 16 bits to represent an entry number, + * make sure that we are within the desired range */ + /* Reserve the highest entry number as a dummy */ + GPR_ASSERT(entries < INVALID_ENTRY_INDEX); + stack->entries = (lockfree_node *)gpr_malloc_aligned( + entries * sizeof(stack->entries[0]), ENTRY_ALIGNMENT_BITS); + /* Clear out all entries */ + memset(stack->entries, 0, entries * sizeof(stack->entries[0])); + memset(&stack->head, 0, sizeof(stack->head)); + + GPR_ASSERT(sizeof(stack->entries->atm) == sizeof(stack->entries->contents)); + + /* Point the head at reserved dummy entry */ + stack->head.contents.index = INVALID_ENTRY_INDEX; +/* Fill in the pad and aba_ctr to avoid confusing memcheck tools */ +#ifdef GPR_ARCH_64 + stack->head.contents.pad = 0; +#endif + stack->head.contents.aba_ctr = 0; + return stack; +} + +void gpr_stack_lockfree_destroy(gpr_stack_lockfree *stack) { + gpr_free_aligned(stack->entries); + gpr_free(stack); +} + +int gpr_stack_lockfree_push(gpr_stack_lockfree *stack, int entry) { + lockfree_node head; + lockfree_node newhead; + lockfree_node curent; + lockfree_node newent; + + /* First fill in the entry's index and aba ctr for new head */ + newhead.contents.index = (uint16_t)entry; +#ifdef GPR_ARCH_64 + /* Fill in the pad to avoid confusing memcheck tools */ + newhead.contents.pad = 0; +#endif + + /* Also post-increment the aba_ctr */ + curent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm); + newhead.contents.aba_ctr = ++curent.contents.aba_ctr; + gpr_atm_no_barrier_store(&stack->entries[entry].atm, curent.atm); + + do { + /* Atomically get the existing head value for use */ + head.atm = gpr_atm_no_barrier_load(&(stack->head.atm)); + /* Point to it */ + newent.atm = gpr_atm_no_barrier_load(&stack->entries[entry].atm); + newent.contents.index = head.contents.index; + gpr_atm_no_barrier_store(&stack->entries[entry].atm, newent.atm); + } while (!gpr_atm_rel_cas(&(stack->head.atm), head.atm, newhead.atm)); + /* Use rel_cas above to make sure that entry index is set properly */ + return head.contents.index == INVALID_ENTRY_INDEX; +} + +int gpr_stack_lockfree_pop(gpr_stack_lockfree *stack) { + lockfree_node head; + lockfree_node newhead; + + do { + head.atm = gpr_atm_acq_load(&(stack->head.atm)); + if (head.contents.index == INVALID_ENTRY_INDEX) { + return -1; + } + newhead.atm = + gpr_atm_no_barrier_load(&(stack->entries[head.contents.index].atm)); + + } while (!gpr_atm_no_barrier_cas(&(stack->head.atm), head.atm, newhead.atm)); + + return head.contents.index; +} diff --git a/src/core/lib/support/string.c b/src/core/lib/support/string.c deleted file mode 100644 index d55863892f..0000000000 --- a/src/core/lib/support/string.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/support/string.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -char *gpr_strdup(const char *src) { - char *dst; - size_t len; - - if (!src) { - return NULL; - } - - len = strlen(src) + 1; - dst = (char *)gpr_malloc(len); - - memcpy(dst, src, len); - - return dst; -} - -typedef struct { - size_t capacity; - size_t length; - char *data; -} dump_out; - -static dump_out dump_out_create(void) { - dump_out r = {0, 0, NULL}; - return r; -} - -static void dump_out_append(dump_out *out, char c) { - if (out->length == out->capacity) { - out->capacity = GPR_MAX(8, 2 * out->capacity); - out->data = (char *)gpr_realloc(out->data, out->capacity); - } - out->data[out->length++] = c; -} - -static void hexdump(dump_out *out, const char *buf, size_t len) { - static const char *hex = "0123456789abcdef"; - - const uint8_t *const beg = (const uint8_t *)buf; - const uint8_t *const end = beg + len; - const uint8_t *cur; - - for (cur = beg; cur != end; ++cur) { - if (cur != beg) dump_out_append(out, ' '); - dump_out_append(out, hex[*cur >> 4]); - dump_out_append(out, hex[*cur & 0xf]); - } -} - -static void asciidump(dump_out *out, const char *buf, size_t len) { - const uint8_t *const beg = (const uint8_t *)buf; - const uint8_t *const end = beg + len; - const uint8_t *cur; - int out_was_empty = (out->length == 0); - if (!out_was_empty) { - dump_out_append(out, ' '); - dump_out_append(out, '\''); - } - for (cur = beg; cur != end; ++cur) { - dump_out_append(out, (char)(isprint(*cur) ? *(char *)cur : '.')); - } - if (!out_was_empty) { - dump_out_append(out, '\''); - } -} - -char *gpr_dump(const char *buf, size_t len, uint32_t flags) { - dump_out out = dump_out_create(); - if (flags & GPR_DUMP_HEX) { - hexdump(&out, buf, len); - } - if (flags & GPR_DUMP_ASCII) { - asciidump(&out, buf, len); - } - dump_out_append(&out, 0); - return out.data; -} - -int gpr_parse_bytes_to_uint32(const char *buf, size_t len, uint32_t *result) { - uint32_t out = 0; - uint32_t new_val; - size_t i; - - if (len == 0) return 0; /* must have some bytes */ - - for (i = 0; i < len; i++) { - if (buf[i] < '0' || buf[i] > '9') return 0; /* bad char */ - new_val = 10 * out + (uint32_t)(buf[i] - '0'); - if (new_val < out) return 0; /* overflow */ - out = new_val; - } - - *result = out; - return 1; -} - -void gpr_reverse_bytes(char *str, int len) { - char *p1, *p2; - for (p1 = str, p2 = str + len - 1; p2 > p1; ++p1, --p2) { - char temp = *p1; - *p1 = *p2; - *p2 = temp; - } -} - -int gpr_ltoa(long value, char *string) { - long sign; - int i = 0; - - if (value == 0) { - string[0] = '0'; - string[1] = 0; - return 1; - } - - sign = value < 0 ? -1 : 1; - while (value) { - string[i++] = (char)('0' + sign * (value % 10)); - value /= 10; - } - if (sign < 0) string[i++] = '-'; - gpr_reverse_bytes(string, i); - string[i] = 0; - return i; -} - -int int64_ttoa(int64_t value, char *string) { - int64_t sign; - int i = 0; - - if (value == 0) { - string[0] = '0'; - string[1] = 0; - return 1; - } - - sign = value < 0 ? -1 : 1; - while (value) { - string[i++] = (char)('0' + sign * (value % 10)); - value /= 10; - } - if (sign < 0) string[i++] = '-'; - gpr_reverse_bytes(string, i); - string[i] = 0; - return i; -} - -int gpr_parse_nonnegative_int(const char *value) { - char *end; - long result = strtol(value, &end, 0); - if (*end != '\0' || result < 0 || result > INT_MAX) return -1; - return (int)result; -} - -char *gpr_leftpad(const char *str, char flag, size_t length) { - const size_t str_length = strlen(str); - const size_t out_length = str_length > length ? str_length : length; - char *out = (char *)gpr_malloc(out_length + 1); - memset(out, flag, out_length - str_length); - memcpy(out + out_length - str_length, str, str_length); - out[out_length] = 0; - return out; -} - -char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { - return gpr_strjoin_sep(strs, nstrs, "", final_length); -} - -char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, - size_t *final_length) { - const size_t sep_len = strlen(sep); - size_t out_length = 0; - size_t i; - char *out; - for (i = 0; i < nstrs; i++) { - out_length += strlen(strs[i]); - } - out_length += 1; /* null terminator */ - if (nstrs > 0) { - out_length += sep_len * (nstrs - 1); /* separators */ - } - out = (char *)gpr_malloc(out_length); - out_length = 0; - for (i = 0; i < nstrs; i++) { - const size_t slen = strlen(strs[i]); - if (i != 0) { - memcpy(out + out_length, sep, sep_len); - out_length += sep_len; - } - memcpy(out + out_length, strs[i], slen); - out_length += slen; - } - out[out_length] = 0; - if (final_length != NULL) { - *final_length = out_length; - } - return out; -} - -void gpr_strvec_init(gpr_strvec *sv) { memset(sv, 0, sizeof(*sv)); } - -void gpr_strvec_destroy(gpr_strvec *sv) { - size_t i; - for (i = 0; i < sv->count; i++) { - gpr_free(sv->strs[i]); - } - gpr_free(sv->strs); -} - -void gpr_strvec_add(gpr_strvec *sv, char *str) { - if (sv->count == sv->capacity) { - sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2); - sv->strs = (char **)gpr_realloc(sv->strs, sizeof(char *) * sv->capacity); - } - sv->strs[sv->count++] = str; -} - -char *gpr_strvec_flatten(gpr_strvec *sv, size_t *final_length) { - return gpr_strjoin((const char **)sv->strs, sv->count, final_length); -} - -int gpr_stricmp(const char *a, const char *b) { - int ca, cb; - do { - ca = tolower(*a); - cb = tolower(*b); - ++a; - ++b; - } while (ca == cb && ca && cb); - return ca - cb; -} - -static void add_string_to_split(const char *beg, const char *end, char ***strs, - size_t *nstrs, size_t *capstrs) { - char *out = (char *)gpr_malloc((size_t)(end - beg) + 1); - memcpy(out, beg, (size_t)(end - beg)); - out[end - beg] = 0; - if (*nstrs == *capstrs) { - *capstrs = GPR_MAX(8, 2 * *capstrs); - *strs = (char **)gpr_realloc(*strs, sizeof(*strs) * *capstrs); - } - (*strs)[*nstrs] = out; - ++*nstrs; -} - -void gpr_string_split(const char *input, const char *sep, char ***strs, - size_t *nstrs) { - const char *next; - *strs = NULL; - *nstrs = 0; - size_t capstrs = 0; - while ((next = strstr(input, sep))) { - add_string_to_split(input, next, strs, nstrs, &capstrs); - input = next + strlen(sep); - } - add_string_to_split(input, input + strlen(input), strs, nstrs, &capstrs); -} - -void *gpr_memrchr(const void *s, int c, size_t n) { - if (s == NULL) return NULL; - char *b = (char *)s; - size_t i; - for (i = 0; i < n; i++) { - if (b[n - i - 1] == c) { - return &b[n - i - 1]; - } - } - return NULL; -} - -bool gpr_is_true(const char *s) { - size_t i; - if (s == NULL) { - return false; - } - static const char *truthy[] = {"yes", "true", "1"}; - for (i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { - if (0 == gpr_stricmp(s, truthy[i])) { - return true; - } - } - return false; -} diff --git a/src/core/lib/support/string.cc b/src/core/lib/support/string.cc new file mode 100644 index 0000000000..d55863892f --- /dev/null +++ b/src/core/lib/support/string.cc @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/support/string.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +char *gpr_strdup(const char *src) { + char *dst; + size_t len; + + if (!src) { + return NULL; + } + + len = strlen(src) + 1; + dst = (char *)gpr_malloc(len); + + memcpy(dst, src, len); + + return dst; +} + +typedef struct { + size_t capacity; + size_t length; + char *data; +} dump_out; + +static dump_out dump_out_create(void) { + dump_out r = {0, 0, NULL}; + return r; +} + +static void dump_out_append(dump_out *out, char c) { + if (out->length == out->capacity) { + out->capacity = GPR_MAX(8, 2 * out->capacity); + out->data = (char *)gpr_realloc(out->data, out->capacity); + } + out->data[out->length++] = c; +} + +static void hexdump(dump_out *out, const char *buf, size_t len) { + static const char *hex = "0123456789abcdef"; + + const uint8_t *const beg = (const uint8_t *)buf; + const uint8_t *const end = beg + len; + const uint8_t *cur; + + for (cur = beg; cur != end; ++cur) { + if (cur != beg) dump_out_append(out, ' '); + dump_out_append(out, hex[*cur >> 4]); + dump_out_append(out, hex[*cur & 0xf]); + } +} + +static void asciidump(dump_out *out, const char *buf, size_t len) { + const uint8_t *const beg = (const uint8_t *)buf; + const uint8_t *const end = beg + len; + const uint8_t *cur; + int out_was_empty = (out->length == 0); + if (!out_was_empty) { + dump_out_append(out, ' '); + dump_out_append(out, '\''); + } + for (cur = beg; cur != end; ++cur) { + dump_out_append(out, (char)(isprint(*cur) ? *(char *)cur : '.')); + } + if (!out_was_empty) { + dump_out_append(out, '\''); + } +} + +char *gpr_dump(const char *buf, size_t len, uint32_t flags) { + dump_out out = dump_out_create(); + if (flags & GPR_DUMP_HEX) { + hexdump(&out, buf, len); + } + if (flags & GPR_DUMP_ASCII) { + asciidump(&out, buf, len); + } + dump_out_append(&out, 0); + return out.data; +} + +int gpr_parse_bytes_to_uint32(const char *buf, size_t len, uint32_t *result) { + uint32_t out = 0; + uint32_t new_val; + size_t i; + + if (len == 0) return 0; /* must have some bytes */ + + for (i = 0; i < len; i++) { + if (buf[i] < '0' || buf[i] > '9') return 0; /* bad char */ + new_val = 10 * out + (uint32_t)(buf[i] - '0'); + if (new_val < out) return 0; /* overflow */ + out = new_val; + } + + *result = out; + return 1; +} + +void gpr_reverse_bytes(char *str, int len) { + char *p1, *p2; + for (p1 = str, p2 = str + len - 1; p2 > p1; ++p1, --p2) { + char temp = *p1; + *p1 = *p2; + *p2 = temp; + } +} + +int gpr_ltoa(long value, char *string) { + long sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = (char)('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int int64_ttoa(int64_t value, char *string) { + int64_t sign; + int i = 0; + + if (value == 0) { + string[0] = '0'; + string[1] = 0; + return 1; + } + + sign = value < 0 ? -1 : 1; + while (value) { + string[i++] = (char)('0' + sign * (value % 10)); + value /= 10; + } + if (sign < 0) string[i++] = '-'; + gpr_reverse_bytes(string, i); + string[i] = 0; + return i; +} + +int gpr_parse_nonnegative_int(const char *value) { + char *end; + long result = strtol(value, &end, 0); + if (*end != '\0' || result < 0 || result > INT_MAX) return -1; + return (int)result; +} + +char *gpr_leftpad(const char *str, char flag, size_t length) { + const size_t str_length = strlen(str); + const size_t out_length = str_length > length ? str_length : length; + char *out = (char *)gpr_malloc(out_length + 1); + memset(out, flag, out_length - str_length); + memcpy(out + out_length - str_length, str, str_length); + out[out_length] = 0; + return out; +} + +char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { + return gpr_strjoin_sep(strs, nstrs, "", final_length); +} + +char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, + size_t *final_length) { + const size_t sep_len = strlen(sep); + size_t out_length = 0; + size_t i; + char *out; + for (i = 0; i < nstrs; i++) { + out_length += strlen(strs[i]); + } + out_length += 1; /* null terminator */ + if (nstrs > 0) { + out_length += sep_len * (nstrs - 1); /* separators */ + } + out = (char *)gpr_malloc(out_length); + out_length = 0; + for (i = 0; i < nstrs; i++) { + const size_t slen = strlen(strs[i]); + if (i != 0) { + memcpy(out + out_length, sep, sep_len); + out_length += sep_len; + } + memcpy(out + out_length, strs[i], slen); + out_length += slen; + } + out[out_length] = 0; + if (final_length != NULL) { + *final_length = out_length; + } + return out; +} + +void gpr_strvec_init(gpr_strvec *sv) { memset(sv, 0, sizeof(*sv)); } + +void gpr_strvec_destroy(gpr_strvec *sv) { + size_t i; + for (i = 0; i < sv->count; i++) { + gpr_free(sv->strs[i]); + } + gpr_free(sv->strs); +} + +void gpr_strvec_add(gpr_strvec *sv, char *str) { + if (sv->count == sv->capacity) { + sv->capacity = GPR_MAX(sv->capacity + 8, sv->capacity * 2); + sv->strs = (char **)gpr_realloc(sv->strs, sizeof(char *) * sv->capacity); + } + sv->strs[sv->count++] = str; +} + +char *gpr_strvec_flatten(gpr_strvec *sv, size_t *final_length) { + return gpr_strjoin((const char **)sv->strs, sv->count, final_length); +} + +int gpr_stricmp(const char *a, const char *b) { + int ca, cb; + do { + ca = tolower(*a); + cb = tolower(*b); + ++a; + ++b; + } while (ca == cb && ca && cb); + return ca - cb; +} + +static void add_string_to_split(const char *beg, const char *end, char ***strs, + size_t *nstrs, size_t *capstrs) { + char *out = (char *)gpr_malloc((size_t)(end - beg) + 1); + memcpy(out, beg, (size_t)(end - beg)); + out[end - beg] = 0; + if (*nstrs == *capstrs) { + *capstrs = GPR_MAX(8, 2 * *capstrs); + *strs = (char **)gpr_realloc(*strs, sizeof(*strs) * *capstrs); + } + (*strs)[*nstrs] = out; + ++*nstrs; +} + +void gpr_string_split(const char *input, const char *sep, char ***strs, + size_t *nstrs) { + const char *next; + *strs = NULL; + *nstrs = 0; + size_t capstrs = 0; + while ((next = strstr(input, sep))) { + add_string_to_split(input, next, strs, nstrs, &capstrs); + input = next + strlen(sep); + } + add_string_to_split(input, input + strlen(input), strs, nstrs, &capstrs); +} + +void *gpr_memrchr(const void *s, int c, size_t n) { + if (s == NULL) return NULL; + char *b = (char *)s; + size_t i; + for (i = 0; i < n; i++) { + if (b[n - i - 1] == c) { + return &b[n - i - 1]; + } + } + return NULL; +} + +bool gpr_is_true(const char *s) { + size_t i; + if (s == NULL) { + return false; + } + static const char *truthy[] = {"yes", "true", "1"}; + for (i = 0; i < GPR_ARRAY_SIZE(truthy); i++) { + if (0 == gpr_stricmp(s, truthy[i])) { + return true; + } + } + return false; +} diff --git a/src/core/lib/support/string_posix.c b/src/core/lib/support/string_posix.c deleted file mode 100644 index 92de21a6e1..0000000000 --- a/src/core/lib/support/string_posix.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_POSIX_STRING - -#include -#include -#include - -#include -#include - -int gpr_asprintf(char **strp, const char *format, ...) { - va_list args; - int ret; - char buf[64]; - size_t strp_buflen; - - /* Use a constant-sized buffer to determine the length. */ - va_start(args, format); - ret = vsnprintf(buf, sizeof(buf), format, args); - va_end(args); - if (ret < 0) { - *strp = NULL; - return -1; - } - - /* Allocate a new buffer, with space for the NUL terminator. */ - strp_buflen = (size_t)ret + 1; - if ((*strp = (char *)gpr_malloc(strp_buflen)) == NULL) { - /* This shouldn't happen, because gpr_malloc() calls abort(). */ - return -1; - } - - /* Return early if we have all the bytes. */ - if (strp_buflen <= sizeof(buf)) { - memcpy(*strp, buf, strp_buflen); - return ret; - } - - /* Try again using the larger buffer. */ - va_start(args, format); - ret = vsnprintf(*strp, strp_buflen, format, args); - va_end(args); - if ((size_t)ret == strp_buflen - 1) { - return ret; - } - - /* This should never happen. */ - gpr_free(*strp); - *strp = NULL; - return -1; -} - -#endif /* GPR_POSIX_STRING */ diff --git a/src/core/lib/support/string_posix.cc b/src/core/lib/support/string_posix.cc new file mode 100644 index 0000000000..92de21a6e1 --- /dev/null +++ b/src/core/lib/support/string_posix.cc @@ -0,0 +1,72 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_STRING + +#include +#include +#include + +#include +#include + +int gpr_asprintf(char **strp, const char *format, ...) { + va_list args; + int ret; + char buf[64]; + size_t strp_buflen; + + /* Use a constant-sized buffer to determine the length. */ + va_start(args, format); + ret = vsnprintf(buf, sizeof(buf), format, args); + va_end(args); + if (ret < 0) { + *strp = NULL; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + if ((*strp = (char *)gpr_malloc(strp_buflen)) == NULL) { + /* This shouldn't happen, because gpr_malloc() calls abort(). */ + return -1; + } + + /* Return early if we have all the bytes. */ + if (strp_buflen <= sizeof(buf)) { + memcpy(*strp, buf, strp_buflen); + return ret; + } + + /* Try again using the larger buffer. */ + va_start(args, format); + ret = vsnprintf(*strp, strp_buflen, format, args); + va_end(args); + if ((size_t)ret == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = NULL; + return -1; +} + +#endif /* GPR_POSIX_STRING */ diff --git a/src/core/lib/support/string_util_windows.c b/src/core/lib/support/string_util_windows.c deleted file mode 100644 index 2a03404448..0000000000 --- a/src/core/lib/support/string_util_windows.c +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Posix code for gpr snprintf support. */ - -#include - -#ifdef GPR_WINDOWS - -/* Some platforms (namely msys) need wchar to be included BEFORE - anything else, especially strsafe.h. */ -#include - -#include -#include -#include -#include - -#include -#include - -#include "src/core/lib/support/string.h" - -#if defined UNICODE || defined _UNICODE -LPTSTR -gpr_char_to_tchar(LPCSTR input) { - LPTSTR ret; - int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); - if (needed <= 0) return NULL; - ret = gpr_malloc((unsigned)needed * sizeof(TCHAR)); - MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed); - return ret; -} - -LPSTR -gpr_tchar_to_char(LPCTSTR input) { - LPSTR ret; - int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); - if (needed <= 0) return NULL; - ret = gpr_malloc((unsigned)needed); - WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL); - return ret; -} -#else -char *gpr_tchar_to_char(LPTSTR input) { return gpr_strdup(input); } - -char *gpr_char_to_tchar(LPTSTR input) { return gpr_strdup(input); } -#endif - -char *gpr_format_message(int messageid) { - LPTSTR tmessage; - char *message; - DWORD status = FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, (DWORD)messageid, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), - (LPTSTR)(&tmessage), 0, NULL); - if (status == 0) return gpr_strdup("Unable to retrieve error string"); - message = gpr_tchar_to_char(tmessage); - LocalFree(tmessage); - return message; -} - -#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/string_util_windows.cc b/src/core/lib/support/string_util_windows.cc new file mode 100644 index 0000000000..2a03404448 --- /dev/null +++ b/src/core/lib/support/string_util_windows.cc @@ -0,0 +1,79 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix code for gpr snprintf support. */ + +#include + +#ifdef GPR_WINDOWS + +/* Some platforms (namely msys) need wchar to be included BEFORE + anything else, especially strsafe.h. */ +#include + +#include +#include +#include +#include + +#include +#include + +#include "src/core/lib/support/string.h" + +#if defined UNICODE || defined _UNICODE +LPTSTR +gpr_char_to_tchar(LPCSTR input) { + LPTSTR ret; + int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); + if (needed <= 0) return NULL; + ret = gpr_malloc((unsigned)needed * sizeof(TCHAR)); + MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed); + return ret; +} + +LPSTR +gpr_tchar_to_char(LPCTSTR input) { + LPSTR ret; + int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); + if (needed <= 0) return NULL; + ret = gpr_malloc((unsigned)needed); + WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL); + return ret; +} +#else +char *gpr_tchar_to_char(LPTSTR input) { return gpr_strdup(input); } + +char *gpr_char_to_tchar(LPTSTR input) { return gpr_strdup(input); } +#endif + +char *gpr_format_message(int messageid) { + LPTSTR tmessage; + char *message; + DWORD status = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, (DWORD)messageid, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), + (LPTSTR)(&tmessage), 0, NULL); + if (status == 0) return gpr_strdup("Unable to retrieve error string"); + message = gpr_tchar_to_char(tmessage); + LocalFree(tmessage); + return message; +} + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/string_windows.c b/src/core/lib/support/string_windows.c deleted file mode 100644 index bae524d7cb..0000000000 --- a/src/core/lib/support/string_windows.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Windows code for gpr snprintf support. */ - -#include - -#ifdef GPR_WINDOWS_STRING - -#include -#include -#include - -#include -#include - -#include "src/core/lib/support/string.h" - -int gpr_asprintf(char **strp, const char *format, ...) { - va_list args; - int ret; - size_t strp_buflen; - - /* Determine the length. */ - va_start(args, format); - ret = _vscprintf(format, args); - va_end(args); - if (ret < 0) { - *strp = NULL; - return -1; - } - - /* Allocate a new buffer, with space for the NUL terminator. */ - strp_buflen = (size_t)ret + 1; - if ((*strp = gpr_malloc(strp_buflen)) == NULL) { - /* This shouldn't happen, because gpr_malloc() calls abort(). */ - return -1; - } - - /* Print to the buffer. */ - va_start(args, format); - ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args); - va_end(args); - if ((size_t)ret == strp_buflen - 1) { - return ret; - } - - /* This should never happen. */ - gpr_free(*strp); - *strp = NULL; - return -1; -} - -#endif /* GPR_WINDOWS_STRING */ diff --git a/src/core/lib/support/string_windows.cc b/src/core/lib/support/string_windows.cc new file mode 100644 index 0000000000..bae524d7cb --- /dev/null +++ b/src/core/lib/support/string_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows code for gpr snprintf support. */ + +#include + +#ifdef GPR_WINDOWS_STRING + +#include +#include +#include + +#include +#include + +#include "src/core/lib/support/string.h" + +int gpr_asprintf(char **strp, const char *format, ...) { + va_list args; + int ret; + size_t strp_buflen; + + /* Determine the length. */ + va_start(args, format); + ret = _vscprintf(format, args); + va_end(args); + if (ret < 0) { + *strp = NULL; + return -1; + } + + /* Allocate a new buffer, with space for the NUL terminator. */ + strp_buflen = (size_t)ret + 1; + if ((*strp = gpr_malloc(strp_buflen)) == NULL) { + /* This shouldn't happen, because gpr_malloc() calls abort(). */ + return -1; + } + + /* Print to the buffer. */ + va_start(args, format); + ret = vsnprintf_s(*strp, strp_buflen, _TRUNCATE, format, args); + va_end(args); + if ((size_t)ret == strp_buflen - 1) { + return ret; + } + + /* This should never happen. */ + gpr_free(*strp); + *strp = NULL; + return -1; +} + +#endif /* GPR_WINDOWS_STRING */ diff --git a/src/core/lib/support/subprocess_posix.c b/src/core/lib/support/subprocess_posix.c deleted file mode 100644 index af75162ee9..0000000000 --- a/src/core/lib/support/subprocess_posix.c +++ /dev/null @@ -1,99 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_POSIX_SUBPROCESS - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -struct gpr_subprocess { - int pid; - bool joined; -}; - -const char *gpr_subprocess_binary_extension() { return ""; } - -gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { - gpr_subprocess *r; - int pid; - char **exec_args; - - pid = fork(); - if (pid == -1) { - return NULL; - } else if (pid == 0) { - exec_args = (char **)gpr_malloc(((size_t)argc + 1) * sizeof(char *)); - memcpy(exec_args, argv, (size_t)argc * sizeof(char *)); - exec_args[argc] = NULL; - execv(exec_args[0], exec_args); - /* if we reach here, an error has occurred */ - gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno)); - _exit(1); - return NULL; - } else { - r = (gpr_subprocess *)gpr_zalloc(sizeof(gpr_subprocess)); - r->pid = pid; - return r; - } -} - -void gpr_subprocess_destroy(gpr_subprocess *p) { - if (!p->joined) { - kill(p->pid, SIGKILL); - gpr_subprocess_join(p); - } - gpr_free(p); -} - -int gpr_subprocess_join(gpr_subprocess *p) { - int status; -retry: - if (waitpid(p->pid, &status, 0) == -1) { - if (errno == EINTR) { - goto retry; - } - gpr_log(GPR_ERROR, "waitpid failed for pid %d: %s", p->pid, - strerror(errno)); - return -1; - } - p->joined = true; - return status; -} - -void gpr_subprocess_interrupt(gpr_subprocess *p) { - if (!p->joined) { - kill(p->pid, SIGINT); - } -} - -#endif /* GPR_POSIX_SUBPROCESS */ diff --git a/src/core/lib/support/subprocess_posix.cc b/src/core/lib/support/subprocess_posix.cc new file mode 100644 index 0000000000..af75162ee9 --- /dev/null +++ b/src/core/lib/support/subprocess_posix.cc @@ -0,0 +1,99 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_SUBPROCESS + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct gpr_subprocess { + int pid; + bool joined; +}; + +const char *gpr_subprocess_binary_extension() { return ""; } + +gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { + gpr_subprocess *r; + int pid; + char **exec_args; + + pid = fork(); + if (pid == -1) { + return NULL; + } else if (pid == 0) { + exec_args = (char **)gpr_malloc(((size_t)argc + 1) * sizeof(char *)); + memcpy(exec_args, argv, (size_t)argc * sizeof(char *)); + exec_args[argc] = NULL; + execv(exec_args[0], exec_args); + /* if we reach here, an error has occurred */ + gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0], strerror(errno)); + _exit(1); + return NULL; + } else { + r = (gpr_subprocess *)gpr_zalloc(sizeof(gpr_subprocess)); + r->pid = pid; + return r; + } +} + +void gpr_subprocess_destroy(gpr_subprocess *p) { + if (!p->joined) { + kill(p->pid, SIGKILL); + gpr_subprocess_join(p); + } + gpr_free(p); +} + +int gpr_subprocess_join(gpr_subprocess *p) { + int status; +retry: + if (waitpid(p->pid, &status, 0) == -1) { + if (errno == EINTR) { + goto retry; + } + gpr_log(GPR_ERROR, "waitpid failed for pid %d: %s", p->pid, + strerror(errno)); + return -1; + } + p->joined = true; + return status; +} + +void gpr_subprocess_interrupt(gpr_subprocess *p) { + if (!p->joined) { + kill(p->pid, SIGINT); + } +} + +#endif /* GPR_POSIX_SUBPROCESS */ diff --git a/src/core/lib/support/subprocess_windows.c b/src/core/lib/support/subprocess_windows.c deleted file mode 100644 index 7412f8d344..0000000000 --- a/src/core/lib/support/subprocess_windows.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_WINDOWS_SUBPROCESS - -#include -#include -#include - -#include -#include -#include -#include "src/core/lib/support/string.h" -#include "src/core/lib/support/string_windows.h" - -struct gpr_subprocess { - PROCESS_INFORMATION pi; - int joined; - int interrupted; -}; - -const char *gpr_subprocess_binary_extension() { return ".exe"; } - -gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { - gpr_subprocess *r; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - char *args = gpr_strjoin_sep(argv, (size_t)argc, " ", NULL); - TCHAR *args_tchar; - - args_tchar = gpr_char_to_tchar(args); - gpr_free(args); - - memset(&si, 0, sizeof(si)); - si.cb = sizeof(si); - memset(&pi, 0, sizeof(pi)); - - if (!CreateProcess(NULL, args_tchar, NULL, NULL, FALSE, - CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) { - gpr_free(args_tchar); - return NULL; - } - gpr_free(args_tchar); - - r = gpr_malloc(sizeof(gpr_subprocess)); - memset(r, 0, sizeof(*r)); - r->pi = pi; - return r; -} - -void gpr_subprocess_destroy(gpr_subprocess *p) { - if (p) { - if (!p->joined) { - gpr_subprocess_interrupt(p); - gpr_subprocess_join(p); - } - if (p->pi.hProcess) { - CloseHandle(p->pi.hProcess); - } - if (p->pi.hThread) { - CloseHandle(p->pi.hThread); - } - gpr_free(p); - } -} - -int gpr_subprocess_join(gpr_subprocess *p) { - DWORD dwExitCode; - if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { - if (dwExitCode == STILL_ACTIVE) { - if (WaitForSingleObject(p->pi.hProcess, INFINITE) == WAIT_OBJECT_0) { - p->joined = 1; - goto getExitCode; - } - return -1; // failed to join - } else { - goto getExitCode; - } - } else { - return -1; // failed to get exit code - } - -getExitCode: - if (p->interrupted) { - return 0; - } - if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { - return (int)dwExitCode; - } else { - return -1; // failed to get exit code - } -} - -void gpr_subprocess_interrupt(gpr_subprocess *p) { - DWORD dwExitCode; - if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { - if (dwExitCode == STILL_ACTIVE) { - gpr_log(GPR_INFO, "sending ctrl-break"); - GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, p->pi.dwProcessId); - p->joined = 1; - p->interrupted = 1; - } - } - return; -} - -#endif /* GPR_WINDOWS_SUBPROCESS */ diff --git a/src/core/lib/support/subprocess_windows.cc b/src/core/lib/support/subprocess_windows.cc new file mode 100644 index 0000000000..7412f8d344 --- /dev/null +++ b/src/core/lib/support/subprocess_windows.cc @@ -0,0 +1,126 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_SUBPROCESS + +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/support/string.h" +#include "src/core/lib/support/string_windows.h" + +struct gpr_subprocess { + PROCESS_INFORMATION pi; + int joined; + int interrupted; +}; + +const char *gpr_subprocess_binary_extension() { return ".exe"; } + +gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { + gpr_subprocess *r; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + char *args = gpr_strjoin_sep(argv, (size_t)argc, " ", NULL); + TCHAR *args_tchar; + + args_tchar = gpr_char_to_tchar(args); + gpr_free(args); + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + memset(&pi, 0, sizeof(pi)); + + if (!CreateProcess(NULL, args_tchar, NULL, NULL, FALSE, + CREATE_NEW_PROCESS_GROUP, NULL, NULL, &si, &pi)) { + gpr_free(args_tchar); + return NULL; + } + gpr_free(args_tchar); + + r = gpr_malloc(sizeof(gpr_subprocess)); + memset(r, 0, sizeof(*r)); + r->pi = pi; + return r; +} + +void gpr_subprocess_destroy(gpr_subprocess *p) { + if (p) { + if (!p->joined) { + gpr_subprocess_interrupt(p); + gpr_subprocess_join(p); + } + if (p->pi.hProcess) { + CloseHandle(p->pi.hProcess); + } + if (p->pi.hThread) { + CloseHandle(p->pi.hThread); + } + gpr_free(p); + } +} + +int gpr_subprocess_join(gpr_subprocess *p) { + DWORD dwExitCode; + if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { + if (dwExitCode == STILL_ACTIVE) { + if (WaitForSingleObject(p->pi.hProcess, INFINITE) == WAIT_OBJECT_0) { + p->joined = 1; + goto getExitCode; + } + return -1; // failed to join + } else { + goto getExitCode; + } + } else { + return -1; // failed to get exit code + } + +getExitCode: + if (p->interrupted) { + return 0; + } + if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { + return (int)dwExitCode; + } else { + return -1; // failed to get exit code + } +} + +void gpr_subprocess_interrupt(gpr_subprocess *p) { + DWORD dwExitCode; + if (GetExitCodeProcess(p->pi.hProcess, &dwExitCode)) { + if (dwExitCode == STILL_ACTIVE) { + gpr_log(GPR_INFO, "sending ctrl-break"); + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, p->pi.dwProcessId); + p->joined = 1; + p->interrupted = 1; + } + } + return; +} + +#endif /* GPR_WINDOWS_SUBPROCESS */ diff --git a/src/core/lib/support/sync.c b/src/core/lib/support/sync.c deleted file mode 100644 index 994dcb0e14..0000000000 --- a/src/core/lib/support/sync.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Generic implementation of synchronization primitives. */ - -#include -#include -#include - -#include - -/* Number of mutexes to allocate for events, to avoid lock contention. - Should be a prime. */ -enum { event_sync_partitions = 31 }; - -/* Events are partitioned by address to avoid lock contention. */ -static struct sync_array_s { - gpr_mu mu; - gpr_cv cv; -} sync_array[event_sync_partitions]; - -/* This routine is executed once on first use, via event_once */ -static gpr_once event_once = GPR_ONCE_INIT; -static void event_initialize(void) { - int i; - for (i = 0; i != event_sync_partitions; i++) { - gpr_mu_init(&sync_array[i].mu); - gpr_cv_init(&sync_array[i].cv); - } -} - -/* Hash ev into an element of sync_array[]. */ -static struct sync_array_s *hash(gpr_event *ev) { - return &sync_array[((uintptr_t)ev) % event_sync_partitions]; -} - -void gpr_event_init(gpr_event *ev) { - gpr_once_init(&event_once, &event_initialize); - ev->state = 0; -} - -void gpr_event_set(gpr_event *ev, void *value) { - struct sync_array_s *s = hash(ev); - gpr_mu_lock(&s->mu); - GPR_ASSERT(gpr_atm_acq_load(&ev->state) == 0); - gpr_atm_rel_store(&ev->state, (gpr_atm)value); - gpr_cv_broadcast(&s->cv); - gpr_mu_unlock(&s->mu); - GPR_ASSERT(value != NULL); -} - -void *gpr_event_get(gpr_event *ev) { - return (void *)gpr_atm_acq_load(&ev->state); -} - -void *gpr_event_wait(gpr_event *ev, gpr_timespec abs_deadline) { - void *result = (void *)gpr_atm_acq_load(&ev->state); - if (result == NULL) { - struct sync_array_s *s = hash(ev); - gpr_mu_lock(&s->mu); - do { - result = (void *)gpr_atm_acq_load(&ev->state); - } while (result == NULL && !gpr_cv_wait(&s->cv, &s->mu, abs_deadline)); - gpr_mu_unlock(&s->mu); - } - return result; -} - -void gpr_ref_init(gpr_refcount *r, int n) { gpr_atm_rel_store(&r->count, n); } - -void gpr_ref(gpr_refcount *r) { gpr_atm_no_barrier_fetch_add(&r->count, 1); } - -void gpr_ref_non_zero(gpr_refcount *r) { -#ifndef NDEBUG - gpr_atm prior = gpr_atm_no_barrier_fetch_add(&r->count, 1); - assert(prior > 0); -#else - gpr_ref(r); -#endif -} - -void gpr_refn(gpr_refcount *r, int n) { - gpr_atm_no_barrier_fetch_add(&r->count, n); -} - -int gpr_unref(gpr_refcount *r) { - gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1); - GPR_ASSERT(prior > 0); - return prior == 1; -} - -int gpr_ref_is_unique(gpr_refcount *r) { - return gpr_atm_acq_load(&r->count) == 1; -} - -void gpr_stats_init(gpr_stats_counter *c, intptr_t n) { - gpr_atm_rel_store(&c->value, n); -} - -void gpr_stats_inc(gpr_stats_counter *c, intptr_t inc) { - gpr_atm_no_barrier_fetch_add(&c->value, inc); -} - -intptr_t gpr_stats_read(const gpr_stats_counter *c) { - /* don't need acquire-load, but we have no no-barrier load yet */ - return gpr_atm_acq_load(&c->value); -} diff --git a/src/core/lib/support/sync.cc b/src/core/lib/support/sync.cc new file mode 100644 index 0000000000..994dcb0e14 --- /dev/null +++ b/src/core/lib/support/sync.cc @@ -0,0 +1,122 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Generic implementation of synchronization primitives. */ + +#include +#include +#include + +#include + +/* Number of mutexes to allocate for events, to avoid lock contention. + Should be a prime. */ +enum { event_sync_partitions = 31 }; + +/* Events are partitioned by address to avoid lock contention. */ +static struct sync_array_s { + gpr_mu mu; + gpr_cv cv; +} sync_array[event_sync_partitions]; + +/* This routine is executed once on first use, via event_once */ +static gpr_once event_once = GPR_ONCE_INIT; +static void event_initialize(void) { + int i; + for (i = 0; i != event_sync_partitions; i++) { + gpr_mu_init(&sync_array[i].mu); + gpr_cv_init(&sync_array[i].cv); + } +} + +/* Hash ev into an element of sync_array[]. */ +static struct sync_array_s *hash(gpr_event *ev) { + return &sync_array[((uintptr_t)ev) % event_sync_partitions]; +} + +void gpr_event_init(gpr_event *ev) { + gpr_once_init(&event_once, &event_initialize); + ev->state = 0; +} + +void gpr_event_set(gpr_event *ev, void *value) { + struct sync_array_s *s = hash(ev); + gpr_mu_lock(&s->mu); + GPR_ASSERT(gpr_atm_acq_load(&ev->state) == 0); + gpr_atm_rel_store(&ev->state, (gpr_atm)value); + gpr_cv_broadcast(&s->cv); + gpr_mu_unlock(&s->mu); + GPR_ASSERT(value != NULL); +} + +void *gpr_event_get(gpr_event *ev) { + return (void *)gpr_atm_acq_load(&ev->state); +} + +void *gpr_event_wait(gpr_event *ev, gpr_timespec abs_deadline) { + void *result = (void *)gpr_atm_acq_load(&ev->state); + if (result == NULL) { + struct sync_array_s *s = hash(ev); + gpr_mu_lock(&s->mu); + do { + result = (void *)gpr_atm_acq_load(&ev->state); + } while (result == NULL && !gpr_cv_wait(&s->cv, &s->mu, abs_deadline)); + gpr_mu_unlock(&s->mu); + } + return result; +} + +void gpr_ref_init(gpr_refcount *r, int n) { gpr_atm_rel_store(&r->count, n); } + +void gpr_ref(gpr_refcount *r) { gpr_atm_no_barrier_fetch_add(&r->count, 1); } + +void gpr_ref_non_zero(gpr_refcount *r) { +#ifndef NDEBUG + gpr_atm prior = gpr_atm_no_barrier_fetch_add(&r->count, 1); + assert(prior > 0); +#else + gpr_ref(r); +#endif +} + +void gpr_refn(gpr_refcount *r, int n) { + gpr_atm_no_barrier_fetch_add(&r->count, n); +} + +int gpr_unref(gpr_refcount *r) { + gpr_atm prior = gpr_atm_full_fetch_add(&r->count, -1); + GPR_ASSERT(prior > 0); + return prior == 1; +} + +int gpr_ref_is_unique(gpr_refcount *r) { + return gpr_atm_acq_load(&r->count) == 1; +} + +void gpr_stats_init(gpr_stats_counter *c, intptr_t n) { + gpr_atm_rel_store(&c->value, n); +} + +void gpr_stats_inc(gpr_stats_counter *c, intptr_t inc) { + gpr_atm_no_barrier_fetch_add(&c->value, inc); +} + +intptr_t gpr_stats_read(const gpr_stats_counter *c) { + /* don't need acquire-load, but we have no no-barrier load yet */ + return gpr_atm_acq_load(&c->value); +} diff --git a/src/core/lib/support/sync_posix.c b/src/core/lib/support/sync_posix.c deleted file mode 100644 index 62d800b18c..0000000000 --- a/src/core/lib/support/sync_posix.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_POSIX_SYNC - -#include -#include -#include -#include -#include -#include "src/core/lib/profiling/timers.h" - -#ifdef GPR_LOW_LEVEL_COUNTERS -gpr_atm gpr_mu_locks = 0; -gpr_atm gpr_counter_atm_cas = 0; -gpr_atm gpr_counter_atm_add = 0; -#endif - -void gpr_mu_init(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_init(mu, NULL) == 0); } - -void gpr_mu_destroy(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } - -void gpr_mu_lock(gpr_mu* mu) { -#ifdef GPR_LOW_LEVEL_COUNTERS - GPR_ATM_INC_COUNTER(gpr_mu_locks); -#endif - GPR_TIMER_BEGIN("gpr_mu_lock", 0); - GPR_ASSERT(pthread_mutex_lock(mu) == 0); - GPR_TIMER_END("gpr_mu_lock", 0); -} - -void gpr_mu_unlock(gpr_mu* mu) { - GPR_TIMER_BEGIN("gpr_mu_unlock", 0); - GPR_ASSERT(pthread_mutex_unlock(mu) == 0); - GPR_TIMER_END("gpr_mu_unlock", 0); -} - -int gpr_mu_trylock(gpr_mu* mu) { - int err; - GPR_TIMER_BEGIN("gpr_mu_trylock", 0); - err = pthread_mutex_trylock(mu); - GPR_ASSERT(err == 0 || err == EBUSY); - GPR_TIMER_END("gpr_mu_trylock", 0); - return err == 0; -} - -/*----------------------------------------*/ - -void gpr_cv_init(gpr_cv* cv) { GPR_ASSERT(pthread_cond_init(cv, NULL) == 0); } - -void gpr_cv_destroy(gpr_cv* cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } - -int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { - int err = 0; - if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == - 0) { - err = pthread_cond_wait(cv, mu); - } else { - struct timespec abs_deadline_ts; - abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); - abs_deadline_ts.tv_sec = (time_t)abs_deadline.tv_sec; - abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec; - err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts); - } - GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN); - return err == ETIMEDOUT; -} - -void gpr_cv_signal(gpr_cv* cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); } - -void gpr_cv_broadcast(gpr_cv* cv) { - GPR_ASSERT(pthread_cond_broadcast(cv) == 0); -} - -/*----------------------------------------*/ - -void gpr_once_init(gpr_once* once, void (*init_function)(void)) { - GPR_ASSERT(pthread_once(once, init_function) == 0); -} - -#endif /* GRP_POSIX_SYNC */ diff --git a/src/core/lib/support/sync_posix.cc b/src/core/lib/support/sync_posix.cc new file mode 100644 index 0000000000..62d800b18c --- /dev/null +++ b/src/core/lib/support/sync_posix.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include +#include "src/core/lib/profiling/timers.h" + +#ifdef GPR_LOW_LEVEL_COUNTERS +gpr_atm gpr_mu_locks = 0; +gpr_atm gpr_counter_atm_cas = 0; +gpr_atm gpr_counter_atm_add = 0; +#endif + +void gpr_mu_init(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_init(mu, NULL) == 0); } + +void gpr_mu_destroy(gpr_mu* mu) { GPR_ASSERT(pthread_mutex_destroy(mu) == 0); } + +void gpr_mu_lock(gpr_mu* mu) { +#ifdef GPR_LOW_LEVEL_COUNTERS + GPR_ATM_INC_COUNTER(gpr_mu_locks); +#endif + GPR_TIMER_BEGIN("gpr_mu_lock", 0); + GPR_ASSERT(pthread_mutex_lock(mu) == 0); + GPR_TIMER_END("gpr_mu_lock", 0); +} + +void gpr_mu_unlock(gpr_mu* mu) { + GPR_TIMER_BEGIN("gpr_mu_unlock", 0); + GPR_ASSERT(pthread_mutex_unlock(mu) == 0); + GPR_TIMER_END("gpr_mu_unlock", 0); +} + +int gpr_mu_trylock(gpr_mu* mu) { + int err; + GPR_TIMER_BEGIN("gpr_mu_trylock", 0); + err = pthread_mutex_trylock(mu); + GPR_ASSERT(err == 0 || err == EBUSY); + GPR_TIMER_END("gpr_mu_trylock", 0); + return err == 0; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv* cv) { GPR_ASSERT(pthread_cond_init(cv, NULL) == 0); } + +void gpr_cv_destroy(gpr_cv* cv) { GPR_ASSERT(pthread_cond_destroy(cv) == 0); } + +int gpr_cv_wait(gpr_cv* cv, gpr_mu* mu, gpr_timespec abs_deadline) { + int err = 0; + if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == + 0) { + err = pthread_cond_wait(cv, mu); + } else { + struct timespec abs_deadline_ts; + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); + abs_deadline_ts.tv_sec = (time_t)abs_deadline.tv_sec; + abs_deadline_ts.tv_nsec = abs_deadline.tv_nsec; + err = pthread_cond_timedwait(cv, mu, &abs_deadline_ts); + } + GPR_ASSERT(err == 0 || err == ETIMEDOUT || err == EAGAIN); + return err == ETIMEDOUT; +} + +void gpr_cv_signal(gpr_cv* cv) { GPR_ASSERT(pthread_cond_signal(cv) == 0); } + +void gpr_cv_broadcast(gpr_cv* cv) { + GPR_ASSERT(pthread_cond_broadcast(cv) == 0); +} + +/*----------------------------------------*/ + +void gpr_once_init(gpr_once* once, void (*init_function)(void)) { + GPR_ASSERT(pthread_once(once, init_function) == 0); +} + +#endif /* GRP_POSIX_SYNC */ diff --git a/src/core/lib/support/sync_windows.c b/src/core/lib/support/sync_windows.c deleted file mode 100644 index 008c5aecba..0000000000 --- a/src/core/lib/support/sync_windows.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Win32 code for gpr synchronization support. */ - -#include - -#ifdef GPR_WINDOWS - -#include -#include -#include - -void gpr_mu_init(gpr_mu *mu) { - InitializeCriticalSection(&mu->cs); - mu->locked = 0; -} - -void gpr_mu_destroy(gpr_mu *mu) { DeleteCriticalSection(&mu->cs); } - -void gpr_mu_lock(gpr_mu *mu) { - EnterCriticalSection(&mu->cs); - GPR_ASSERT(!mu->locked); - mu->locked = 1; -} - -void gpr_mu_unlock(gpr_mu *mu) { - mu->locked = 0; - LeaveCriticalSection(&mu->cs); -} - -int gpr_mu_trylock(gpr_mu *mu) { - int result = TryEnterCriticalSection(&mu->cs); - if (result) { - if (mu->locked) { /* This thread already holds the lock. */ - LeaveCriticalSection(&mu->cs); /* Decrement lock count. */ - result = 0; /* Indicate failure */ - } - mu->locked = 1; - } - return result; -} - -/*----------------------------------------*/ - -void gpr_cv_init(gpr_cv *cv) { InitializeConditionVariable(cv); } - -void gpr_cv_destroy(gpr_cv *cv) { - /* Condition variables don't need destruction in Win32. */ -} - -int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { - int timeout = 0; - DWORD timeout_max_ms; - mu->locked = 0; - if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == - 0) { - SleepConditionVariableCS(cv, &mu->cs, INFINITE); - } else { - abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); - gpr_timespec now = gpr_now(abs_deadline.clock_type); - int64_t now_ms = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000; - int64_t deadline_ms = - (int64_t)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; - if (now_ms >= deadline_ms) { - timeout = 1; - } else { - if ((deadline_ms - now_ms) >= INFINITE) { - timeout_max_ms = INFINITE - 1; - } else { - timeout_max_ms = (DWORD)(deadline_ms - now_ms); - } - timeout = (SleepConditionVariableCS(cv, &mu->cs, timeout_max_ms) == 0 && - GetLastError() == ERROR_TIMEOUT); - } - } - mu->locked = 1; - return timeout; -} - -void gpr_cv_signal(gpr_cv *cv) { WakeConditionVariable(cv); } - -void gpr_cv_broadcast(gpr_cv *cv) { WakeAllConditionVariable(cv); } - -/*----------------------------------------*/ - -static void *dummy; -struct run_once_func_arg { - void (*init_function)(void); -}; -static BOOL CALLBACK run_once_func(gpr_once *once, void *v, void **pv) { - struct run_once_func_arg *arg = v; - (*arg->init_function)(); - return 1; -} - -void gpr_once_init(gpr_once *once, void (*init_function)(void)) { - struct run_once_func_arg arg; - arg.init_function = init_function; - InitOnceExecuteOnce(once, run_once_func, &arg, &dummy); -} - -#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/sync_windows.cc b/src/core/lib/support/sync_windows.cc new file mode 100644 index 0000000000..008c5aecba --- /dev/null +++ b/src/core/lib/support/sync_windows.cc @@ -0,0 +1,118 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Win32 code for gpr synchronization support. */ + +#include + +#ifdef GPR_WINDOWS + +#include +#include +#include + +void gpr_mu_init(gpr_mu *mu) { + InitializeCriticalSection(&mu->cs); + mu->locked = 0; +} + +void gpr_mu_destroy(gpr_mu *mu) { DeleteCriticalSection(&mu->cs); } + +void gpr_mu_lock(gpr_mu *mu) { + EnterCriticalSection(&mu->cs); + GPR_ASSERT(!mu->locked); + mu->locked = 1; +} + +void gpr_mu_unlock(gpr_mu *mu) { + mu->locked = 0; + LeaveCriticalSection(&mu->cs); +} + +int gpr_mu_trylock(gpr_mu *mu) { + int result = TryEnterCriticalSection(&mu->cs); + if (result) { + if (mu->locked) { /* This thread already holds the lock. */ + LeaveCriticalSection(&mu->cs); /* Decrement lock count. */ + result = 0; /* Indicate failure */ + } + mu->locked = 1; + } + return result; +} + +/*----------------------------------------*/ + +void gpr_cv_init(gpr_cv *cv) { InitializeConditionVariable(cv); } + +void gpr_cv_destroy(gpr_cv *cv) { + /* Condition variables don't need destruction in Win32. */ +} + +int gpr_cv_wait(gpr_cv *cv, gpr_mu *mu, gpr_timespec abs_deadline) { + int timeout = 0; + DWORD timeout_max_ms; + mu->locked = 0; + if (gpr_time_cmp(abs_deadline, gpr_inf_future(abs_deadline.clock_type)) == + 0) { + SleepConditionVariableCS(cv, &mu->cs, INFINITE); + } else { + abs_deadline = gpr_convert_clock_type(abs_deadline, GPR_CLOCK_REALTIME); + gpr_timespec now = gpr_now(abs_deadline.clock_type); + int64_t now_ms = (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000; + int64_t deadline_ms = + (int64_t)abs_deadline.tv_sec * 1000 + abs_deadline.tv_nsec / 1000000; + if (now_ms >= deadline_ms) { + timeout = 1; + } else { + if ((deadline_ms - now_ms) >= INFINITE) { + timeout_max_ms = INFINITE - 1; + } else { + timeout_max_ms = (DWORD)(deadline_ms - now_ms); + } + timeout = (SleepConditionVariableCS(cv, &mu->cs, timeout_max_ms) == 0 && + GetLastError() == ERROR_TIMEOUT); + } + } + mu->locked = 1; + return timeout; +} + +void gpr_cv_signal(gpr_cv *cv) { WakeConditionVariable(cv); } + +void gpr_cv_broadcast(gpr_cv *cv) { WakeAllConditionVariable(cv); } + +/*----------------------------------------*/ + +static void *dummy; +struct run_once_func_arg { + void (*init_function)(void); +}; +static BOOL CALLBACK run_once_func(gpr_once *once, void *v, void **pv) { + struct run_once_func_arg *arg = v; + (*arg->init_function)(); + return 1; +} + +void gpr_once_init(gpr_once *once, void (*init_function)(void)) { + struct run_once_func_arg arg; + arg.init_function = init_function; + InitOnceExecuteOnce(once, run_once_func, &arg, &dummy); +} + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/thd.c b/src/core/lib/support/thd.c deleted file mode 100644 index ca62615d65..0000000000 --- a/src/core/lib/support/thd.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Posix implementation for gpr threads. */ - -#include - -#include - -enum { GPR_THD_JOINABLE = 1 }; - -gpr_thd_options gpr_thd_options_default(void) { - gpr_thd_options options; - memset(&options, 0, sizeof(options)); - return options; -} - -void gpr_thd_options_set_detached(gpr_thd_options* options) { - options->flags &= ~GPR_THD_JOINABLE; -} - -void gpr_thd_options_set_joinable(gpr_thd_options* options) { - options->flags |= GPR_THD_JOINABLE; -} - -int gpr_thd_options_is_detached(const gpr_thd_options* options) { - if (!options) return 1; - return (options->flags & GPR_THD_JOINABLE) == 0; -} - -int gpr_thd_options_is_joinable(const gpr_thd_options* options) { - if (!options) return 0; - return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; -} diff --git a/src/core/lib/support/thd.cc b/src/core/lib/support/thd.cc new file mode 100644 index 0000000000..ca62615d65 --- /dev/null +++ b/src/core/lib/support/thd.cc @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#include + +enum { GPR_THD_JOINABLE = 1 }; + +gpr_thd_options gpr_thd_options_default(void) { + gpr_thd_options options; + memset(&options, 0, sizeof(options)); + return options; +} + +void gpr_thd_options_set_detached(gpr_thd_options* options) { + options->flags &= ~GPR_THD_JOINABLE; +} + +void gpr_thd_options_set_joinable(gpr_thd_options* options) { + options->flags |= GPR_THD_JOINABLE; +} + +int gpr_thd_options_is_detached(const gpr_thd_options* options) { + if (!options) return 1; + return (options->flags & GPR_THD_JOINABLE) == 0; +} + +int gpr_thd_options_is_joinable(const gpr_thd_options* options) { + if (!options) return 0; + return (options->flags & GPR_THD_JOINABLE) == GPR_THD_JOINABLE; +} diff --git a/src/core/lib/support/thd_posix.c b/src/core/lib/support/thd_posix.c deleted file mode 100644 index 98afd10df7..0000000000 --- a/src/core/lib/support/thd_posix.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Posix implementation for gpr threads. */ - -#include - -#ifdef GPR_POSIX_SYNC - -#include -#include -#include -#include -#include -#include -#include - -struct thd_arg { - void (*body)(void *arg); /* body of a thread */ - void *arg; /* argument to a thread */ -}; - -/* Body of every thread started via gpr_thd_new. */ -static void *thread_body(void *v) { - struct thd_arg a = *(struct thd_arg *)v; - free(v); - (*a.body)(a.arg); - return NULL; -} - -int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, - const gpr_thd_options *options) { - int thread_started; - pthread_attr_t attr; - pthread_t p; - /* don't use gpr_malloc as we may cause an infinite recursion with - * the profiling code */ - struct thd_arg *a = (struct thd_arg *)malloc(sizeof(*a)); - GPR_ASSERT(a != NULL); - a->body = thd_body; - a->arg = arg; - - GPR_ASSERT(pthread_attr_init(&attr) == 0); - if (gpr_thd_options_is_detached(options)) { - GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == - 0); - } else { - GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == - 0); - } - thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0); - GPR_ASSERT(pthread_attr_destroy(&attr) == 0); - if (!thread_started) { - /* don't use gpr_free, as this was allocated using malloc (see above) */ - free(a); - } - *t = (gpr_thd_id)p; - return thread_started; -} - -gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } - -void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, NULL); } - -#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/support/thd_posix.cc b/src/core/lib/support/thd_posix.cc new file mode 100644 index 0000000000..98afd10df7 --- /dev/null +++ b/src/core/lib/support/thd_posix.cc @@ -0,0 +1,80 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Posix implementation for gpr threads. */ + +#include + +#ifdef GPR_POSIX_SYNC + +#include +#include +#include +#include +#include +#include +#include + +struct thd_arg { + void (*body)(void *arg); /* body of a thread */ + void *arg; /* argument to a thread */ +}; + +/* Body of every thread started via gpr_thd_new. */ +static void *thread_body(void *v) { + struct thd_arg a = *(struct thd_arg *)v; + free(v); + (*a.body)(a.arg); + return NULL; +} + +int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, + const gpr_thd_options *options) { + int thread_started; + pthread_attr_t attr; + pthread_t p; + /* don't use gpr_malloc as we may cause an infinite recursion with + * the profiling code */ + struct thd_arg *a = (struct thd_arg *)malloc(sizeof(*a)); + GPR_ASSERT(a != NULL); + a->body = thd_body; + a->arg = arg; + + GPR_ASSERT(pthread_attr_init(&attr) == 0); + if (gpr_thd_options_is_detached(options)) { + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) == + 0); + } else { + GPR_ASSERT(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE) == + 0); + } + thread_started = (pthread_create(&p, &attr, &thread_body, a) == 0); + GPR_ASSERT(pthread_attr_destroy(&attr) == 0); + if (!thread_started) { + /* don't use gpr_free, as this was allocated using malloc (see above) */ + free(a); + } + *t = (gpr_thd_id)p; + return thread_started; +} + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)pthread_self(); } + +void gpr_thd_join(gpr_thd_id t) { pthread_join((pthread_t)t, NULL); } + +#endif /* GPR_POSIX_SYNC */ diff --git a/src/core/lib/support/thd_windows.c b/src/core/lib/support/thd_windows.c deleted file mode 100644 index 54533e9412..0000000000 --- a/src/core/lib/support/thd_windows.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Windows implementation for gpr threads. */ - -#include - -#ifdef GPR_WINDOWS - -#include -#include -#include -#include - -#if defined(_MSC_VER) -#define thread_local __declspec(thread) -#elif defined(__GNUC__) -#define thread_local __thread -#else -#error "Unknown compiler - please file a bug report" -#endif - -struct thd_info { - void (*body)(void *arg); /* body of a thread */ - void *arg; /* argument to a thread */ - HANDLE join_event; /* if joinable, the join event */ - int joinable; /* true if not detached */ -}; - -static thread_local struct thd_info *g_thd_info; - -/* Destroys a thread info */ -static void destroy_thread(struct thd_info *t) { - if (t->joinable) CloseHandle(t->join_event); - gpr_free(t); -} - -/* Body of every thread started via gpr_thd_new. */ -static DWORD WINAPI thread_body(void *v) { - g_thd_info = (struct thd_info *)v; - g_thd_info->body(g_thd_info->arg); - if (g_thd_info->joinable) { - BOOL ret = SetEvent(g_thd_info->join_event); - GPR_ASSERT(ret); - } else { - destroy_thread(g_thd_info); - } - return 0; -} - -int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, - const gpr_thd_options *options) { - HANDLE handle; - struct thd_info *info = gpr_malloc(sizeof(*info)); - info->body = thd_body; - info->arg = arg; - *t = 0; - if (gpr_thd_options_is_joinable(options)) { - info->joinable = 1; - info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (info->join_event == NULL) { - gpr_free(info); - return 0; - } - } else { - info->joinable = 0; - } - handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL); - if (handle == NULL) { - destroy_thread(info); - } else { - *t = (gpr_thd_id)info; - CloseHandle(handle); - } - return handle != NULL; -} - -gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } - -void gpr_thd_join(gpr_thd_id t) { - struct thd_info *info = (struct thd_info *)t; - DWORD ret = WaitForSingleObject(info->join_event, INFINITE); - GPR_ASSERT(ret == WAIT_OBJECT_0); - destroy_thread(info); -} - -#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/thd_windows.cc b/src/core/lib/support/thd_windows.cc new file mode 100644 index 0000000000..54533e9412 --- /dev/null +++ b/src/core/lib/support/thd_windows.cc @@ -0,0 +1,102 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Windows implementation for gpr threads. */ + +#include + +#ifdef GPR_WINDOWS + +#include +#include +#include +#include + +#if defined(_MSC_VER) +#define thread_local __declspec(thread) +#elif defined(__GNUC__) +#define thread_local __thread +#else +#error "Unknown compiler - please file a bug report" +#endif + +struct thd_info { + void (*body)(void *arg); /* body of a thread */ + void *arg; /* argument to a thread */ + HANDLE join_event; /* if joinable, the join event */ + int joinable; /* true if not detached */ +}; + +static thread_local struct thd_info *g_thd_info; + +/* Destroys a thread info */ +static void destroy_thread(struct thd_info *t) { + if (t->joinable) CloseHandle(t->join_event); + gpr_free(t); +} + +/* Body of every thread started via gpr_thd_new. */ +static DWORD WINAPI thread_body(void *v) { + g_thd_info = (struct thd_info *)v; + g_thd_info->body(g_thd_info->arg); + if (g_thd_info->joinable) { + BOOL ret = SetEvent(g_thd_info->join_event); + GPR_ASSERT(ret); + } else { + destroy_thread(g_thd_info); + } + return 0; +} + +int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, + const gpr_thd_options *options) { + HANDLE handle; + struct thd_info *info = gpr_malloc(sizeof(*info)); + info->body = thd_body; + info->arg = arg; + *t = 0; + if (gpr_thd_options_is_joinable(options)) { + info->joinable = 1; + info->join_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (info->join_event == NULL) { + gpr_free(info); + return 0; + } + } else { + info->joinable = 0; + } + handle = CreateThread(NULL, 64 * 1024, thread_body, info, 0, NULL); + if (handle == NULL) { + destroy_thread(info); + } else { + *t = (gpr_thd_id)info; + CloseHandle(handle); + } + return handle != NULL; +} + +gpr_thd_id gpr_thd_currentid(void) { return (gpr_thd_id)g_thd_info; } + +void gpr_thd_join(gpr_thd_id t) { + struct thd_info *info = (struct thd_info *)t; + DWORD ret = WaitForSingleObject(info->join_event, INFINITE); + GPR_ASSERT(ret == WAIT_OBJECT_0); + destroy_thread(info); +} + +#endif /* GPR_WINDOWS */ diff --git a/src/core/lib/support/time.c b/src/core/lib/support/time.c deleted file mode 100644 index 6903674d75..0000000000 --- a/src/core/lib/support/time.c +++ /dev/null @@ -1,247 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Generic implementation of time calls. */ - -#include -#include -#include -#include -#include - -int gpr_time_cmp(gpr_timespec a, gpr_timespec b) { - int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); - GPR_ASSERT(a.clock_type == b.clock_type); - if (cmp == 0 && a.tv_sec != INT64_MAX && a.tv_sec != INT64_MIN) { - cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); - } - return cmp; -} - -gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b) { - return gpr_time_cmp(a, b) < 0 ? a : b; -} - -gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) { - return gpr_time_cmp(a, b) > 0 ? a : b; -} - -gpr_timespec gpr_time_0(gpr_clock_type type) { - gpr_timespec out; - out.tv_sec = 0; - out.tv_nsec = 0; - out.clock_type = type; - return out; -} - -gpr_timespec gpr_inf_future(gpr_clock_type type) { - gpr_timespec out; - out.tv_sec = INT64_MAX; - out.tv_nsec = 0; - out.clock_type = type; - return out; -} - -gpr_timespec gpr_inf_past(gpr_clock_type type) { - gpr_timespec out; - out.tv_sec = INT64_MIN; - out.tv_nsec = 0; - out.clock_type = type; - return out; -} - -static gpr_timespec to_seconds_from_sub_second_time(int64_t time_in_units, - int64_t units_per_sec, - gpr_clock_type type) { - gpr_timespec out; - if (time_in_units == INT64_MAX) { - out = gpr_inf_future(type); - } else if (time_in_units == INT64_MIN) { - out = gpr_inf_past(type); - } else { - if (time_in_units >= 0) { - out.tv_sec = time_in_units / units_per_sec; - } else { - out.tv_sec = (-((units_per_sec - 1) - (time_in_units + units_per_sec)) / - units_per_sec) - - 1; - } - out.tv_nsec = (int32_t)((time_in_units - out.tv_sec * units_per_sec) * - GPR_NS_PER_SEC / units_per_sec); - out.clock_type = type; - } - return out; -} - -static gpr_timespec to_seconds_from_above_second_time(int64_t time_in_units, - int64_t secs_per_unit, - gpr_clock_type type) { - gpr_timespec out; - if (time_in_units >= INT64_MAX / secs_per_unit) { - out = gpr_inf_future(type); - } else if (time_in_units <= INT64_MIN / secs_per_unit) { - out = gpr_inf_past(type); - } else { - out.tv_sec = time_in_units * secs_per_unit; - out.tv_nsec = 0; - out.clock_type = type; - } - return out; -} - -gpr_timespec gpr_time_from_nanos(int64_t ns, gpr_clock_type type) { - return to_seconds_from_sub_second_time(ns, GPR_NS_PER_SEC, type); -} - -gpr_timespec gpr_time_from_micros(int64_t us, gpr_clock_type type) { - return to_seconds_from_sub_second_time(us, GPR_US_PER_SEC, type); -} - -gpr_timespec gpr_time_from_millis(int64_t ms, gpr_clock_type type) { - return to_seconds_from_sub_second_time(ms, GPR_MS_PER_SEC, type); -} - -gpr_timespec gpr_time_from_seconds(int64_t s, gpr_clock_type type) { - return to_seconds_from_sub_second_time(s, 1, type); -} - -gpr_timespec gpr_time_from_minutes(int64_t m, gpr_clock_type type) { - return to_seconds_from_above_second_time(m, 60, type); -} - -gpr_timespec gpr_time_from_hours(int64_t h, gpr_clock_type type) { - return to_seconds_from_above_second_time(h, 3600, type); -} - -gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { - gpr_timespec sum; - int64_t inc = 0; - GPR_ASSERT(b.clock_type == GPR_TIMESPAN); - sum.clock_type = a.clock_type; - sum.tv_nsec = a.tv_nsec + b.tv_nsec; - if (sum.tv_nsec >= GPR_NS_PER_SEC) { - sum.tv_nsec -= GPR_NS_PER_SEC; - inc++; - } - if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { - sum = a; - } else if (b.tv_sec == INT64_MAX || - (b.tv_sec >= 0 && a.tv_sec >= INT64_MAX - b.tv_sec)) { - sum = gpr_inf_future(sum.clock_type); - } else if (b.tv_sec == INT64_MIN || - (b.tv_sec <= 0 && a.tv_sec <= INT64_MIN - b.tv_sec)) { - sum = gpr_inf_past(sum.clock_type); - } else { - sum.tv_sec = a.tv_sec + b.tv_sec; - if (inc != 0 && sum.tv_sec == INT64_MAX - 1) { - sum = gpr_inf_future(sum.clock_type); - } else { - sum.tv_sec += inc; - } - } - return sum; -} - -gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { - gpr_timespec diff; - int64_t dec = 0; - if (b.clock_type == GPR_TIMESPAN) { - diff.clock_type = a.clock_type; - } else { - GPR_ASSERT(a.clock_type == b.clock_type); - diff.clock_type = GPR_TIMESPAN; - } - diff.tv_nsec = a.tv_nsec - b.tv_nsec; - if (diff.tv_nsec < 0) { - diff.tv_nsec += GPR_NS_PER_SEC; - dec++; - } - if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { - diff = a; - } else if (b.tv_sec == INT64_MIN || - (b.tv_sec <= 0 && a.tv_sec >= INT64_MAX + b.tv_sec)) { - diff = gpr_inf_future(GPR_CLOCK_REALTIME); - } else if (b.tv_sec == INT64_MAX || - (b.tv_sec >= 0 && a.tv_sec <= INT64_MIN + b.tv_sec)) { - diff = gpr_inf_past(GPR_CLOCK_REALTIME); - } else { - diff.tv_sec = a.tv_sec - b.tv_sec; - if (dec != 0 && diff.tv_sec == INT64_MIN + 1) { - diff = gpr_inf_past(GPR_CLOCK_REALTIME); - } else { - diff.tv_sec -= dec; - } - } - return diff; -} - -int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) { - int cmp_ab; - - GPR_ASSERT(a.clock_type == b.clock_type); - GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN); - - cmp_ab = gpr_time_cmp(a, b); - if (cmp_ab == 0) return 1; - if (cmp_ab < 0) { - return gpr_time_cmp(gpr_time_sub(b, a), threshold) <= 0; - } else { - return gpr_time_cmp(gpr_time_sub(a, b), threshold) <= 0; - } -} - -int32_t gpr_time_to_millis(gpr_timespec t) { - if (t.tv_sec >= 2147483) { - if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) { - return 2147483 * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS; - } - return 2147483647; - } else if (t.tv_sec <= -2147483) { - /* TODO(ctiller): correct handling here (it's so far in the past do we - care?) */ - return -2147483647; - } else { - return (int32_t)(t.tv_sec * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS); - } -} - -double gpr_timespec_to_micros(gpr_timespec t) { - return (double)t.tv_sec * GPR_US_PER_SEC + t.tv_nsec * 1e-3; -} - -gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) { - if (t.clock_type == clock_type) { - return t; - } - - if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) { - t.clock_type = clock_type; - return t; - } - - if (clock_type == GPR_TIMESPAN) { - return gpr_time_sub(t, gpr_now(t.clock_type)); - } - - if (t.clock_type == GPR_TIMESPAN) { - return gpr_time_add(gpr_now(clock_type), t); - } - - return gpr_time_add(gpr_now(clock_type), - gpr_time_sub(t, gpr_now(t.clock_type))); -} diff --git a/src/core/lib/support/time.cc b/src/core/lib/support/time.cc new file mode 100644 index 0000000000..6903674d75 --- /dev/null +++ b/src/core/lib/support/time.cc @@ -0,0 +1,247 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Generic implementation of time calls. */ + +#include +#include +#include +#include +#include + +int gpr_time_cmp(gpr_timespec a, gpr_timespec b) { + int cmp = (a.tv_sec > b.tv_sec) - (a.tv_sec < b.tv_sec); + GPR_ASSERT(a.clock_type == b.clock_type); + if (cmp == 0 && a.tv_sec != INT64_MAX && a.tv_sec != INT64_MIN) { + cmp = (a.tv_nsec > b.tv_nsec) - (a.tv_nsec < b.tv_nsec); + } + return cmp; +} + +gpr_timespec gpr_time_min(gpr_timespec a, gpr_timespec b) { + return gpr_time_cmp(a, b) < 0 ? a : b; +} + +gpr_timespec gpr_time_max(gpr_timespec a, gpr_timespec b) { + return gpr_time_cmp(a, b) > 0 ? a : b; +} + +gpr_timespec gpr_time_0(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = 0; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_future(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = INT64_MAX; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +gpr_timespec gpr_inf_past(gpr_clock_type type) { + gpr_timespec out; + out.tv_sec = INT64_MIN; + out.tv_nsec = 0; + out.clock_type = type; + return out; +} + +static gpr_timespec to_seconds_from_sub_second_time(int64_t time_in_units, + int64_t units_per_sec, + gpr_clock_type type) { + gpr_timespec out; + if (time_in_units == INT64_MAX) { + out = gpr_inf_future(type); + } else if (time_in_units == INT64_MIN) { + out = gpr_inf_past(type); + } else { + if (time_in_units >= 0) { + out.tv_sec = time_in_units / units_per_sec; + } else { + out.tv_sec = (-((units_per_sec - 1) - (time_in_units + units_per_sec)) / + units_per_sec) - + 1; + } + out.tv_nsec = (int32_t)((time_in_units - out.tv_sec * units_per_sec) * + GPR_NS_PER_SEC / units_per_sec); + out.clock_type = type; + } + return out; +} + +static gpr_timespec to_seconds_from_above_second_time(int64_t time_in_units, + int64_t secs_per_unit, + gpr_clock_type type) { + gpr_timespec out; + if (time_in_units >= INT64_MAX / secs_per_unit) { + out = gpr_inf_future(type); + } else if (time_in_units <= INT64_MIN / secs_per_unit) { + out = gpr_inf_past(type); + } else { + out.tv_sec = time_in_units * secs_per_unit; + out.tv_nsec = 0; + out.clock_type = type; + } + return out; +} + +gpr_timespec gpr_time_from_nanos(int64_t ns, gpr_clock_type type) { + return to_seconds_from_sub_second_time(ns, GPR_NS_PER_SEC, type); +} + +gpr_timespec gpr_time_from_micros(int64_t us, gpr_clock_type type) { + return to_seconds_from_sub_second_time(us, GPR_US_PER_SEC, type); +} + +gpr_timespec gpr_time_from_millis(int64_t ms, gpr_clock_type type) { + return to_seconds_from_sub_second_time(ms, GPR_MS_PER_SEC, type); +} + +gpr_timespec gpr_time_from_seconds(int64_t s, gpr_clock_type type) { + return to_seconds_from_sub_second_time(s, 1, type); +} + +gpr_timespec gpr_time_from_minutes(int64_t m, gpr_clock_type type) { + return to_seconds_from_above_second_time(m, 60, type); +} + +gpr_timespec gpr_time_from_hours(int64_t h, gpr_clock_type type) { + return to_seconds_from_above_second_time(h, 3600, type); +} + +gpr_timespec gpr_time_add(gpr_timespec a, gpr_timespec b) { + gpr_timespec sum; + int64_t inc = 0; + GPR_ASSERT(b.clock_type == GPR_TIMESPAN); + sum.clock_type = a.clock_type; + sum.tv_nsec = a.tv_nsec + b.tv_nsec; + if (sum.tv_nsec >= GPR_NS_PER_SEC) { + sum.tv_nsec -= GPR_NS_PER_SEC; + inc++; + } + if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { + sum = a; + } else if (b.tv_sec == INT64_MAX || + (b.tv_sec >= 0 && a.tv_sec >= INT64_MAX - b.tv_sec)) { + sum = gpr_inf_future(sum.clock_type); + } else if (b.tv_sec == INT64_MIN || + (b.tv_sec <= 0 && a.tv_sec <= INT64_MIN - b.tv_sec)) { + sum = gpr_inf_past(sum.clock_type); + } else { + sum.tv_sec = a.tv_sec + b.tv_sec; + if (inc != 0 && sum.tv_sec == INT64_MAX - 1) { + sum = gpr_inf_future(sum.clock_type); + } else { + sum.tv_sec += inc; + } + } + return sum; +} + +gpr_timespec gpr_time_sub(gpr_timespec a, gpr_timespec b) { + gpr_timespec diff; + int64_t dec = 0; + if (b.clock_type == GPR_TIMESPAN) { + diff.clock_type = a.clock_type; + } else { + GPR_ASSERT(a.clock_type == b.clock_type); + diff.clock_type = GPR_TIMESPAN; + } + diff.tv_nsec = a.tv_nsec - b.tv_nsec; + if (diff.tv_nsec < 0) { + diff.tv_nsec += GPR_NS_PER_SEC; + dec++; + } + if (a.tv_sec == INT64_MAX || a.tv_sec == INT64_MIN) { + diff = a; + } else if (b.tv_sec == INT64_MIN || + (b.tv_sec <= 0 && a.tv_sec >= INT64_MAX + b.tv_sec)) { + diff = gpr_inf_future(GPR_CLOCK_REALTIME); + } else if (b.tv_sec == INT64_MAX || + (b.tv_sec >= 0 && a.tv_sec <= INT64_MIN + b.tv_sec)) { + diff = gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + diff.tv_sec = a.tv_sec - b.tv_sec; + if (dec != 0 && diff.tv_sec == INT64_MIN + 1) { + diff = gpr_inf_past(GPR_CLOCK_REALTIME); + } else { + diff.tv_sec -= dec; + } + } + return diff; +} + +int gpr_time_similar(gpr_timespec a, gpr_timespec b, gpr_timespec threshold) { + int cmp_ab; + + GPR_ASSERT(a.clock_type == b.clock_type); + GPR_ASSERT(threshold.clock_type == GPR_TIMESPAN); + + cmp_ab = gpr_time_cmp(a, b); + if (cmp_ab == 0) return 1; + if (cmp_ab < 0) { + return gpr_time_cmp(gpr_time_sub(b, a), threshold) <= 0; + } else { + return gpr_time_cmp(gpr_time_sub(a, b), threshold) <= 0; + } +} + +int32_t gpr_time_to_millis(gpr_timespec t) { + if (t.tv_sec >= 2147483) { + if (t.tv_sec == 2147483 && t.tv_nsec < 648 * GPR_NS_PER_MS) { + return 2147483 * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS; + } + return 2147483647; + } else if (t.tv_sec <= -2147483) { + /* TODO(ctiller): correct handling here (it's so far in the past do we + care?) */ + return -2147483647; + } else { + return (int32_t)(t.tv_sec * GPR_MS_PER_SEC + t.tv_nsec / GPR_NS_PER_MS); + } +} + +double gpr_timespec_to_micros(gpr_timespec t) { + return (double)t.tv_sec * GPR_US_PER_SEC + t.tv_nsec * 1e-3; +} + +gpr_timespec gpr_convert_clock_type(gpr_timespec t, gpr_clock_type clock_type) { + if (t.clock_type == clock_type) { + return t; + } + + if (t.tv_sec == INT64_MAX || t.tv_sec == INT64_MIN) { + t.clock_type = clock_type; + return t; + } + + if (clock_type == GPR_TIMESPAN) { + return gpr_time_sub(t, gpr_now(t.clock_type)); + } + + if (t.clock_type == GPR_TIMESPAN) { + return gpr_time_add(gpr_now(clock_type), t); + } + + return gpr_time_add(gpr_now(clock_type), + gpr_time_sub(t, gpr_now(t.clock_type))); +} diff --git a/src/core/lib/support/time_posix.c b/src/core/lib/support/time_posix.c deleted file mode 100644 index 3ead40d807..0000000000 --- a/src/core/lib/support/time_posix.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include "src/core/lib/support/time_precise.h" - -#ifdef GPR_POSIX_TIME - -#include -#include -#include -#ifdef __linux__ -#include -#endif -#include -#include -#include -#include "src/core/lib/support/block_annotate.h" - -static struct timespec timespec_from_gpr(gpr_timespec gts) { - struct timespec rv; - if (sizeof(time_t) < sizeof(int64_t)) { - /* fine to assert, as this is only used in gpr_sleep_until */ - GPR_ASSERT(gts.tv_sec <= INT32_MAX && gts.tv_sec >= INT32_MIN); - } - rv.tv_sec = (time_t)gts.tv_sec; - rv.tv_nsec = gts.tv_nsec; - return rv; -} - -#if _POSIX_TIMERS > 0 -static gpr_timespec gpr_from_timespec(struct timespec ts, - gpr_clock_type clock_type) { - /* - * timespec.tv_sec can have smaller size than gpr_timespec.tv_sec, - * but we are only using this function to implement gpr_now - * so there's no need to handle "infinity" values. - */ - gpr_timespec rv; - rv.tv_sec = ts.tv_sec; - rv.tv_nsec = (int32_t)ts.tv_nsec; - rv.clock_type = clock_type; - return rv; -} - -/** maps gpr_clock_type --> clockid_t for clock_gettime */ -static const clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, - CLOCK_REALTIME}; - -void gpr_time_init(void) { gpr_precise_clock_init(); } - -static gpr_timespec now_impl(gpr_clock_type clock_type) { - struct timespec now; - GPR_ASSERT(clock_type != GPR_TIMESPAN); - if (clock_type == GPR_CLOCK_PRECISE) { - gpr_timespec ret; - gpr_precise_clock_now(&ret); - return ret; - } else { -#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) && defined(__linux__) - /* avoid ABI problems by invoking syscalls directly */ - syscall(SYS_clock_gettime, clockid_for_gpr_clock[clock_type], &now); -#else - clock_gettime(clockid_for_gpr_clock[clock_type], &now); -#endif - return gpr_from_timespec(now, clock_type); - } -} -#else -/* For some reason Apple's OSes haven't implemented clock_gettime. */ - -#include -#include -#include - -static double g_time_scale; -static uint64_t g_time_start; - -void gpr_time_init(void) { - mach_timebase_info_data_t tb = {0, 1}; - gpr_precise_clock_init(); - mach_timebase_info(&tb); - g_time_scale = tb.numer; - g_time_scale /= tb.denom; - g_time_start = mach_absolute_time(); -} - -static gpr_timespec now_impl(gpr_clock_type clock) { - gpr_timespec now; - struct timeval now_tv; - double now_dbl; - - now.clock_type = clock; - switch (clock) { - case GPR_CLOCK_REALTIME: - gettimeofday(&now_tv, NULL); - now.tv_sec = now_tv.tv_sec; - now.tv_nsec = now_tv.tv_usec * 1000; - break; - case GPR_CLOCK_MONOTONIC: - now_dbl = ((double)(mach_absolute_time() - g_time_start)) * g_time_scale; - now.tv_sec = (int64_t)(now_dbl * 1e-9); - now.tv_nsec = (int32_t)(now_dbl - ((double)now.tv_sec) * 1e9); - break; - case GPR_CLOCK_PRECISE: - gpr_precise_clock_now(&now); - break; - case GPR_TIMESPAN: - abort(); - } - - return now; -} -#endif - -gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; - -#ifdef GPR_LOW_LEVEL_COUNTERS -gpr_atm gpr_now_call_count; -#endif - -gpr_timespec gpr_now(gpr_clock_type clock_type) { -#ifdef GPR_LOW_LEVEL_COUNTERS - __atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED); -#endif - return gpr_now_impl(clock_type); -} - -void gpr_sleep_until(gpr_timespec until) { - gpr_timespec now; - gpr_timespec delta; - struct timespec delta_ts; - int ns_result; - - for (;;) { - /* We could simplify by using clock_nanosleep instead, but it might be - * slightly less portable. */ - now = gpr_now(until.clock_type); - if (gpr_time_cmp(until, now) <= 0) { - return; - } - - delta = gpr_time_sub(until, now); - delta_ts = timespec_from_gpr(delta); - GRPC_SCHEDULING_START_BLOCKING_REGION; - ns_result = nanosleep(&delta_ts, NULL); - GRPC_SCHEDULING_END_BLOCKING_REGION; - if (ns_result == 0) { - break; - } - } -} - -#endif /* GPR_POSIX_TIME */ diff --git a/src/core/lib/support/time_posix.cc b/src/core/lib/support/time_posix.cc new file mode 100644 index 0000000000..3ead40d807 --- /dev/null +++ b/src/core/lib/support/time_posix.cc @@ -0,0 +1,169 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include "src/core/lib/support/time_precise.h" + +#ifdef GPR_POSIX_TIME + +#include +#include +#include +#ifdef __linux__ +#include +#endif +#include +#include +#include +#include "src/core/lib/support/block_annotate.h" + +static struct timespec timespec_from_gpr(gpr_timespec gts) { + struct timespec rv; + if (sizeof(time_t) < sizeof(int64_t)) { + /* fine to assert, as this is only used in gpr_sleep_until */ + GPR_ASSERT(gts.tv_sec <= INT32_MAX && gts.tv_sec >= INT32_MIN); + } + rv.tv_sec = (time_t)gts.tv_sec; + rv.tv_nsec = gts.tv_nsec; + return rv; +} + +#if _POSIX_TIMERS > 0 +static gpr_timespec gpr_from_timespec(struct timespec ts, + gpr_clock_type clock_type) { + /* + * timespec.tv_sec can have smaller size than gpr_timespec.tv_sec, + * but we are only using this function to implement gpr_now + * so there's no need to handle "infinity" values. + */ + gpr_timespec rv; + rv.tv_sec = ts.tv_sec; + rv.tv_nsec = (int32_t)ts.tv_nsec; + rv.clock_type = clock_type; + return rv; +} + +/** maps gpr_clock_type --> clockid_t for clock_gettime */ +static const clockid_t clockid_for_gpr_clock[] = {CLOCK_MONOTONIC, + CLOCK_REALTIME}; + +void gpr_time_init(void) { gpr_precise_clock_init(); } + +static gpr_timespec now_impl(gpr_clock_type clock_type) { + struct timespec now; + GPR_ASSERT(clock_type != GPR_TIMESPAN); + if (clock_type == GPR_CLOCK_PRECISE) { + gpr_timespec ret; + gpr_precise_clock_now(&ret); + return ret; + } else { +#if defined(GPR_BACKWARDS_COMPATIBILITY_MODE) && defined(__linux__) + /* avoid ABI problems by invoking syscalls directly */ + syscall(SYS_clock_gettime, clockid_for_gpr_clock[clock_type], &now); +#else + clock_gettime(clockid_for_gpr_clock[clock_type], &now); +#endif + return gpr_from_timespec(now, clock_type); + } +} +#else +/* For some reason Apple's OSes haven't implemented clock_gettime. */ + +#include +#include +#include + +static double g_time_scale; +static uint64_t g_time_start; + +void gpr_time_init(void) { + mach_timebase_info_data_t tb = {0, 1}; + gpr_precise_clock_init(); + mach_timebase_info(&tb); + g_time_scale = tb.numer; + g_time_scale /= tb.denom; + g_time_start = mach_absolute_time(); +} + +static gpr_timespec now_impl(gpr_clock_type clock) { + gpr_timespec now; + struct timeval now_tv; + double now_dbl; + + now.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + gettimeofday(&now_tv, NULL); + now.tv_sec = now_tv.tv_sec; + now.tv_nsec = now_tv.tv_usec * 1000; + break; + case GPR_CLOCK_MONOTONIC: + now_dbl = ((double)(mach_absolute_time() - g_time_start)) * g_time_scale; + now.tv_sec = (int64_t)(now_dbl * 1e-9); + now.tv_nsec = (int32_t)(now_dbl - ((double)now.tv_sec) * 1e9); + break; + case GPR_CLOCK_PRECISE: + gpr_precise_clock_now(&now); + break; + case GPR_TIMESPAN: + abort(); + } + + return now; +} +#endif + +gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; + +#ifdef GPR_LOW_LEVEL_COUNTERS +gpr_atm gpr_now_call_count; +#endif + +gpr_timespec gpr_now(gpr_clock_type clock_type) { +#ifdef GPR_LOW_LEVEL_COUNTERS + __atomic_fetch_add(&gpr_now_call_count, 1, __ATOMIC_RELAXED); +#endif + return gpr_now_impl(clock_type); +} + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + struct timespec delta_ts; + int ns_result; + + for (;;) { + /* We could simplify by using clock_nanosleep instead, but it might be + * slightly less portable. */ + now = gpr_now(until.clock_type); + if (gpr_time_cmp(until, now) <= 0) { + return; + } + + delta = gpr_time_sub(until, now); + delta_ts = timespec_from_gpr(delta); + GRPC_SCHEDULING_START_BLOCKING_REGION; + ns_result = nanosleep(&delta_ts, NULL); + GRPC_SCHEDULING_END_BLOCKING_REGION; + if (ns_result == 0) { + break; + } + } +} + +#endif /* GPR_POSIX_TIME */ diff --git a/src/core/lib/support/time_precise.c b/src/core/lib/support/time_precise.c deleted file mode 100644 index 05ef7c59bc..0000000000 --- a/src/core/lib/support/time_precise.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include - -#include "src/core/lib/support/time_precise.h" - -#ifdef GRPC_TIMERS_RDTSC -#if defined(__i386__) -static void gpr_get_cycle_counter(int64_t int *clk) { - int64_t int ret; - __asm__ volatile("rdtsc" : "=A"(ret)); - *clk = ret; -} - -// ---------------------------------------------------------------- -#elif defined(__x86_64__) || defined(__amd64__) -static void gpr_get_cycle_counter(int64_t *clk) { - uint64_t low, high; - __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); - *clk = (int64_t)(high << 32) | (int64_t)low; -} -#endif - -static double cycles_per_second = 0; -static int64_t start_cycle; -void gpr_precise_clock_init(void) { - time_t start; - int64_t end_cycle; - gpr_log(GPR_DEBUG, "Calibrating timers"); - start = time(NULL); - while (time(NULL) == start) - ; - gpr_get_cycle_counter(&start_cycle); - while (time(NULL) <= start + 10) - ; - gpr_get_cycle_counter(&end_cycle); - cycles_per_second = (double)(end_cycle - start_cycle) / 10.0; - gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second); -} - -void gpr_precise_clock_now(gpr_timespec *clk) { - int64_t counter; - double secs; - gpr_get_cycle_counter(&counter); - secs = (double)(counter - start_cycle) / cycles_per_second; - clk->clock_type = GPR_CLOCK_PRECISE; - clk->tv_sec = (int64_t)secs; - clk->tv_nsec = (int32_t)(1e9 * (secs - (double)clk->tv_sec)); -} - -#else /* GRPC_TIMERS_RDTSC */ -void gpr_precise_clock_init(void) {} - -void gpr_precise_clock_now(gpr_timespec *clk) { - *clk = gpr_now(GPR_CLOCK_REALTIME); - clk->clock_type = GPR_CLOCK_PRECISE; -} -#endif /* GRPC_TIMERS_RDTSC */ diff --git a/src/core/lib/support/time_precise.cc b/src/core/lib/support/time_precise.cc new file mode 100644 index 0000000000..05ef7c59bc --- /dev/null +++ b/src/core/lib/support/time_precise.cc @@ -0,0 +1,76 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "src/core/lib/support/time_precise.h" + +#ifdef GRPC_TIMERS_RDTSC +#if defined(__i386__) +static void gpr_get_cycle_counter(int64_t int *clk) { + int64_t int ret; + __asm__ volatile("rdtsc" : "=A"(ret)); + *clk = ret; +} + +// ---------------------------------------------------------------- +#elif defined(__x86_64__) || defined(__amd64__) +static void gpr_get_cycle_counter(int64_t *clk) { + uint64_t low, high; + __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + *clk = (int64_t)(high << 32) | (int64_t)low; +} +#endif + +static double cycles_per_second = 0; +static int64_t start_cycle; +void gpr_precise_clock_init(void) { + time_t start; + int64_t end_cycle; + gpr_log(GPR_DEBUG, "Calibrating timers"); + start = time(NULL); + while (time(NULL) == start) + ; + gpr_get_cycle_counter(&start_cycle); + while (time(NULL) <= start + 10) + ; + gpr_get_cycle_counter(&end_cycle); + cycles_per_second = (double)(end_cycle - start_cycle) / 10.0; + gpr_log(GPR_DEBUG, "... cycles_per_second = %f\n", cycles_per_second); +} + +void gpr_precise_clock_now(gpr_timespec *clk) { + int64_t counter; + double secs; + gpr_get_cycle_counter(&counter); + secs = (double)(counter - start_cycle) / cycles_per_second; + clk->clock_type = GPR_CLOCK_PRECISE; + clk->tv_sec = (int64_t)secs; + clk->tv_nsec = (int32_t)(1e9 * (secs - (double)clk->tv_sec)); +} + +#else /* GRPC_TIMERS_RDTSC */ +void gpr_precise_clock_init(void) {} + +void gpr_precise_clock_now(gpr_timespec *clk) { + *clk = gpr_now(GPR_CLOCK_REALTIME); + clk->clock_type = GPR_CLOCK_PRECISE; +} +#endif /* GRPC_TIMERS_RDTSC */ diff --git a/src/core/lib/support/time_windows.c b/src/core/lib/support/time_windows.c deleted file mode 100644 index 40df3761c0..0000000000 --- a/src/core/lib/support/time_windows.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* Win32 code for gpr time support. */ - -#include - -#ifdef GPR_WINDOWS_TIME - -#include -#include -#include -#include -#include - -#include "src/core/lib/support/block_annotate.h" -#include "src/core/lib/support/time_precise.h" - -static LARGE_INTEGER g_start_time; -static double g_time_scale; - -void gpr_time_init(void) { - LARGE_INTEGER frequency; - QueryPerformanceFrequency(&frequency); - QueryPerformanceCounter(&g_start_time); - g_time_scale = 1.0 / (double)frequency.QuadPart; -} - -static gpr_timespec now_impl(gpr_clock_type clock) { - gpr_timespec now_tv; - LONGLONG diff; - struct _timeb now_tb; - LARGE_INTEGER timestamp; - double now_dbl; - now_tv.clock_type = clock; - switch (clock) { - case GPR_CLOCK_REALTIME: - _ftime_s(&now_tb); - now_tv.tv_sec = (int64_t)now_tb.time; - now_tv.tv_nsec = now_tb.millitm * 1000000; - break; - case GPR_CLOCK_MONOTONIC: - case GPR_CLOCK_PRECISE: - QueryPerformanceCounter(×tamp); - diff = timestamp.QuadPart - g_start_time.QuadPart; - now_dbl = (double)diff * g_time_scale; - now_tv.tv_sec = (int64_t)now_dbl; - now_tv.tv_nsec = (int32_t)((now_dbl - (double)now_tv.tv_sec) * 1e9); - break; - case GPR_TIMESPAN: - abort(); - break; - } - return now_tv; -} - -gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; - -gpr_timespec gpr_now(gpr_clock_type clock_type) { - return gpr_now_impl(clock_type); -} - -void gpr_sleep_until(gpr_timespec until) { - gpr_timespec now; - gpr_timespec delta; - int64_t sleep_millis; - - for (;;) { - /* We could simplify by using clock_nanosleep instead, but it might be - * slightly less portable. */ - now = gpr_now(until.clock_type); - if (gpr_time_cmp(until, now) <= 0) { - return; - } - - delta = gpr_time_sub(until, now); - sleep_millis = - delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; - GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX)); - GRPC_SCHEDULING_START_BLOCKING_REGION; - Sleep((DWORD)sleep_millis); - GRPC_SCHEDULING_END_BLOCKING_REGION; - } -} - -#endif /* GPR_WINDOWS_TIME */ diff --git a/src/core/lib/support/time_windows.cc b/src/core/lib/support/time_windows.cc new file mode 100644 index 0000000000..40df3761c0 --- /dev/null +++ b/src/core/lib/support/time_windows.cc @@ -0,0 +1,101 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Win32 code for gpr time support. */ + +#include + +#ifdef GPR_WINDOWS_TIME + +#include +#include +#include +#include +#include + +#include "src/core/lib/support/block_annotate.h" +#include "src/core/lib/support/time_precise.h" + +static LARGE_INTEGER g_start_time; +static double g_time_scale; + +void gpr_time_init(void) { + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&g_start_time); + g_time_scale = 1.0 / (double)frequency.QuadPart; +} + +static gpr_timespec now_impl(gpr_clock_type clock) { + gpr_timespec now_tv; + LONGLONG diff; + struct _timeb now_tb; + LARGE_INTEGER timestamp; + double now_dbl; + now_tv.clock_type = clock; + switch (clock) { + case GPR_CLOCK_REALTIME: + _ftime_s(&now_tb); + now_tv.tv_sec = (int64_t)now_tb.time; + now_tv.tv_nsec = now_tb.millitm * 1000000; + break; + case GPR_CLOCK_MONOTONIC: + case GPR_CLOCK_PRECISE: + QueryPerformanceCounter(×tamp); + diff = timestamp.QuadPart - g_start_time.QuadPart; + now_dbl = (double)diff * g_time_scale; + now_tv.tv_sec = (int64_t)now_dbl; + now_tv.tv_nsec = (int32_t)((now_dbl - (double)now_tv.tv_sec) * 1e9); + break; + case GPR_TIMESPAN: + abort(); + break; + } + return now_tv; +} + +gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; + +gpr_timespec gpr_now(gpr_clock_type clock_type) { + return gpr_now_impl(clock_type); +} + +void gpr_sleep_until(gpr_timespec until) { + gpr_timespec now; + gpr_timespec delta; + int64_t sleep_millis; + + for (;;) { + /* We could simplify by using clock_nanosleep instead, but it might be + * slightly less portable. */ + now = gpr_now(until.clock_type); + if (gpr_time_cmp(until, now) <= 0) { + return; + } + + delta = gpr_time_sub(until, now); + sleep_millis = + delta.tv_sec * GPR_MS_PER_SEC + delta.tv_nsec / GPR_NS_PER_MS; + GPR_ASSERT((sleep_millis >= 0) && (sleep_millis <= INT_MAX)); + GRPC_SCHEDULING_START_BLOCKING_REGION; + Sleep((DWORD)sleep_millis); + GRPC_SCHEDULING_END_BLOCKING_REGION; + } +} + +#endif /* GPR_WINDOWS_TIME */ diff --git a/src/core/lib/support/tls_pthread.c b/src/core/lib/support/tls_pthread.c deleted file mode 100644 index 9ebee577fe..0000000000 --- a/src/core/lib/support/tls_pthread.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_PTHREAD_TLS - -#include - -intptr_t gpr_tls_set(struct gpr_pthread_thread_local *tls, intptr_t value) { - GPR_ASSERT(0 == pthread_setspecific(tls->key, (void *)value)); - return value; -} - -#endif /* GPR_PTHREAD_TLS */ diff --git a/src/core/lib/support/tls_pthread.cc b/src/core/lib/support/tls_pthread.cc new file mode 100644 index 0000000000..9ebee577fe --- /dev/null +++ b/src/core/lib/support/tls_pthread.cc @@ -0,0 +1,30 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_PTHREAD_TLS + +#include + +intptr_t gpr_tls_set(struct gpr_pthread_thread_local *tls, intptr_t value) { + GPR_ASSERT(0 == pthread_setspecific(tls->key, (void *)value)); + return value; +} + +#endif /* GPR_PTHREAD_TLS */ diff --git a/src/core/lib/support/tmpfile_msys.c b/src/core/lib/support/tmpfile_msys.c deleted file mode 100644 index 614c0a4a18..0000000000 --- a/src/core/lib/support/tmpfile_msys.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_MSYS_TMPFILE - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/support/string_windows.h" -#include "src/core/lib/support/tmpfile.h" - -FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) { - FILE *result = NULL; - char tmp_filename[MAX_PATH]; - UINT success; - - if (tmp_filename_out != NULL) *tmp_filename_out = NULL; - - /* Generate a unique filename with our template + temporary path. */ - success = GetTempFileNameA(".", prefix, 0, tmp_filename); - fprintf(stderr, "success = %d\n", success); - - if (success) { - /* Open a file there. */ - result = fopen(tmp_filename, "wb+"); - fprintf(stderr, "result = %p\n", result); - } - if (result != NULL && tmp_filename_out) { - *tmp_filename_out = gpr_strdup(tmp_filename); - } - - return result; -} - -#endif /* GPR_MSYS_TMPFILE */ diff --git a/src/core/lib/support/tmpfile_msys.cc b/src/core/lib/support/tmpfile_msys.cc new file mode 100644 index 0000000000..614c0a4a18 --- /dev/null +++ b/src/core/lib/support/tmpfile_msys.cc @@ -0,0 +1,58 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_MSYS_TMPFILE + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/support/string_windows.h" +#include "src/core/lib/support/tmpfile.h" + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) { + FILE *result = NULL; + char tmp_filename[MAX_PATH]; + UINT success; + + if (tmp_filename_out != NULL) *tmp_filename_out = NULL; + + /* Generate a unique filename with our template + temporary path. */ + success = GetTempFileNameA(".", prefix, 0, tmp_filename); + fprintf(stderr, "success = %d\n", success); + + if (success) { + /* Open a file there. */ + result = fopen(tmp_filename, "wb+"); + fprintf(stderr, "result = %p\n", result); + } + if (result != NULL && tmp_filename_out) { + *tmp_filename_out = gpr_strdup(tmp_filename); + } + + return result; +} + +#endif /* GPR_MSYS_TMPFILE */ diff --git a/src/core/lib/support/tmpfile_posix.c b/src/core/lib/support/tmpfile_posix.c deleted file mode 100644 index 7ad3af0a57..0000000000 --- a/src/core/lib/support/tmpfile_posix.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_POSIX_TMPFILE - -#include "src/core/lib/support/tmpfile.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/support/string.h" - -FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) { - FILE *result = NULL; - char *filename_template; - int fd; - - if (tmp_filename != NULL) *tmp_filename = NULL; - - gpr_asprintf(&filename_template, "/tmp/%s_XXXXXX", prefix); - GPR_ASSERT(filename_template != NULL); - - fd = mkstemp(filename_template); - if (fd == -1) { - gpr_log(GPR_ERROR, "mkstemp failed for filename_template %s with error %s.", - filename_template, strerror(errno)); - goto end; - } - result = fdopen(fd, "w+"); - if (result == NULL) { - gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", - filename_template, fd, strerror(errno)); - unlink(filename_template); - close(fd); - goto end; - } - -end: - if (result != NULL && tmp_filename != NULL) { - *tmp_filename = filename_template; - } else { - gpr_free(filename_template); - } - return result; -} - -#endif /* GPR_POSIX_TMPFILE */ diff --git a/src/core/lib/support/tmpfile_posix.cc b/src/core/lib/support/tmpfile_posix.cc new file mode 100644 index 0000000000..7ad3af0a57 --- /dev/null +++ b/src/core/lib/support/tmpfile_posix.cc @@ -0,0 +1,70 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_POSIX_TMPFILE + +#include "src/core/lib/support/tmpfile.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/support/string.h" + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename) { + FILE *result = NULL; + char *filename_template; + int fd; + + if (tmp_filename != NULL) *tmp_filename = NULL; + + gpr_asprintf(&filename_template, "/tmp/%s_XXXXXX", prefix); + GPR_ASSERT(filename_template != NULL); + + fd = mkstemp(filename_template); + if (fd == -1) { + gpr_log(GPR_ERROR, "mkstemp failed for filename_template %s with error %s.", + filename_template, strerror(errno)); + goto end; + } + result = fdopen(fd, "w+"); + if (result == NULL) { + gpr_log(GPR_ERROR, "Could not open file %s from fd %d (error = %s).", + filename_template, fd, strerror(errno)); + unlink(filename_template); + close(fd); + goto end; + } + +end: + if (result != NULL && tmp_filename != NULL) { + *tmp_filename = filename_template; + } else { + gpr_free(filename_template); + } + return result; +} + +#endif /* GPR_POSIX_TMPFILE */ diff --git a/src/core/lib/support/tmpfile_windows.c b/src/core/lib/support/tmpfile_windows.c deleted file mode 100644 index 47b4510a72..0000000000 --- a/src/core/lib/support/tmpfile_windows.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#ifdef GPR_WINDOWS_TMPFILE - -#include -#include -#include -#include - -#include -#include -#include - -#include "src/core/lib/support/string_windows.h" -#include "src/core/lib/support/tmpfile.h" - -FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) { - FILE *result = NULL; - LPTSTR template_string = NULL; - TCHAR tmp_path[MAX_PATH]; - TCHAR tmp_filename[MAX_PATH]; - DWORD status; - UINT success; - - if (tmp_filename_out != NULL) *tmp_filename_out = NULL; - - /* Convert our prefix to TCHAR. */ - template_string = gpr_char_to_tchar(prefix); - GPR_ASSERT(template_string); - - /* Get the path to the best temporary folder available. */ - status = GetTempPath(MAX_PATH, tmp_path); - if (status == 0 || status > MAX_PATH) goto end; - - /* Generate a unique filename with our template + temporary path. */ - success = GetTempFileName(tmp_path, template_string, 0, tmp_filename); - if (!success) goto end; - - /* Open a file there. */ - if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end; - -end: - if (result && tmp_filename_out) { - *tmp_filename_out = gpr_tchar_to_char(tmp_filename); - } - - gpr_free(template_string); - return result; -} - -#endif /* GPR_WINDOWS_TMPFILE */ diff --git a/src/core/lib/support/tmpfile_windows.cc b/src/core/lib/support/tmpfile_windows.cc new file mode 100644 index 0000000000..47b4510a72 --- /dev/null +++ b/src/core/lib/support/tmpfile_windows.cc @@ -0,0 +1,69 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#ifdef GPR_WINDOWS_TMPFILE + +#include +#include +#include +#include + +#include +#include +#include + +#include "src/core/lib/support/string_windows.h" +#include "src/core/lib/support/tmpfile.h" + +FILE *gpr_tmpfile(const char *prefix, char **tmp_filename_out) { + FILE *result = NULL; + LPTSTR template_string = NULL; + TCHAR tmp_path[MAX_PATH]; + TCHAR tmp_filename[MAX_PATH]; + DWORD status; + UINT success; + + if (tmp_filename_out != NULL) *tmp_filename_out = NULL; + + /* Convert our prefix to TCHAR. */ + template_string = gpr_char_to_tchar(prefix); + GPR_ASSERT(template_string); + + /* Get the path to the best temporary folder available. */ + status = GetTempPath(MAX_PATH, tmp_path); + if (status == 0 || status > MAX_PATH) goto end; + + /* Generate a unique filename with our template + temporary path. */ + success = GetTempFileName(tmp_path, template_string, 0, tmp_filename); + if (!success) goto end; + + /* Open a file there. */ + if (_tfopen_s(&result, tmp_filename, TEXT("wb+")) != 0) goto end; + +end: + if (result && tmp_filename_out) { + *tmp_filename_out = gpr_tchar_to_char(tmp_filename); + } + + gpr_free(template_string); + return result; +} + +#endif /* GPR_WINDOWS_TMPFILE */ diff --git a/src/core/lib/support/wrap_memcpy.c b/src/core/lib/support/wrap_memcpy.c deleted file mode 100644 index cff056dc3b..0000000000 --- a/src/core/lib/support/wrap_memcpy.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include - -/* Provide a wrapped memcpy for targets that need to be backwards - * compatible with older libc's. - * - * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking. - */ - -#ifdef __linux__ -#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT) -__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); -void *__wrap_memcpy(void *destination, const void *source, size_t num) { - return memcpy(destination, source, num); -} -#else /* !__x86_64__ */ -void *__wrap_memcpy(void *destination, const void *source, size_t num) { - return memmove(destination, source, num); -} -#endif -#endif diff --git a/src/core/lib/support/wrap_memcpy.cc b/src/core/lib/support/wrap_memcpy.cc new file mode 100644 index 0000000000..cff056dc3b --- /dev/null +++ b/src/core/lib/support/wrap_memcpy.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include + +/* Provide a wrapped memcpy for targets that need to be backwards + * compatible with older libc's. + * + * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking. + */ + +#ifdef __linux__ +#if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT) +__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); +void *__wrap_memcpy(void *destination, const void *source, size_t num) { + return memcpy(destination, source, num); +} +#else /* !__x86_64__ */ +void *__wrap_memcpy(void *destination, const void *source, size_t num) { + return memmove(destination, source, num); +} +#endif +#endif diff --git a/src/core/lib/surface/alarm.c b/src/core/lib/surface/alarm.c deleted file mode 100644 index 7712f560b9..0000000000 --- a/src/core/lib/surface/alarm.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include "src/core/lib/surface/alarm_internal.h" - -#include -#include -#include -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/surface/completion_queue.h" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_alarm_refcount = - GRPC_TRACER_INITIALIZER(false, "alarm_refcount"); -#endif - -struct grpc_alarm { - gpr_refcount refs; - grpc_timer alarm; - grpc_closure on_alarm; - grpc_cq_completion completion; - /** completion queue where events about this alarm will be posted */ - grpc_completion_queue *cq; - /** user supplied tag */ - void *tag; -}; - -static void alarm_ref(grpc_alarm *alarm) { gpr_ref(&alarm->refs); } - -static void alarm_unref(grpc_alarm *alarm) { - if (gpr_unref(&alarm->refs)) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - if (alarm->cq != NULL) { - GRPC_CQ_INTERNAL_UNREF(&exec_ctx, alarm->cq, "alarm"); - } - grpc_exec_ctx_finish(&exec_ctx); - gpr_free(alarm); - } -} - -#ifndef NDEBUG -static void alarm_ref_dbg(grpc_alarm *alarm, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "Alarm:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val, - val + 1, reason); - } - - alarm_ref(alarm); -} - -static void alarm_unref_dbg(grpc_alarm *alarm, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "Alarm:%p Unref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val, - val - 1, reason); - } - - alarm_unref(alarm); -} -#endif - -static void alarm_end_completion(grpc_exec_ctx *exec_ctx, void *arg, - grpc_cq_completion *c) { - grpc_alarm *alarm = (grpc_alarm *)arg; - GRPC_ALARM_UNREF(alarm, "dequeue-end-op"); -} - -static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_alarm *alarm = (grpc_alarm *)arg; - - /* We are queuing an op on completion queue. This means, the alarm's structure - cannot be destroyed until the op is dequeued. Adding an extra ref - here and unref'ing when the op is dequeued will achieve this */ - GRPC_ALARM_REF(alarm, "queue-end-op"); - grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, error, alarm_end_completion, - (void *)alarm, &alarm->completion); -} - -grpc_alarm *grpc_alarm_create(void *reserved) { - grpc_alarm *alarm = (grpc_alarm *)gpr_malloc(sizeof(grpc_alarm)); - -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) { - gpr_log(GPR_DEBUG, "Alarm:%p created (ref: 1)", alarm); - } -#endif - - gpr_ref_init(&alarm->refs, 1); - grpc_timer_init_unset(&alarm->alarm); - alarm->cq = NULL; - GRPC_CLOSURE_INIT(&alarm->on_alarm, alarm_cb, alarm, - grpc_schedule_on_exec_ctx); - return alarm; -} - -void grpc_alarm_set(grpc_alarm *alarm, grpc_completion_queue *cq, - gpr_timespec deadline, void *tag, void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_CQ_INTERNAL_REF(cq, "alarm"); - alarm->cq = cq; - alarm->tag = tag; - - GPR_ASSERT(grpc_cq_begin_op(cq, tag)); - grpc_timer_init(&exec_ctx, &alarm->alarm, - gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), - &alarm->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC)); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_alarm_cancel(grpc_alarm *alarm, void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_timer_cancel(&exec_ctx, &alarm->alarm); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_alarm_destroy(grpc_alarm *alarm, void *reserved) { - grpc_alarm_cancel(alarm, reserved); - GRPC_ALARM_UNREF(alarm, "alarm_destroy"); -} diff --git a/src/core/lib/surface/alarm.cc b/src/core/lib/surface/alarm.cc new file mode 100644 index 0000000000..7712f560b9 --- /dev/null +++ b/src/core/lib/surface/alarm.cc @@ -0,0 +1,139 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "src/core/lib/surface/alarm_internal.h" + +#include +#include +#include +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/completion_queue.h" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_alarm_refcount = + GRPC_TRACER_INITIALIZER(false, "alarm_refcount"); +#endif + +struct grpc_alarm { + gpr_refcount refs; + grpc_timer alarm; + grpc_closure on_alarm; + grpc_cq_completion completion; + /** completion queue where events about this alarm will be posted */ + grpc_completion_queue *cq; + /** user supplied tag */ + void *tag; +}; + +static void alarm_ref(grpc_alarm *alarm) { gpr_ref(&alarm->refs); } + +static void alarm_unref(grpc_alarm *alarm) { + if (gpr_unref(&alarm->refs)) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + if (alarm->cq != NULL) { + GRPC_CQ_INTERNAL_UNREF(&exec_ctx, alarm->cq, "alarm"); + } + grpc_exec_ctx_finish(&exec_ctx); + gpr_free(alarm); + } +} + +#ifndef NDEBUG +static void alarm_ref_dbg(grpc_alarm *alarm, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "Alarm:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val, + val + 1, reason); + } + + alarm_ref(alarm); +} + +static void alarm_unref_dbg(grpc_alarm *alarm, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&alarm->refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "Alarm:%p Unref %" PRIdPTR " -> %" PRIdPTR " %s", alarm, val, + val - 1, reason); + } + + alarm_unref(alarm); +} +#endif + +static void alarm_end_completion(grpc_exec_ctx *exec_ctx, void *arg, + grpc_cq_completion *c) { + grpc_alarm *alarm = (grpc_alarm *)arg; + GRPC_ALARM_UNREF(alarm, "dequeue-end-op"); +} + +static void alarm_cb(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_alarm *alarm = (grpc_alarm *)arg; + + /* We are queuing an op on completion queue. This means, the alarm's structure + cannot be destroyed until the op is dequeued. Adding an extra ref + here and unref'ing when the op is dequeued will achieve this */ + GRPC_ALARM_REF(alarm, "queue-end-op"); + grpc_cq_end_op(exec_ctx, alarm->cq, alarm->tag, error, alarm_end_completion, + (void *)alarm, &alarm->completion); +} + +grpc_alarm *grpc_alarm_create(void *reserved) { + grpc_alarm *alarm = (grpc_alarm *)gpr_malloc(sizeof(grpc_alarm)); + +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_alarm_refcount)) { + gpr_log(GPR_DEBUG, "Alarm:%p created (ref: 1)", alarm); + } +#endif + + gpr_ref_init(&alarm->refs, 1); + grpc_timer_init_unset(&alarm->alarm); + alarm->cq = NULL; + GRPC_CLOSURE_INIT(&alarm->on_alarm, alarm_cb, alarm, + grpc_schedule_on_exec_ctx); + return alarm; +} + +void grpc_alarm_set(grpc_alarm *alarm, grpc_completion_queue *cq, + gpr_timespec deadline, void *tag, void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_CQ_INTERNAL_REF(cq, "alarm"); + alarm->cq = cq; + alarm->tag = tag; + + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + grpc_timer_init(&exec_ctx, &alarm->alarm, + gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC), + &alarm->on_alarm, gpr_now(GPR_CLOCK_MONOTONIC)); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_alarm_cancel(grpc_alarm *alarm, void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_timer_cancel(&exec_ctx, &alarm->alarm); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_alarm_destroy(grpc_alarm *alarm, void *reserved) { + grpc_alarm_cancel(alarm, reserved); + GRPC_ALARM_UNREF(alarm, "alarm_destroy"); +} diff --git a/src/core/lib/surface/api_trace.c b/src/core/lib/surface/api_trace.c deleted file mode 100644 index 56973303da..0000000000 --- a/src/core/lib/surface/api_trace.c +++ /dev/null @@ -1,22 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/debug/trace.h" - -grpc_tracer_flag grpc_api_trace = GRPC_TRACER_INITIALIZER(false, "api"); diff --git a/src/core/lib/surface/api_trace.cc b/src/core/lib/surface/api_trace.cc new file mode 100644 index 0000000000..56973303da --- /dev/null +++ b/src/core/lib/surface/api_trace.cc @@ -0,0 +1,22 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/debug/trace.h" + +grpc_tracer_flag grpc_api_trace = GRPC_TRACER_INITIALIZER(false, "api"); diff --git a/src/core/lib/surface/byte_buffer.c b/src/core/lib/surface/byte_buffer.c deleted file mode 100644 index 7ed550ef87..0000000000 --- a/src/core/lib/surface/byte_buffer.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include - -#include "src/core/lib/slice/slice_internal.h" - -grpc_byte_buffer *grpc_raw_byte_buffer_create(grpc_slice *slices, - size_t nslices) { - return grpc_raw_compressed_byte_buffer_create(slices, nslices, - GRPC_COMPRESS_NONE); -} - -grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create( - grpc_slice *slices, size_t nslices, - grpc_compression_algorithm compression) { - size_t i; - grpc_byte_buffer *bb = - (grpc_byte_buffer *)gpr_malloc(sizeof(grpc_byte_buffer)); - bb->type = GRPC_BB_RAW; - bb->data.raw.compression = compression; - grpc_slice_buffer_init(&bb->data.raw.slice_buffer); - for (i = 0; i < nslices; i++) { - grpc_slice_ref_internal(slices[i]); - grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slices[i]); - } - return bb; -} - -grpc_byte_buffer *grpc_raw_byte_buffer_from_reader( - grpc_byte_buffer_reader *reader) { - grpc_byte_buffer *bb = - (grpc_byte_buffer *)gpr_malloc(sizeof(grpc_byte_buffer)); - grpc_slice slice; - bb->type = GRPC_BB_RAW; - bb->data.raw.compression = GRPC_COMPRESS_NONE; - grpc_slice_buffer_init(&bb->data.raw.slice_buffer); - - while (grpc_byte_buffer_reader_next(reader, &slice)) { - grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slice); - } - return bb; -} - -grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { - switch (bb->type) { - case GRPC_BB_RAW: - return grpc_raw_compressed_byte_buffer_create( - bb->data.raw.slice_buffer.slices, bb->data.raw.slice_buffer.count, - bb->data.raw.compression); - } - GPR_UNREACHABLE_CODE(return NULL); -} - -void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) { - if (!bb) return; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - switch (bb->type) { - case GRPC_BB_RAW: - grpc_slice_buffer_destroy_internal(&exec_ctx, &bb->data.raw.slice_buffer); - break; - } - gpr_free(bb); - grpc_exec_ctx_finish(&exec_ctx); -} - -size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) { - switch (bb->type) { - case GRPC_BB_RAW: - return bb->data.raw.slice_buffer.length; - } - GPR_UNREACHABLE_CODE(return 0); -} diff --git a/src/core/lib/surface/byte_buffer.cc b/src/core/lib/surface/byte_buffer.cc new file mode 100644 index 0000000000..7ed550ef87 --- /dev/null +++ b/src/core/lib/surface/byte_buffer.cc @@ -0,0 +1,90 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include + +#include "src/core/lib/slice/slice_internal.h" + +grpc_byte_buffer *grpc_raw_byte_buffer_create(grpc_slice *slices, + size_t nslices) { + return grpc_raw_compressed_byte_buffer_create(slices, nslices, + GRPC_COMPRESS_NONE); +} + +grpc_byte_buffer *grpc_raw_compressed_byte_buffer_create( + grpc_slice *slices, size_t nslices, + grpc_compression_algorithm compression) { + size_t i; + grpc_byte_buffer *bb = + (grpc_byte_buffer *)gpr_malloc(sizeof(grpc_byte_buffer)); + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = compression; + grpc_slice_buffer_init(&bb->data.raw.slice_buffer); + for (i = 0; i < nslices; i++) { + grpc_slice_ref_internal(slices[i]); + grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slices[i]); + } + return bb; +} + +grpc_byte_buffer *grpc_raw_byte_buffer_from_reader( + grpc_byte_buffer_reader *reader) { + grpc_byte_buffer *bb = + (grpc_byte_buffer *)gpr_malloc(sizeof(grpc_byte_buffer)); + grpc_slice slice; + bb->type = GRPC_BB_RAW; + bb->data.raw.compression = GRPC_COMPRESS_NONE; + grpc_slice_buffer_init(&bb->data.raw.slice_buffer); + + while (grpc_byte_buffer_reader_next(reader, &slice)) { + grpc_slice_buffer_add(&bb->data.raw.slice_buffer, slice); + } + return bb; +} + +grpc_byte_buffer *grpc_byte_buffer_copy(grpc_byte_buffer *bb) { + switch (bb->type) { + case GRPC_BB_RAW: + return grpc_raw_compressed_byte_buffer_create( + bb->data.raw.slice_buffer.slices, bb->data.raw.slice_buffer.count, + bb->data.raw.compression); + } + GPR_UNREACHABLE_CODE(return NULL); +} + +void grpc_byte_buffer_destroy(grpc_byte_buffer *bb) { + if (!bb) return; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + switch (bb->type) { + case GRPC_BB_RAW: + grpc_slice_buffer_destroy_internal(&exec_ctx, &bb->data.raw.slice_buffer); + break; + } + gpr_free(bb); + grpc_exec_ctx_finish(&exec_ctx); +} + +size_t grpc_byte_buffer_length(grpc_byte_buffer *bb) { + switch (bb->type) { + case GRPC_BB_RAW: + return bb->data.raw.slice_buffer.length; + } + GPR_UNREACHABLE_CODE(return 0); +} diff --git a/src/core/lib/surface/byte_buffer_reader.c b/src/core/lib/surface/byte_buffer_reader.c deleted file mode 100644 index 87bd3239c0..0000000000 --- a/src/core/lib/surface/byte_buffer_reader.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/compression/message_compress.h" -#include "src/core/lib/slice/slice_internal.h" - -static int is_compressed(grpc_byte_buffer *buffer) { - switch (buffer->type) { - case GRPC_BB_RAW: - if (buffer->data.raw.compression == GRPC_COMPRESS_NONE) { - return 0 /* GPR_FALSE */; - } - break; - } - return 1 /* GPR_TRUE */; -} - -int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, - grpc_byte_buffer *buffer) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_buffer decompressed_slices_buffer; - reader->buffer_in = buffer; - switch (reader->buffer_in->type) { - case GRPC_BB_RAW: - grpc_slice_buffer_init(&decompressed_slices_buffer); - if (is_compressed(reader->buffer_in)) { - if (grpc_msg_decompress(&exec_ctx, - reader->buffer_in->data.raw.compression, - &reader->buffer_in->data.raw.slice_buffer, - &decompressed_slices_buffer) == 0) { - gpr_log(GPR_ERROR, - "Unexpected error decompressing data for algorithm with enum " - "value '%d'.", - reader->buffer_in->data.raw.compression); - memset(reader, 0, sizeof(*reader)); - return 0; - } else { /* all fine */ - reader->buffer_out = - grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices, - decompressed_slices_buffer.count); - } - grpc_slice_buffer_destroy_internal(&exec_ctx, - &decompressed_slices_buffer); - } else { /* not compressed, use the input buffer as output */ - reader->buffer_out = reader->buffer_in; - } - reader->current.index = 0; - break; - } - grpc_exec_ctx_finish(&exec_ctx); - return 1; -} - -void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) { - switch (reader->buffer_in->type) { - case GRPC_BB_RAW: - /* keeping the same if-else structure as in the init function */ - if (is_compressed(reader->buffer_in)) { - grpc_byte_buffer_destroy(reader->buffer_out); - } - break; - } -} - -int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, - grpc_slice *slice) { - switch (reader->buffer_in->type) { - case GRPC_BB_RAW: { - grpc_slice_buffer *slice_buffer; - slice_buffer = &reader->buffer_out->data.raw.slice_buffer; - if (reader->current.index < slice_buffer->count) { - *slice = grpc_slice_ref_internal( - slice_buffer->slices[reader->current.index]); - reader->current.index += 1; - return 1; - } - break; - } - } - return 0; -} - -grpc_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader) { - grpc_slice in_slice; - size_t bytes_read = 0; - const size_t input_size = grpc_byte_buffer_length(reader->buffer_out); - grpc_slice out_slice = GRPC_SLICE_MALLOC(input_size); - uint8_t *const outbuf = GRPC_SLICE_START_PTR(out_slice); /* just an alias */ - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) { - const size_t slice_length = GRPC_SLICE_LENGTH(in_slice); - memcpy(&(outbuf[bytes_read]), GRPC_SLICE_START_PTR(in_slice), slice_length); - bytes_read += slice_length; - grpc_slice_unref_internal(&exec_ctx, in_slice); - GPR_ASSERT(bytes_read <= input_size); - } - grpc_exec_ctx_finish(&exec_ctx); - return out_slice; -} diff --git a/src/core/lib/surface/byte_buffer_reader.cc b/src/core/lib/surface/byte_buffer_reader.cc new file mode 100644 index 0000000000..87bd3239c0 --- /dev/null +++ b/src/core/lib/surface/byte_buffer_reader.cc @@ -0,0 +1,125 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/compression/message_compress.h" +#include "src/core/lib/slice/slice_internal.h" + +static int is_compressed(grpc_byte_buffer *buffer) { + switch (buffer->type) { + case GRPC_BB_RAW: + if (buffer->data.raw.compression == GRPC_COMPRESS_NONE) { + return 0 /* GPR_FALSE */; + } + break; + } + return 1 /* GPR_TRUE */; +} + +int grpc_byte_buffer_reader_init(grpc_byte_buffer_reader *reader, + grpc_byte_buffer *buffer) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_buffer decompressed_slices_buffer; + reader->buffer_in = buffer; + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: + grpc_slice_buffer_init(&decompressed_slices_buffer); + if (is_compressed(reader->buffer_in)) { + if (grpc_msg_decompress(&exec_ctx, + reader->buffer_in->data.raw.compression, + &reader->buffer_in->data.raw.slice_buffer, + &decompressed_slices_buffer) == 0) { + gpr_log(GPR_ERROR, + "Unexpected error decompressing data for algorithm with enum " + "value '%d'.", + reader->buffer_in->data.raw.compression); + memset(reader, 0, sizeof(*reader)); + return 0; + } else { /* all fine */ + reader->buffer_out = + grpc_raw_byte_buffer_create(decompressed_slices_buffer.slices, + decompressed_slices_buffer.count); + } + grpc_slice_buffer_destroy_internal(&exec_ctx, + &decompressed_slices_buffer); + } else { /* not compressed, use the input buffer as output */ + reader->buffer_out = reader->buffer_in; + } + reader->current.index = 0; + break; + } + grpc_exec_ctx_finish(&exec_ctx); + return 1; +} + +void grpc_byte_buffer_reader_destroy(grpc_byte_buffer_reader *reader) { + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: + /* keeping the same if-else structure as in the init function */ + if (is_compressed(reader->buffer_in)) { + grpc_byte_buffer_destroy(reader->buffer_out); + } + break; + } +} + +int grpc_byte_buffer_reader_next(grpc_byte_buffer_reader *reader, + grpc_slice *slice) { + switch (reader->buffer_in->type) { + case GRPC_BB_RAW: { + grpc_slice_buffer *slice_buffer; + slice_buffer = &reader->buffer_out->data.raw.slice_buffer; + if (reader->current.index < slice_buffer->count) { + *slice = grpc_slice_ref_internal( + slice_buffer->slices[reader->current.index]); + reader->current.index += 1; + return 1; + } + break; + } + } + return 0; +} + +grpc_slice grpc_byte_buffer_reader_readall(grpc_byte_buffer_reader *reader) { + grpc_slice in_slice; + size_t bytes_read = 0; + const size_t input_size = grpc_byte_buffer_length(reader->buffer_out); + grpc_slice out_slice = GRPC_SLICE_MALLOC(input_size); + uint8_t *const outbuf = GRPC_SLICE_START_PTR(out_slice); /* just an alias */ + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + while (grpc_byte_buffer_reader_next(reader, &in_slice) != 0) { + const size_t slice_length = GRPC_SLICE_LENGTH(in_slice); + memcpy(&(outbuf[bytes_read]), GRPC_SLICE_START_PTR(in_slice), slice_length); + bytes_read += slice_length; + grpc_slice_unref_internal(&exec_ctx, in_slice); + GPR_ASSERT(bytes_read <= input_size); + } + grpc_exec_ctx_finish(&exec_ctx); + return out_slice; +} diff --git a/src/core/lib/surface/call.c b/src/core/lib/surface/call.c deleted file mode 100644 index 74e55d5741..0000000000 --- a/src/core/lib/surface/call.c +++ /dev/null @@ -1,2160 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/compression/algorithm_metadata.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/arena.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/call_test_only.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/surface/completion_queue.h" -#include "src/core/lib/surface/validate_metadata.h" -#include "src/core/lib/transport/error_utils.h" -#include "src/core/lib/transport/metadata.h" -#include "src/core/lib/transport/static_metadata.h" -#include "src/core/lib/transport/transport.h" - -/** The maximum number of concurrent batches possible. - Based upon the maximum number of individually queueable ops in the batch - api: - - initial metadata send - - message send - - status/close send (depending on client/server) - - initial metadata recv - - message recv - - status/close recv (depending on client/server) */ -#define MAX_CONCURRENT_BATCHES 6 - -#define MAX_SEND_EXTRA_METADATA_COUNT 3 - -/* Status data for a request can come from several sources; this - enumerates them all, and acts as a priority sorting for which - status to return to the application - earlier entries override - later ones */ -typedef enum { - /* Status came from the application layer overriding whatever - the wire says */ - STATUS_FROM_API_OVERRIDE = 0, - /* Status came from 'the wire' - or somewhere below the surface - layer */ - STATUS_FROM_WIRE, - /* Status was created by some internal channel stack operation: must come via - add_batch_error */ - STATUS_FROM_CORE, - /* Status was created by some surface error */ - STATUS_FROM_SURFACE, - /* Status came from the server sending status */ - STATUS_FROM_SERVER_STATUS, - STATUS_SOURCE_COUNT -} status_source; - -typedef struct { - bool is_set; - grpc_error *error; -} received_status; - -static gpr_atm pack_received_status(received_status r) { - return r.is_set ? (1 | (gpr_atm)r.error) : 0; -} - -static received_status unpack_received_status(gpr_atm atm) { - return (atm & 1) == 0 - ? (received_status){.is_set = false, .error = GRPC_ERROR_NONE} - : (received_status){.is_set = true, - .error = (grpc_error *)(atm & ~(gpr_atm)1)}; -} - -#define MAX_ERRORS_PER_BATCH 4 - -typedef struct batch_control { - grpc_call *call; - /* Share memory for cq_completion and notify_tag as they are never needed - simultaneously. Each byte used in this data structure count as six bytes - per call, so any savings we can make are worthwhile, - - We use notify_tag to determine whether or not to send notification to the - completion queue. Once we've made that determination, we can reuse the - memory for cq_completion. */ - union { - grpc_cq_completion cq_completion; - struct { - /* Any given op indicates completion by either (a) calling a closure or - (b) sending a notification on the call's completion queue. If - \a is_closure is true, \a tag indicates a closure to be invoked; - otherwise, \a tag indicates the tag to be used in the notification to - be sent to the completion queue. */ - void *tag; - bool is_closure; - } notify_tag; - } completion_data; - grpc_closure start_batch; - grpc_closure finish_batch; - gpr_refcount steps_to_complete; - - grpc_error *errors[MAX_ERRORS_PER_BATCH]; - gpr_atm num_errors; - - grpc_transport_stream_op_batch op; -} batch_control; - -typedef struct { - gpr_mu child_list_mu; - grpc_call *first_child; -} parent_call; - -typedef struct { - grpc_call *parent; - /** siblings: children of the same parent form a list, and this list is - protected under - parent->mu */ - grpc_call *sibling_next; - grpc_call *sibling_prev; -} child_call; - -#define RECV_NONE ((gpr_atm)0) -#define RECV_INITIAL_METADATA_FIRST ((gpr_atm)1) - -struct grpc_call { - gpr_refcount ext_ref; - gpr_arena *arena; - grpc_call_combiner call_combiner; - grpc_completion_queue *cq; - grpc_polling_entity pollent; - grpc_channel *channel; - gpr_timespec start_time; - /* parent_call* */ gpr_atm parent_call_atm; - child_call *child; - - /* client or server call */ - bool is_client; - /** has grpc_call_unref been called */ - bool destroy_called; - /** flag indicating that cancellation is inherited */ - bool cancellation_is_inherited; - /** which ops are in-flight */ - bool sent_initial_metadata; - bool sending_message; - bool sent_final_op; - bool received_initial_metadata; - bool receiving_message; - bool requested_final_op; - gpr_atm any_ops_sent_atm; - gpr_atm received_final_op_atm; - - batch_control *active_batches[MAX_CONCURRENT_BATCHES]; - grpc_transport_stream_op_batch_payload stream_op_payload; - - /* first idx: is_receiving, second idx: is_trailing */ - grpc_metadata_batch metadata_batch[2][2]; - - /* Buffered read metadata waiting to be returned to the application. - Element 0 is initial metadata, element 1 is trailing metadata. */ - grpc_metadata_array *buffered_metadata[2]; - - grpc_metadata compression_md; - - // A char* indicating the peer name. - gpr_atm peer_string; - - /* Packed received call statuses from various sources */ - gpr_atm status[STATUS_SOURCE_COUNT]; - - /* Call data useful used for reporting. Only valid after the call has - * completed */ - grpc_call_final_info final_info; - - /* Compression algorithm for *incoming* data */ - grpc_compression_algorithm incoming_compression_algorithm; - /* Stream compression algorithm for *incoming* data */ - grpc_stream_compression_algorithm incoming_stream_compression_algorithm; - /* Supported encodings (compression algorithms), a bitset */ - uint32_t encodings_accepted_by_peer; - /* Supported stream encodings (stream compression algorithms), a bitset */ - uint32_t stream_encodings_accepted_by_peer; - - /* Contexts for various subsystems (security, tracing, ...). */ - grpc_call_context_element context[GRPC_CONTEXT_COUNT]; - - /* for the client, extra metadata is initial metadata; for the - server, it's trailing metadata */ - grpc_linked_mdelem send_extra_metadata[MAX_SEND_EXTRA_METADATA_COUNT]; - int send_extra_metadata_count; - gpr_timespec send_deadline; - - grpc_slice_buffer_stream sending_stream; - - grpc_byte_stream *receiving_stream; - grpc_byte_buffer **receiving_buffer; - grpc_slice receiving_slice; - grpc_closure receiving_slice_ready; - grpc_closure receiving_stream_ready; - grpc_closure receiving_initial_metadata_ready; - uint32_t test_only_last_message_flags; - - grpc_closure release_call; - - union { - struct { - grpc_status_code *status; - grpc_slice *status_details; - } client; - struct { - int *cancelled; - } server; - } final_op; - - /* recv_state can contain one of the following values: - RECV_NONE : : no initial metadata and messages received - RECV_INITIAL_METADATA_FIRST : received initial metadata first - a batch_control* : received messages first - - +------1------RECV_NONE------3-----+ - | | - | | - v v - RECV_INITIAL_METADATA_FIRST receiving_stream_ready_bctlp - | ^ | ^ - | | | | - +-----2-----+ +-----4-----+ - - For 1, 4: See receiving_initial_metadata_ready() function - For 2, 3: See receiving_stream_ready() function */ - gpr_atm recv_state; -}; - -grpc_tracer_flag grpc_call_error_trace = - GRPC_TRACER_INITIALIZER(false, "call_error"); -grpc_tracer_flag grpc_compression_trace = - GRPC_TRACER_INITIALIZER(false, "compression"); - -#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) -#define CALL_FROM_CALL_STACK(call_stack) (((grpc_call *)(call_stack)) - 1) -#define CALL_ELEM_FROM_CALL(call, idx) \ - grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) -#define CALL_FROM_TOP_ELEM(top_elem) \ - CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem)) - -static void execute_batch(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_transport_stream_op_batch *op, - grpc_closure *start_batch_closure); -static void cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, - status_source source, grpc_status_code status, - const char *description); -static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c, - status_source source, grpc_error *error); -static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack, - grpc_error *error); -static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, - grpc_error *error); -static void get_final_status(grpc_call *call, - void (*set_value)(grpc_status_code code, - void *user_data), - void *set_value_user_data, grpc_slice *details); -static void set_status_value_directly(grpc_status_code status, void *dest); -static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call, - status_source source, grpc_error *error); -static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl); -static void post_batch_completion(grpc_exec_ctx *exec_ctx, batch_control *bctl); -static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl, - grpc_error *error, bool has_cancelled); - -static void add_init_error(grpc_error **composite, grpc_error *new_err) { - if (new_err == GRPC_ERROR_NONE) return; - if (*composite == GRPC_ERROR_NONE) - *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Call creation failed"); - *composite = grpc_error_add_child(*composite, new_err); -} - -void *grpc_call_arena_alloc(grpc_call *call, size_t size) { - return gpr_arena_alloc(call->arena, size); -} - -static parent_call *get_or_create_parent_call(grpc_call *call) { - parent_call *p = (parent_call *)gpr_atm_acq_load(&call->parent_call_atm); - if (p == NULL) { - p = (parent_call *)gpr_arena_alloc(call->arena, sizeof(*p)); - gpr_mu_init(&p->child_list_mu); - if (!gpr_atm_rel_cas(&call->parent_call_atm, (gpr_atm)NULL, (gpr_atm)p)) { - gpr_mu_destroy(&p->child_list_mu); - p = (parent_call *)gpr_atm_acq_load(&call->parent_call_atm); - } - } - return p; -} - -static parent_call *get_parent_call(grpc_call *call) { - return (parent_call *)gpr_atm_acq_load(&call->parent_call_atm); -} - -grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx, - const grpc_call_create_args *args, - grpc_call **out_call) { - size_t i, j; - grpc_error *error = GRPC_ERROR_NONE; - grpc_channel_stack *channel_stack = - grpc_channel_get_channel_stack(args->channel); - grpc_call *call; - GPR_TIMER_BEGIN("grpc_call_create", 0); - size_t initial_size = grpc_channel_get_call_size_estimate(args->channel); - GRPC_STATS_INC_CALL_INITIAL_SIZE(exec_ctx, initial_size); - gpr_arena *arena = gpr_arena_create(initial_size); - call = (grpc_call *)gpr_arena_alloc( - arena, sizeof(grpc_call) + channel_stack->call_stack_size); - gpr_ref_init(&call->ext_ref, 1); - call->arena = arena; - grpc_call_combiner_init(&call->call_combiner); - *out_call = call; - call->channel = args->channel; - call->cq = args->cq; - call->start_time = gpr_now(GPR_CLOCK_MONOTONIC); - /* Always support no compression */ - GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE); - call->is_client = args->server_transport_data == NULL; - if (call->is_client) { - GRPC_STATS_INC_CLIENT_CALLS_CREATED(exec_ctx); - } else { - GRPC_STATS_INC_SERVER_CALLS_CREATED(exec_ctx); - } - call->stream_op_payload.context = call->context; - grpc_slice path = grpc_empty_slice(); - if (call->is_client) { - GPR_ASSERT(args->add_initial_metadata_count < - MAX_SEND_EXTRA_METADATA_COUNT); - for (i = 0; i < args->add_initial_metadata_count; i++) { - call->send_extra_metadata[i].md = args->add_initial_metadata[i]; - if (grpc_slice_eq(GRPC_MDKEY(args->add_initial_metadata[i]), - GRPC_MDSTR_PATH)) { - path = grpc_slice_ref_internal( - GRPC_MDVALUE(args->add_initial_metadata[i])); - } - } - call->send_extra_metadata_count = (int)args->add_initial_metadata_count; - } else { - GPR_ASSERT(args->add_initial_metadata_count == 0); - call->send_extra_metadata_count = 0; - } - for (i = 0; i < 2; i++) { - for (j = 0; j < 2; j++) { - call->metadata_batch[i][j].deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - } - } - gpr_timespec send_deadline = - gpr_convert_clock_type(args->send_deadline, GPR_CLOCK_MONOTONIC); - - bool immediately_cancel = false; - - if (args->parent != NULL) { - child_call *cc = call->child = - (child_call *)gpr_arena_alloc(arena, sizeof(child_call)); - call->child->parent = args->parent; - - GRPC_CALL_INTERNAL_REF(args->parent, "child"); - GPR_ASSERT(call->is_client); - GPR_ASSERT(!args->parent->is_client); - - parent_call *pc = get_or_create_parent_call(args->parent); - - gpr_mu_lock(&pc->child_list_mu); - - if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) { - send_deadline = gpr_time_min( - gpr_convert_clock_type(send_deadline, - args->parent->send_deadline.clock_type), - args->parent->send_deadline); - } - /* for now GRPC_PROPAGATE_TRACING_CONTEXT *MUST* be passed with - * GRPC_PROPAGATE_STATS_CONTEXT */ - /* TODO(ctiller): This should change to use the appropriate census start_op - * call. */ - if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT) { - if (0 == (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)) { - add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Census tracing propagation requested " - "without Census context propagation")); - } - grpc_call_context_set(call, GRPC_CONTEXT_TRACING, - args->parent->context[GRPC_CONTEXT_TRACING].value, - NULL); - } else if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT) { - add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Census context propagation requested " - "without Census tracing propagation")); - } - if (args->propagation_mask & GRPC_PROPAGATE_CANCELLATION) { - call->cancellation_is_inherited = 1; - if (gpr_atm_acq_load(&args->parent->received_final_op_atm)) { - immediately_cancel = true; - } - } - - if (pc->first_child == NULL) { - pc->first_child = call; - cc->sibling_next = cc->sibling_prev = call; - } else { - cc->sibling_next = pc->first_child; - cc->sibling_prev = pc->first_child->child->sibling_prev; - cc->sibling_next->child->sibling_prev = - cc->sibling_prev->child->sibling_next = call; - } - - gpr_mu_unlock(&pc->child_list_mu); - } - - call->send_deadline = send_deadline; - - GRPC_CHANNEL_INTERNAL_REF(args->channel, "call"); - /* initial refcount dropped by grpc_call_unref */ - grpc_call_element_args call_args = { - .call_stack = CALL_STACK_FROM_CALL(call), - .server_transport_data = args->server_transport_data, - .context = call->context, - .path = path, - .start_time = call->start_time, - .deadline = send_deadline, - .arena = call->arena, - .call_combiner = &call->call_combiner}; - add_init_error(&error, grpc_call_stack_init(exec_ctx, channel_stack, 1, - destroy_call, call, &call_args)); - if (error != GRPC_ERROR_NONE) { - cancel_with_error(exec_ctx, call, STATUS_FROM_SURFACE, - GRPC_ERROR_REF(error)); - } - if (immediately_cancel) { - cancel_with_error(exec_ctx, call, STATUS_FROM_API_OVERRIDE, - GRPC_ERROR_CANCELLED); - } - if (args->cq != NULL) { - GPR_ASSERT( - args->pollset_set_alternative == NULL && - "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL."); - GRPC_CQ_INTERNAL_REF(args->cq, "bind"); - call->pollent = - grpc_polling_entity_create_from_pollset(grpc_cq_pollset(args->cq)); - } - if (args->pollset_set_alternative != NULL) { - call->pollent = grpc_polling_entity_create_from_pollset_set( - args->pollset_set_alternative); - } - if (!grpc_polling_entity_is_empty(&call->pollent)) { - grpc_call_stack_set_pollset_or_pollset_set( - exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent); - } - - grpc_slice_unref_internal(exec_ctx, path); - - GPR_TIMER_END("grpc_call_create", 0); - return error; -} - -void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_completion_queue *cq) { - GPR_ASSERT(cq); - - if (grpc_polling_entity_pollset_set(&call->pollent) != NULL) { - gpr_log(GPR_ERROR, "A pollset_set is already registered for this call."); - abort(); - } - call->cq = cq; - GRPC_CQ_INTERNAL_REF(cq, "bind"); - call->pollent = grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)); - grpc_call_stack_set_pollset_or_pollset_set( - exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent); -} - -#ifndef NDEBUG -#define REF_REASON reason -#define REF_ARG , const char *reason -#else -#define REF_REASON "" -#define REF_ARG -#endif -void grpc_call_internal_ref(grpc_call *c REF_ARG) { - GRPC_CALL_STACK_REF(CALL_STACK_FROM_CALL(c), REF_REASON); -} -void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c REF_ARG) { - GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON); -} - -static void release_call(grpc_exec_ctx *exec_ctx, void *call, - grpc_error *error) { - grpc_call *c = (grpc_call *)call; - grpc_channel *channel = c->channel; - grpc_call_combiner_destroy(&c->call_combiner); - gpr_free((char *)c->peer_string); - grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(c->arena)); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call"); -} - -static void set_status_value_directly(grpc_status_code status, void *dest); -static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, - grpc_error *error) { - size_t i; - int ii; - grpc_call *c = (grpc_call *)call; - GPR_TIMER_BEGIN("destroy_call", 0); - for (i = 0; i < 2; i++) { - grpc_metadata_batch_destroy( - exec_ctx, &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); - } - if (c->receiving_stream != NULL) { - grpc_byte_stream_destroy(exec_ctx, c->receiving_stream); - } - parent_call *pc = get_parent_call(c); - if (pc != NULL) { - gpr_mu_destroy(&pc->child_list_mu); - } - for (ii = 0; ii < c->send_extra_metadata_count; ii++) { - GRPC_MDELEM_UNREF(exec_ctx, c->send_extra_metadata[ii].md); - } - for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { - if (c->context[i].destroy) { - c->context[i].destroy(c->context[i].value); - } - } - if (c->cq) { - GRPC_CQ_INTERNAL_UNREF(exec_ctx, c->cq, "bind"); - } - - get_final_status(c, set_status_value_directly, &c->final_info.final_status, - NULL); - c->final_info.stats.latency = - gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); - - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - GRPC_ERROR_UNREF( - unpack_received_status(gpr_atm_acq_load(&c->status[i])).error); - } - - grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->final_info, - GRPC_CLOSURE_INIT(&c->release_call, release_call, c, - grpc_schedule_on_exec_ctx)); - GPR_TIMER_END("destroy_call", 0); -} - -void grpc_call_ref(grpc_call *c) { gpr_ref(&c->ext_ref); } - -void grpc_call_unref(grpc_call *c) { - if (!gpr_unref(&c->ext_ref)) return; - - child_call *cc = c->child; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GPR_TIMER_BEGIN("grpc_call_unref", 0); - GRPC_API_TRACE("grpc_call_unref(c=%p)", 1, (c)); - - if (cc) { - parent_call *pc = get_parent_call(cc->parent); - gpr_mu_lock(&pc->child_list_mu); - if (c == pc->first_child) { - pc->first_child = cc->sibling_next; - if (c == pc->first_child) { - pc->first_child = NULL; - } - } - cc->sibling_prev->child->sibling_next = cc->sibling_next; - cc->sibling_next->child->sibling_prev = cc->sibling_prev; - gpr_mu_unlock(&pc->child_list_mu); - GRPC_CALL_INTERNAL_UNREF(&exec_ctx, cc->parent, "child"); - } - - GPR_ASSERT(!c->destroy_called); - c->destroy_called = 1; - bool cancel = gpr_atm_acq_load(&c->any_ops_sent_atm) != 0 && - gpr_atm_acq_load(&c->received_final_op_atm) == 0; - if (cancel) { - cancel_with_error(&exec_ctx, c, STATUS_FROM_API_OVERRIDE, - GRPC_ERROR_CANCELLED); - } else { - // Unset the call combiner cancellation closure. This has the - // effect of scheduling the previously set cancellation closure, if - // any, so that it can release any internal references it may be - // holding to the call stack. - grpc_call_combiner_set_notify_on_cancel(&exec_ctx, &c->call_combiner, NULL); - } - GRPC_CALL_INTERNAL_UNREF(&exec_ctx, c, "destroy"); - grpc_exec_ctx_finish(&exec_ctx); - GPR_TIMER_END("grpc_call_unref", 0); -} - -grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) { - GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, reserved)); - GPR_ASSERT(!reserved); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - cancel_with_error(&exec_ctx, call, STATUS_FROM_API_OVERRIDE, - GRPC_ERROR_CANCELLED); - grpc_exec_ctx_finish(&exec_ctx); - return GRPC_CALL_OK; -} - -// This is called via the call combiner to start sending a batch down -// the filter stack. -static void execute_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *ignored) { - grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; - grpc_call *call = (grpc_call *)batch->handler_private.extra_arg; - GPR_TIMER_BEGIN("execute_batch", 0); - grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0); - GRPC_CALL_LOG_OP(GPR_INFO, elem, batch); - elem->filter->start_transport_stream_op_batch(exec_ctx, elem, batch); - GPR_TIMER_END("execute_batch", 0); -} - -// start_batch_closure points to a caller-allocated closure to be used -// for entering the call combiner. -static void execute_batch(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_transport_stream_op_batch *batch, - grpc_closure *start_batch_closure) { - batch->handler_private.extra_arg = call; - GRPC_CLOSURE_INIT(start_batch_closure, execute_batch_in_call_combiner, batch, - grpc_schedule_on_exec_ctx); - GRPC_CALL_COMBINER_START(exec_ctx, &call->call_combiner, start_batch_closure, - GRPC_ERROR_NONE, "executing batch"); -} - -char *grpc_call_get_peer(grpc_call *call) { - char *peer_string = (char *)gpr_atm_acq_load(&call->peer_string); - if (peer_string != NULL) return gpr_strdup(peer_string); - peer_string = grpc_channel_get_target(call->channel); - if (peer_string != NULL) return peer_string; - return gpr_strdup("unknown"); -} - -grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { - return CALL_FROM_TOP_ELEM(elem); -} - -/******************************************************************************* - * CANCELLATION - */ - -grpc_call_error grpc_call_cancel_with_status(grpc_call *c, - grpc_status_code status, - const char *description, - void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE( - "grpc_call_cancel_with_status(" - "c=%p, status=%d, description=%s, reserved=%p)", - 4, (c, (int)status, description, reserved)); - GPR_ASSERT(reserved == NULL); - cancel_with_status(&exec_ctx, c, STATUS_FROM_API_OVERRIDE, status, - description); - grpc_exec_ctx_finish(&exec_ctx); - return GRPC_CALL_OK; -} - -typedef struct { - grpc_call *call; - grpc_closure start_batch; - grpc_closure finish_batch; -} cancel_state; - -// The on_complete callback used when sending a cancel_stream batch down -// the filter stack. Yields the call combiner when the batch is done. -static void done_termination(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - cancel_state *state = (cancel_state *)arg; - GRPC_CALL_COMBINER_STOP(exec_ctx, &state->call->call_combiner, - "on_complete for cancel_stream op"); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, state->call, "termination"); - gpr_free(state); -} - -static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c, - status_source source, grpc_error *error) { - GRPC_CALL_INTERNAL_REF(c, "termination"); - // Inform the call combiner of the cancellation, so that it can cancel - // any in-flight asynchronous actions that may be holding the call - // combiner. This ensures that the cancel_stream batch can be sent - // down the filter stack in a timely manner. - grpc_call_combiner_cancel(exec_ctx, &c->call_combiner, GRPC_ERROR_REF(error)); - set_status_from_error(exec_ctx, c, source, GRPC_ERROR_REF(error)); - cancel_state *state = (cancel_state *)gpr_malloc(sizeof(*state)); - state->call = c; - GRPC_CLOSURE_INIT(&state->finish_batch, done_termination, state, - grpc_schedule_on_exec_ctx); - grpc_transport_stream_op_batch *op = - grpc_make_transport_stream_op(&state->finish_batch); - op->cancel_stream = true; - op->payload->cancel_stream.cancel_error = error; - execute_batch(exec_ctx, c, op, &state->start_batch); -} - -static grpc_error *error_from_status(grpc_status_code status, - const char *description) { - // copying 'description' is needed to ensure the grpc_call_cancel_with_status - // guarantee that can be short-lived. - return grpc_error_set_int( - grpc_error_set_str(GRPC_ERROR_CREATE_FROM_COPIED_STRING(description), - GRPC_ERROR_STR_GRPC_MESSAGE, - grpc_slice_from_copied_string(description)), - GRPC_ERROR_INT_GRPC_STATUS, status); -} - -static void cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, - status_source source, grpc_status_code status, - const char *description) { - cancel_with_error(exec_ctx, c, source, - error_from_status(status, description)); -} - -/******************************************************************************* - * FINAL STATUS CODE MANIPULATION - */ - -static bool get_final_status_from( - grpc_call *call, grpc_error *error, bool allow_ok_status, - void (*set_value)(grpc_status_code code, void *user_data), - void *set_value_user_data, grpc_slice *details) { - grpc_status_code code; - grpc_slice slice = grpc_empty_slice(); - grpc_error_get_status(error, call->send_deadline, &code, &slice, NULL); - if (code == GRPC_STATUS_OK && !allow_ok_status) { - return false; - } - - set_value(code, set_value_user_data); - if (details != NULL) { - *details = grpc_slice_ref_internal(slice); - } - return true; -} - -static void get_final_status(grpc_call *call, - void (*set_value)(grpc_status_code code, - void *user_data), - void *set_value_user_data, grpc_slice *details) { - int i; - received_status status[STATUS_SOURCE_COUNT]; - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - status[i] = unpack_received_status(gpr_atm_acq_load(&call->status[i])); - } - if (GRPC_TRACER_ON(grpc_call_error_trace)) { - gpr_log(GPR_DEBUG, "get_final_status %s", call->is_client ? "CLI" : "SVR"); - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - if (status[i].is_set) { - gpr_log(GPR_DEBUG, " %d: %s", i, grpc_error_string(status[i].error)); - } - } - } - /* first search through ignoring "OK" statuses: if something went wrong, - * ensure we report it */ - for (int allow_ok_status = 0; allow_ok_status < 2; allow_ok_status++) { - /* search for the best status we can present: ideally the error we use has a - clearly defined grpc-status, and we'll prefer that. */ - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - if (status[i].is_set && - grpc_error_has_clear_grpc_status(status[i].error)) { - if (get_final_status_from(call, status[i].error, allow_ok_status != 0, - set_value, set_value_user_data, details)) { - return; - } - } - } - /* If no clearly defined status exists, search for 'anything' */ - for (i = 0; i < STATUS_SOURCE_COUNT; i++) { - if (status[i].is_set) { - if (get_final_status_from(call, status[i].error, allow_ok_status != 0, - set_value, set_value_user_data, details)) { - return; - } - } - } - } - /* If nothing exists, set some default */ - if (call->is_client) { - set_value(GRPC_STATUS_UNKNOWN, set_value_user_data); - } else { - set_value(GRPC_STATUS_OK, set_value_user_data); - } -} - -static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call, - status_source source, grpc_error *error) { - if (!gpr_atm_rel_cas(&call->status[source], - pack_received_status((received_status){ - .is_set = false, .error = GRPC_ERROR_NONE}), - pack_received_status((received_status){ - .is_set = true, .error = error}))) { - GRPC_ERROR_UNREF(error); - } -} - -/******************************************************************************* - * COMPRESSION - */ - -static void set_incoming_compression_algorithm( - grpc_call *call, grpc_compression_algorithm algo) { - GPR_ASSERT(algo < GRPC_COMPRESS_ALGORITHMS_COUNT); - call->incoming_compression_algorithm = algo; -} - -static void set_incoming_stream_compression_algorithm( - grpc_call *call, grpc_stream_compression_algorithm algo) { - GPR_ASSERT(algo < GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT); - call->incoming_stream_compression_algorithm = algo; -} - -grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( - grpc_call *call) { - grpc_compression_algorithm algorithm; - algorithm = call->incoming_compression_algorithm; - return algorithm; -} - -static grpc_compression_algorithm compression_algorithm_for_level_locked( - grpc_call *call, grpc_compression_level level) { - return grpc_compression_algorithm_for_level(level, - call->encodings_accepted_by_peer); -} - -static grpc_stream_compression_algorithm -stream_compression_algorithm_for_level_locked( - grpc_call *call, grpc_stream_compression_level level) { - return grpc_stream_compression_algorithm_for_level( - level, call->stream_encodings_accepted_by_peer); -} - -uint32_t grpc_call_test_only_get_message_flags(grpc_call *call) { - uint32_t flags; - flags = call->test_only_last_message_flags; - return flags; -} - -static void destroy_encodings_accepted_by_peer(void *p) { return; } - -static void set_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx, - grpc_call *call, grpc_mdelem mdel) { - size_t i; - grpc_compression_algorithm algorithm; - grpc_slice_buffer accept_encoding_parts; - grpc_slice accept_encoding_slice; - void *accepted_user_data; - - accepted_user_data = - grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); - if (accepted_user_data != NULL) { - call->encodings_accepted_by_peer = - (uint32_t)(((uintptr_t)accepted_user_data) - 1); - return; - } - - accept_encoding_slice = GRPC_MDVALUE(mdel); - grpc_slice_buffer_init(&accept_encoding_parts); - grpc_slice_split(accept_encoding_slice, ",", &accept_encoding_parts); - - /* No need to zero call->encodings_accepted_by_peer: grpc_call_create already - * zeroes the whole grpc_call */ - /* Always support no compression */ - GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE); - for (i = 0; i < accept_encoding_parts.count; i++) { - grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i]; - if (grpc_compression_algorithm_parse(accept_encoding_entry_slice, - &algorithm)) { - GPR_BITSET(&call->encodings_accepted_by_peer, algorithm); - } else { - char *accept_encoding_entry_str = - grpc_slice_to_c_string(accept_encoding_entry_slice); - gpr_log(GPR_ERROR, - "Invalid entry in accept encoding metadata: '%s'. Ignoring.", - accept_encoding_entry_str); - gpr_free(accept_encoding_entry_str); - } - } - - grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts); - - grpc_mdelem_set_user_data( - mdel, destroy_encodings_accepted_by_peer, - (void *)(((uintptr_t)call->encodings_accepted_by_peer) + 1)); -} - -static void set_stream_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx, - grpc_call *call, - grpc_mdelem mdel) { - size_t i; - grpc_stream_compression_algorithm algorithm; - grpc_slice_buffer accept_encoding_parts; - grpc_slice accept_encoding_slice; - void *accepted_user_data; - - accepted_user_data = - grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); - if (accepted_user_data != NULL) { - call->stream_encodings_accepted_by_peer = - (uint32_t)(((uintptr_t)accepted_user_data) - 1); - return; - } - - accept_encoding_slice = GRPC_MDVALUE(mdel); - grpc_slice_buffer_init(&accept_encoding_parts); - grpc_slice_split(accept_encoding_slice, ",", &accept_encoding_parts); - - /* Always support no compression */ - GPR_BITSET(&call->stream_encodings_accepted_by_peer, - GRPC_STREAM_COMPRESS_NONE); - for (i = 0; i < accept_encoding_parts.count; i++) { - grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i]; - if (grpc_stream_compression_algorithm_parse(accept_encoding_entry_slice, - &algorithm)) { - GPR_BITSET(&call->stream_encodings_accepted_by_peer, algorithm); - } else { - char *accept_encoding_entry_str = - grpc_slice_to_c_string(accept_encoding_entry_slice); - gpr_log(GPR_ERROR, - "Invalid entry in accept encoding metadata: '%s'. Ignoring.", - accept_encoding_entry_str); - gpr_free(accept_encoding_entry_str); - } - } - - grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts); - - grpc_mdelem_set_user_data( - mdel, destroy_encodings_accepted_by_peer, - (void *)(((uintptr_t)call->stream_encodings_accepted_by_peer) + 1)); -} - -uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) { - uint32_t encodings_accepted_by_peer; - encodings_accepted_by_peer = call->encodings_accepted_by_peer; - return encodings_accepted_by_peer; -} - -uint32_t grpc_call_test_only_get_stream_encodings_accepted_by_peer( - grpc_call *call) { - uint32_t stream_encodings_accepted_by_peer; - stream_encodings_accepted_by_peer = call->stream_encodings_accepted_by_peer; - return stream_encodings_accepted_by_peer; -} - -grpc_stream_compression_algorithm -grpc_call_test_only_get_incoming_stream_encodings(grpc_call *call) { - return call->incoming_stream_compression_algorithm; -} - -static grpc_linked_mdelem *linked_from_md(const grpc_metadata *md) { - return (grpc_linked_mdelem *)&md->internal_data; -} - -static grpc_metadata *get_md_elem(grpc_metadata *metadata, - grpc_metadata *additional_metadata, int i, - int count) { - grpc_metadata *res = - i < count ? &metadata[i] : &additional_metadata[i - count]; - GPR_ASSERT(res); - return res; -} - -static int prepare_application_metadata( - grpc_exec_ctx *exec_ctx, grpc_call *call, int count, - grpc_metadata *metadata, int is_trailing, int prepend_extra_metadata, - grpc_metadata *additional_metadata, int additional_metadata_count) { - int total_count = count + additional_metadata_count; - int i; - grpc_metadata_batch *batch = - &call->metadata_batch[0 /* is_receiving */][is_trailing]; - for (i = 0; i < total_count; i++) { - const grpc_metadata *md = - get_md_elem(metadata, additional_metadata, i, count); - grpc_linked_mdelem *l = linked_from_md(md); - GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data)); - if (!GRPC_LOG_IF_ERROR("validate_metadata", - grpc_validate_header_key_is_legal(md->key))) { - break; - } else if (!grpc_is_binary_header(md->key) && - !GRPC_LOG_IF_ERROR( - "validate_metadata", - grpc_validate_header_nonbin_value_is_legal(md->value))) { - break; - } - l->md = grpc_mdelem_from_grpc_metadata(exec_ctx, (grpc_metadata *)md); - } - if (i != total_count) { - for (int j = 0; j < i; j++) { - const grpc_metadata *md = - get_md_elem(metadata, additional_metadata, j, count); - grpc_linked_mdelem *l = linked_from_md(md); - GRPC_MDELEM_UNREF(exec_ctx, l->md); - } - return 0; - } - if (prepend_extra_metadata) { - if (call->send_extra_metadata_count == 0) { - prepend_extra_metadata = 0; - } else { - for (i = 0; i < call->send_extra_metadata_count; i++) { - GRPC_LOG_IF_ERROR("prepare_application_metadata", - grpc_metadata_batch_link_tail( - exec_ctx, batch, &call->send_extra_metadata[i])); - } - } - } - for (i = 0; i < total_count; i++) { - grpc_metadata *md = get_md_elem(metadata, additional_metadata, i, count); - grpc_linked_mdelem *l = linked_from_md(md); - grpc_error *error = grpc_metadata_batch_link_tail(exec_ctx, batch, l); - if (error != GRPC_ERROR_NONE) { - GRPC_MDELEM_UNREF(exec_ctx, l->md); - } - GRPC_LOG_IF_ERROR("prepare_application_metadata", error); - } - call->send_extra_metadata_count = 0; - - return 1; -} - -/* we offset status by a small amount when storing it into transport metadata - as metadata cannot store a 0 value (which is used as OK for grpc_status_codes - */ -#define STATUS_OFFSET 1 -static void destroy_status(void *ignored) {} - -static uint32_t decode_status(grpc_mdelem md) { - uint32_t status; - void *user_data; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) return 0; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) return 1; - if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) return 2; - user_data = grpc_mdelem_get_user_data(md, destroy_status); - if (user_data != NULL) { - status = ((uint32_t)(intptr_t)user_data) - STATUS_OFFSET; - } else { - if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { - status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ - } - grpc_mdelem_set_user_data(md, destroy_status, - (void *)(intptr_t)(status + STATUS_OFFSET)); - } - return status; -} - -static grpc_compression_algorithm decode_compression(grpc_mdelem md) { - grpc_compression_algorithm algorithm = - grpc_compression_algorithm_from_slice(GRPC_MDVALUE(md)); - if (algorithm == GRPC_COMPRESS_ALGORITHMS_COUNT) { - char *md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log(GPR_ERROR, - "Invalid incoming compression algorithm: '%s'. Interpreting " - "incoming data as uncompressed.", - md_c_str); - gpr_free(md_c_str); - return GRPC_COMPRESS_NONE; - } - return algorithm; -} - -static grpc_stream_compression_algorithm decode_stream_compression( - grpc_mdelem md) { - grpc_stream_compression_algorithm algorithm = - grpc_stream_compression_algorithm_from_slice(GRPC_MDVALUE(md)); - if (algorithm == GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) { - char *md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); - gpr_log(GPR_ERROR, - "Invalid incoming stream compression algorithm: '%s'. Interpreting " - "incoming data as uncompressed.", - md_c_str); - gpr_free(md_c_str); - return GRPC_STREAM_COMPRESS_NONE; - } - return algorithm; -} - -static void publish_app_metadata(grpc_call *call, grpc_metadata_batch *b, - int is_trailing) { - if (b->list.count == 0) return; - GPR_TIMER_BEGIN("publish_app_metadata", 0); - grpc_metadata_array *dest; - grpc_metadata *mdusr; - dest = call->buffered_metadata[is_trailing]; - if (dest->count + b->list.count > dest->capacity) { - dest->capacity = - GPR_MAX(dest->capacity + b->list.count, dest->capacity * 3 / 2); - dest->metadata = (grpc_metadata *)gpr_realloc( - dest->metadata, sizeof(grpc_metadata) * dest->capacity); - } - for (grpc_linked_mdelem *l = b->list.head; l != NULL; l = l->next) { - mdusr = &dest->metadata[dest->count++]; - /* we pass back borrowed slices that are valid whilst the call is valid */ - mdusr->key = GRPC_MDKEY(l->md); - mdusr->value = GRPC_MDVALUE(l->md); - } - GPR_TIMER_END("publish_app_metadata", 0); -} - -static void recv_initial_filter(grpc_exec_ctx *exec_ctx, grpc_call *call, - grpc_metadata_batch *b) { - if (b->idx.named.content_encoding != NULL) { - if (b->idx.named.grpc_encoding != NULL) { - gpr_log(GPR_ERROR, - "Received both content-encoding and grpc-encoding header. " - "Ignoring grpc-encoding."); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_encoding); - } - GPR_TIMER_BEGIN("incoming_stream_compression_algorithm", 0); - set_incoming_stream_compression_algorithm( - call, decode_stream_compression(b->idx.named.content_encoding->md)); - GPR_TIMER_END("incoming_stream_compression_algorithm", 0); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.content_encoding); - } else if (b->idx.named.grpc_encoding != NULL) { - GPR_TIMER_BEGIN("incoming_compression_algorithm", 0); - set_incoming_compression_algorithm( - call, decode_compression(b->idx.named.grpc_encoding->md)); - GPR_TIMER_END("incoming_compression_algorithm", 0); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_encoding); - } - if (b->idx.named.grpc_accept_encoding != NULL) { - GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0); - set_encodings_accepted_by_peer(exec_ctx, call, - b->idx.named.grpc_accept_encoding->md); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_accept_encoding); - GPR_TIMER_END("encodings_accepted_by_peer", 0); - } - if (b->idx.named.accept_encoding != NULL) { - GPR_TIMER_BEGIN("stream_encodings_accepted_by_peer", 0); - set_stream_encodings_accepted_by_peer(exec_ctx, call, - b->idx.named.accept_encoding->md); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.accept_encoding); - GPR_TIMER_END("stream_encodings_accepted_by_peer", 0); - } - publish_app_metadata(call, b, false); -} - -static void recv_trailing_filter(grpc_exec_ctx *exec_ctx, void *args, - grpc_metadata_batch *b) { - grpc_call *call = (grpc_call *)args; - if (b->idx.named.grpc_status != NULL) { - uint32_t status_code = decode_status(b->idx.named.grpc_status->md); - grpc_error *error = - status_code == GRPC_STATUS_OK - ? GRPC_ERROR_NONE - : grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Error received from peer"), - GRPC_ERROR_INT_GRPC_STATUS, - (intptr_t)status_code); - if (b->idx.named.grpc_message != NULL) { - error = grpc_error_set_str( - error, GRPC_ERROR_STR_GRPC_MESSAGE, - grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_message->md))); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_message); - } else if (error != GRPC_ERROR_NONE) { - error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, - grpc_empty_slice()); - } - set_status_from_error(exec_ctx, call, STATUS_FROM_WIRE, error); - grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_status); - } - publish_app_metadata(call, b, true); -} - -grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) { - return CALL_STACK_FROM_CALL(call); -} - -/******************************************************************************* - * BATCH API IMPLEMENTATION - */ - -static void set_status_value_directly(grpc_status_code status, void *dest) { - *(grpc_status_code *)dest = status; -} - -static void set_cancelled_value(grpc_status_code status, void *dest) { - *(int *)dest = (status != GRPC_STATUS_OK); -} - -static bool are_write_flags_valid(uint32_t flags) { - /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ - const uint32_t allowed_write_positions = - (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK); - const uint32_t invalid_positions = ~allowed_write_positions; - return !(flags & invalid_positions); -} - -static bool are_initial_metadata_flags_valid(uint32_t flags, bool is_client) { - /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ - uint32_t invalid_positions = ~GRPC_INITIAL_METADATA_USED_MASK; - if (!is_client) { - invalid_positions |= GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; - } - return !(flags & invalid_positions); -} - -static int batch_slot_for_op(grpc_op_type type) { - switch (type) { - case GRPC_OP_SEND_INITIAL_METADATA: - return 0; - case GRPC_OP_SEND_MESSAGE: - return 1; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - case GRPC_OP_SEND_STATUS_FROM_SERVER: - return 2; - case GRPC_OP_RECV_INITIAL_METADATA: - return 3; - case GRPC_OP_RECV_MESSAGE: - return 4; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - case GRPC_OP_RECV_STATUS_ON_CLIENT: - return 5; - } - GPR_UNREACHABLE_CODE(return 123456789); -} - -static batch_control *allocate_batch_control(grpc_call *call, - const grpc_op *ops, - size_t num_ops) { - int slot = batch_slot_for_op(ops[0].op); - batch_control **pslot = &call->active_batches[slot]; - if (*pslot == NULL) { - *pslot = - (batch_control *)gpr_arena_alloc(call->arena, sizeof(batch_control)); - } - batch_control *bctl = *pslot; - if (bctl->call != NULL) { - return NULL; - } - memset(bctl, 0, sizeof(*bctl)); - bctl->call = call; - bctl->op.payload = &call->stream_op_payload; - return bctl; -} - -static void finish_batch_completion(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_cq_completion *storage) { - batch_control *bctl = (batch_control *)user_data; - grpc_call *call = bctl->call; - bctl->call = NULL; - GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); -} - -static grpc_error *consolidate_batch_errors(batch_control *bctl) { - size_t n = (size_t)gpr_atm_acq_load(&bctl->num_errors); - if (n == 0) { - return GRPC_ERROR_NONE; - } else if (n == 1) { - /* Skip creating a composite error in the case that only one error was - logged */ - grpc_error *e = bctl->errors[0]; - bctl->errors[0] = NULL; - return e; - } else { - grpc_error *error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Call batch failed", bctl->errors, n); - for (size_t i = 0; i < n; i++) { - GRPC_ERROR_UNREF(bctl->errors[i]); - bctl->errors[i] = NULL; - } - return error; - } -} - -static void post_batch_completion(grpc_exec_ctx *exec_ctx, - batch_control *bctl) { - grpc_call *next_child_call; - grpc_call *call = bctl->call; - grpc_error *error = consolidate_batch_errors(bctl); - - if (bctl->op.send_initial_metadata) { - grpc_metadata_batch_destroy( - exec_ctx, - &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]); - } - if (bctl->op.send_message) { - call->sending_message = false; - } - if (bctl->op.send_trailing_metadata) { - grpc_metadata_batch_destroy( - exec_ctx, - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]); - } - if (bctl->op.recv_trailing_metadata) { - grpc_metadata_batch *md = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - recv_trailing_filter(exec_ctx, call, md); - - /* propagate cancellation to any interested children */ - gpr_atm_rel_store(&call->received_final_op_atm, 1); - parent_call *pc = get_parent_call(call); - if (pc != NULL) { - grpc_call *child; - gpr_mu_lock(&pc->child_list_mu); - child = pc->first_child; - if (child != NULL) { - do { - next_child_call = child->child->sibling_next; - if (child->cancellation_is_inherited) { - GRPC_CALL_INTERNAL_REF(child, "propagate_cancel"); - cancel_with_error(exec_ctx, child, STATUS_FROM_API_OVERRIDE, - GRPC_ERROR_CANCELLED); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, child, "propagate_cancel"); - } - child = next_child_call; - } while (child != pc->first_child); - } - gpr_mu_unlock(&pc->child_list_mu); - } - - if (call->is_client) { - get_final_status(call, set_status_value_directly, - call->final_op.client.status, - call->final_op.client.status_details); - } else { - get_final_status(call, set_cancelled_value, - call->final_op.server.cancelled, NULL); - } - - GRPC_ERROR_UNREF(error); - error = GRPC_ERROR_NONE; - } - - if (bctl->completion_data.notify_tag.is_closure) { - /* unrefs bctl->error */ - bctl->call = NULL; - GRPC_CLOSURE_RUN( - exec_ctx, (grpc_closure *)bctl->completion_data.notify_tag.tag, error); - GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); - } else { - /* unrefs bctl->error */ - grpc_cq_end_op( - exec_ctx, bctl->call->cq, bctl->completion_data.notify_tag.tag, error, - finish_batch_completion, bctl, &bctl->completion_data.cq_completion); - } -} - -static void finish_batch_step(grpc_exec_ctx *exec_ctx, batch_control *bctl) { - if (gpr_unref(&bctl->steps_to_complete)) { - post_batch_completion(exec_ctx, bctl); - } -} - -static void continue_receiving_slices(grpc_exec_ctx *exec_ctx, - batch_control *bctl) { - grpc_error *error; - grpc_call *call = bctl->call; - for (;;) { - size_t remaining = call->receiving_stream->length - - (*call->receiving_buffer)->data.raw.slice_buffer.length; - if (remaining == 0) { - call->receiving_message = 0; - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - finish_batch_step(exec_ctx, bctl); - return; - } - if (grpc_byte_stream_next(exec_ctx, call->receiving_stream, remaining, - &call->receiving_slice_ready)) { - error = grpc_byte_stream_pull(exec_ctx, call->receiving_stream, - &call->receiving_slice); - if (error == GRPC_ERROR_NONE) { - grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, - call->receiving_slice); - } else { - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - grpc_byte_buffer_destroy(*call->receiving_buffer); - *call->receiving_buffer = NULL; - call->receiving_message = 0; - finish_batch_step(exec_ctx, bctl); - return; - } - } else { - return; - } - } -} - -static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, - grpc_error *error) { - batch_control *bctl = (batch_control *)bctlp; - grpc_call *call = bctl->call; - grpc_byte_stream *bs = call->receiving_stream; - bool release_error = false; - - if (error == GRPC_ERROR_NONE) { - grpc_slice slice; - error = grpc_byte_stream_pull(exec_ctx, bs, &slice); - if (error == GRPC_ERROR_NONE) { - grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, - slice); - continue_receiving_slices(exec_ctx, bctl); - } else { - /* Error returned by grpc_byte_stream_pull needs to be released manually - */ - release_error = true; - } - } - - if (error != GRPC_ERROR_NONE) { - if (GRPC_TRACER_ON(grpc_trace_operation_failures)) { - GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error)); - } - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - grpc_byte_buffer_destroy(*call->receiving_buffer); - *call->receiving_buffer = NULL; - call->receiving_message = 0; - finish_batch_step(exec_ctx, bctl); - if (release_error) { - GRPC_ERROR_UNREF(error); - } - } -} - -static void process_data_after_md(grpc_exec_ctx *exec_ctx, - batch_control *bctl) { - grpc_call *call = bctl->call; - if (call->receiving_stream == NULL) { - *call->receiving_buffer = NULL; - call->receiving_message = 0; - finish_batch_step(exec_ctx, bctl); - } else { - call->test_only_last_message_flags = call->receiving_stream->flags; - if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) && - (call->incoming_compression_algorithm > GRPC_COMPRESS_NONE)) { - *call->receiving_buffer = grpc_raw_compressed_byte_buffer_create( - NULL, 0, call->incoming_compression_algorithm); - } else { - *call->receiving_buffer = grpc_raw_byte_buffer_create(NULL, 0); - } - GRPC_CLOSURE_INIT(&call->receiving_slice_ready, receiving_slice_ready, bctl, - grpc_schedule_on_exec_ctx); - continue_receiving_slices(exec_ctx, bctl); - } -} - -static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp, - grpc_error *error) { - batch_control *bctl = (batch_control *)bctlp; - grpc_call *call = bctl->call; - if (error != GRPC_ERROR_NONE) { - if (call->receiving_stream != NULL) { - grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); - call->receiving_stream = NULL; - } - add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), true); - cancel_with_error(exec_ctx, call, STATUS_FROM_SURFACE, - GRPC_ERROR_REF(error)); - } - /* If recv_state is RECV_NONE, we will save the batch_control - * object with rel_cas, and will not use it after the cas. Its corresponding - * acq_load is in receiving_initial_metadata_ready() */ - if (error != GRPC_ERROR_NONE || call->receiving_stream == NULL || - !gpr_atm_rel_cas(&call->recv_state, RECV_NONE, (gpr_atm)bctlp)) { - process_data_after_md(exec_ctx, bctl); - } -} - -// The recv_message_ready callback used when sending a batch containing -// a recv_message op down the filter stack. Yields the call combiner -// before processing the received message. -static void receiving_stream_ready_in_call_combiner(grpc_exec_ctx *exec_ctx, - void *bctlp, - grpc_error *error) { - batch_control *bctl = (batch_control *)bctlp; - grpc_call *call = bctl->call; - GRPC_CALL_COMBINER_STOP(exec_ctx, &call->call_combiner, "recv_message_ready"); - receiving_stream_ready(exec_ctx, bctlp, error); -} - -static void validate_filtered_metadata(grpc_exec_ctx *exec_ctx, - batch_control *bctl) { - grpc_call *call = bctl->call; - /* validate compression algorithms */ - if (call->incoming_stream_compression_algorithm != - GRPC_STREAM_COMPRESS_NONE) { - const grpc_stream_compression_algorithm algo = - call->incoming_stream_compression_algorithm; - char *error_msg = NULL; - const grpc_compression_options compression_options = - grpc_channel_compression_options(call->channel); - if (algo >= GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) { - gpr_asprintf(&error_msg, - "Invalid stream compression algorithm value '%d'.", algo); - gpr_log(GPR_ERROR, "%s", error_msg); - cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, - GRPC_STATUS_UNIMPLEMENTED, error_msg); - } else if (grpc_compression_options_is_stream_compression_algorithm_enabled( - &compression_options, algo) == 0) { - /* check if algorithm is supported by current channel config */ - const char *algo_name = NULL; - grpc_stream_compression_algorithm_name(algo, &algo_name); - gpr_asprintf(&error_msg, "Stream compression algorithm '%s' is disabled.", - algo_name); - gpr_log(GPR_ERROR, "%s", error_msg); - cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, - GRPC_STATUS_UNIMPLEMENTED, error_msg); - } - gpr_free(error_msg); - - GPR_ASSERT(call->stream_encodings_accepted_by_peer != 0); - if (!GPR_BITGET(call->stream_encodings_accepted_by_peer, - call->incoming_stream_compression_algorithm)) { - if (GRPC_TRACER_ON(grpc_compression_trace)) { - const char *algo_name = NULL; - grpc_stream_compression_algorithm_name( - call->incoming_stream_compression_algorithm, &algo_name); - gpr_log( - GPR_ERROR, - "Stream compression algorithm (content-encoding = '%s') not " - "present in the bitset of accepted encodings (accept-encodings: " - "'0x%x')", - algo_name, call->stream_encodings_accepted_by_peer); - } - } - } else if (call->incoming_compression_algorithm != GRPC_COMPRESS_NONE) { - const grpc_compression_algorithm algo = - call->incoming_compression_algorithm; - char *error_msg = NULL; - const grpc_compression_options compression_options = - grpc_channel_compression_options(call->channel); - /* check if algorithm is known */ - if (algo >= GRPC_COMPRESS_ALGORITHMS_COUNT) { - gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.", - algo); - gpr_log(GPR_ERROR, "%s", error_msg); - cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, - GRPC_STATUS_UNIMPLEMENTED, error_msg); - } else if (grpc_compression_options_is_algorithm_enabled( - &compression_options, algo) == 0) { - /* check if algorithm is supported by current channel config */ - const char *algo_name = NULL; - grpc_compression_algorithm_name(algo, &algo_name); - gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.", - algo_name); - gpr_log(GPR_ERROR, "%s", error_msg); - cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, - GRPC_STATUS_UNIMPLEMENTED, error_msg); - } else { - call->incoming_compression_algorithm = algo; - } - gpr_free(error_msg); - - GPR_ASSERT(call->encodings_accepted_by_peer != 0); - if (!GPR_BITGET(call->encodings_accepted_by_peer, - call->incoming_compression_algorithm)) { - if (GRPC_TRACER_ON(grpc_compression_trace)) { - const char *algo_name = NULL; - grpc_compression_algorithm_name(call->incoming_compression_algorithm, - &algo_name); - gpr_log(GPR_ERROR, - "Compression algorithm (grpc-encoding = '%s') not present in " - "the bitset of accepted encodings (grpc-accept-encodings: " - "'0x%x')", - algo_name, call->encodings_accepted_by_peer); - } - } - } -} - -static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl, - grpc_error *error, bool has_cancelled) { - if (error == GRPC_ERROR_NONE) return; - int idx = (int)gpr_atm_full_fetch_add(&bctl->num_errors, 1); - if (idx == 0 && !has_cancelled) { - cancel_with_error(exec_ctx, bctl->call, STATUS_FROM_CORE, - GRPC_ERROR_REF(error)); - } - bctl->errors[idx] = error; -} - -static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx, - void *bctlp, grpc_error *error) { - batch_control *bctl = (batch_control *)bctlp; - grpc_call *call = bctl->call; - - GRPC_CALL_COMBINER_STOP(exec_ctx, &call->call_combiner, - "recv_initial_metadata_ready"); - - add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), false); - if (error == GRPC_ERROR_NONE) { - grpc_metadata_batch *md = - &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; - recv_initial_filter(exec_ctx, call, md); - - /* TODO(ctiller): this could be moved into recv_initial_filter now */ - GPR_TIMER_BEGIN("validate_filtered_metadata", 0); - validate_filtered_metadata(exec_ctx, bctl); - GPR_TIMER_END("validate_filtered_metadata", 0); - - if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) != - 0 && - !call->is_client) { - call->send_deadline = - gpr_convert_clock_type(md->deadline, GPR_CLOCK_MONOTONIC); - } - } - - grpc_closure *saved_rsr_closure = NULL; - while (true) { - gpr_atm rsr_bctlp = gpr_atm_acq_load(&call->recv_state); - /* Should only receive initial metadata once */ - GPR_ASSERT(rsr_bctlp != 1); - if (rsr_bctlp == 0) { - /* We haven't seen initial metadata and messages before, thus initial - * metadata is received first. - * no_barrier_cas is used, as this function won't access the batch_control - * object saved by receiving_stream_ready() if the initial metadata is - * received first. */ - if (gpr_atm_no_barrier_cas(&call->recv_state, RECV_NONE, - RECV_INITIAL_METADATA_FIRST)) { - break; - } - } else { - /* Already received messages */ - saved_rsr_closure = GRPC_CLOSURE_CREATE(receiving_stream_ready, - (batch_control *)rsr_bctlp, - grpc_schedule_on_exec_ctx); - /* No need to modify recv_state */ - break; - } - } - if (saved_rsr_closure != NULL) { - GRPC_CLOSURE_RUN(exec_ctx, saved_rsr_closure, GRPC_ERROR_REF(error)); - } - - finish_batch_step(exec_ctx, bctl); -} - -static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, - grpc_error *error) { - batch_control *bctl = (batch_control *)bctlp; - grpc_call *call = bctl->call; - GRPC_CALL_COMBINER_STOP(exec_ctx, &call->call_combiner, "on_complete"); - add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), false); - finish_batch_step(exec_ctx, bctl); -} - -static void free_no_op_completion(grpc_exec_ctx *exec_ctx, void *p, - grpc_cq_completion *completion) { - gpr_free(completion); -} - -static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, - grpc_call *call, const grpc_op *ops, - size_t nops, void *notify_tag, - int is_notify_tag_closure) { - size_t i; - const grpc_op *op; - batch_control *bctl; - int num_completion_callbacks_needed = 1; - grpc_call_error error = GRPC_CALL_OK; - grpc_transport_stream_op_batch *stream_op; - grpc_transport_stream_op_batch_payload *stream_op_payload; - - GPR_TIMER_BEGIN("grpc_call_start_batch", 0); - GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag); - - if (nops == 0) { - if (!is_notify_tag_closure) { - GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); - grpc_cq_end_op( - exec_ctx, call->cq, notify_tag, GRPC_ERROR_NONE, - free_no_op_completion, NULL, - (grpc_cq_completion *)gpr_malloc(sizeof(grpc_cq_completion))); - } else { - GRPC_CLOSURE_SCHED(exec_ctx, (grpc_closure *)notify_tag, GRPC_ERROR_NONE); - } - error = GRPC_CALL_OK; - goto done; - } - - bctl = allocate_batch_control(call, ops, nops); - if (bctl == NULL) { - return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - } - bctl->completion_data.notify_tag.tag = notify_tag; - bctl->completion_data.notify_tag.is_closure = - (uint8_t)(is_notify_tag_closure != 0); - - stream_op = &bctl->op; - stream_op_payload = &call->stream_op_payload; - - /* rewrite batch ops into a transport op */ - for (i = 0; i < nops; i++) { - op = &ops[i]; - if (op->reserved != NULL) { - error = GRPC_CALL_ERROR; - goto done_with_error; - } - switch (op->op) { - case GRPC_OP_SEND_INITIAL_METADATA: { - /* Flag validation: currently allow no flags */ - if (!are_initial_metadata_flags_valid(op->flags, call->is_client)) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->sent_initial_metadata) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - /* process compression level */ - memset(&call->compression_md, 0, sizeof(call->compression_md)); - size_t additional_metadata_count = 0; - grpc_compression_level effective_compression_level = - GRPC_COMPRESS_LEVEL_NONE; - grpc_stream_compression_level effective_stream_compression_level = - GRPC_STREAM_COMPRESS_LEVEL_NONE; - bool level_set = false; - bool stream_compression = false; - if (op->data.send_initial_metadata.maybe_stream_compression_level - .is_set) { - effective_stream_compression_level = - op->data.send_initial_metadata.maybe_stream_compression_level - .level; - level_set = true; - stream_compression = true; - } else if (op->data.send_initial_metadata.maybe_compression_level - .is_set) { - effective_compression_level = - op->data.send_initial_metadata.maybe_compression_level.level; - level_set = true; - } else { - const grpc_compression_options copts = - grpc_channel_compression_options(call->channel); - if (copts.default_stream_compression_level.is_set) { - level_set = true; - effective_stream_compression_level = - copts.default_stream_compression_level.level; - stream_compression = true; - } else if (copts.default_level.is_set) { - level_set = true; - effective_compression_level = copts.default_level.level; - } - } - if (level_set && !call->is_client) { - if (stream_compression) { - const grpc_stream_compression_algorithm calgo = - stream_compression_algorithm_for_level_locked( - call, effective_stream_compression_level); - call->compression_md.key = - GRPC_MDSTR_GRPC_INTERNAL_STREAM_ENCODING_REQUEST; - call->compression_md.value = - grpc_stream_compression_algorithm_slice(calgo); - } else { - const grpc_compression_algorithm calgo = - compression_algorithm_for_level_locked( - call, effective_compression_level); - /* the following will be picked up by the compress filter and used - * as the call's compression algorithm. */ - call->compression_md.key = - GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST; - call->compression_md.value = - grpc_compression_algorithm_slice(calgo); - additional_metadata_count++; - } - } - - if (op->data.send_initial_metadata.count + additional_metadata_count > - INT_MAX) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - stream_op->send_initial_metadata = true; - call->sent_initial_metadata = true; - if (!prepare_application_metadata( - exec_ctx, call, (int)op->data.send_initial_metadata.count, - op->data.send_initial_metadata.metadata, 0, call->is_client, - &call->compression_md, (int)additional_metadata_count)) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - /* TODO(ctiller): just make these the same variable? */ - if (call->is_client) { - call->metadata_batch[0][0].deadline = call->send_deadline; - } - stream_op_payload->send_initial_metadata.send_initial_metadata = - &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]; - stream_op_payload->send_initial_metadata.send_initial_metadata_flags = - op->flags; - if (call->is_client) { - stream_op_payload->send_initial_metadata.peer_string = - &call->peer_string; - } - break; - } - case GRPC_OP_SEND_MESSAGE: { - if (!are_write_flags_valid(op->flags)) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (op->data.send_message.send_message == NULL) { - error = GRPC_CALL_ERROR_INVALID_MESSAGE; - goto done_with_error; - } - if (call->sending_message) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - stream_op->send_message = true; - call->sending_message = true; - grpc_slice_buffer_stream_init( - &call->sending_stream, - &op->data.send_message.send_message->data.raw.slice_buffer, - op->flags); - /* If the outgoing buffer is already compressed, mark it as so in the - flags. These will be picked up by the compression filter and further - (wasteful) attempts at compression skipped. */ - if (op->data.send_message.send_message->data.raw.compression > - GRPC_COMPRESS_NONE) { - call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; - } - stream_op_payload->send_message.send_message = - &call->sending_stream.base; - break; - } - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: { - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (!call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_SERVER; - goto done_with_error; - } - if (call->sent_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - stream_op->send_trailing_metadata = true; - call->sent_final_op = true; - stream_op_payload->send_trailing_metadata.send_trailing_metadata = - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; - break; - } - case GRPC_OP_SEND_STATUS_FROM_SERVER: { - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_CLIENT; - goto done_with_error; - } - if (call->sent_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - if (op->data.send_status_from_server.trailing_metadata_count > - INT_MAX) { - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - stream_op->send_trailing_metadata = true; - call->sent_final_op = true; - GPR_ASSERT(call->send_extra_metadata_count == 0); - call->send_extra_metadata_count = 1; - call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem( - exec_ctx, call->channel, op->data.send_status_from_server.status); - { - grpc_error *override_error = GRPC_ERROR_NONE; - if (op->data.send_status_from_server.status != GRPC_STATUS_OK) { - override_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Error from server send status"); - } - if (op->data.send_status_from_server.status_details != NULL) { - call->send_extra_metadata[1].md = grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_GRPC_MESSAGE, - grpc_slice_ref_internal( - *op->data.send_status_from_server.status_details)); - call->send_extra_metadata_count++; - char *msg = grpc_slice_to_c_string( - GRPC_MDVALUE(call->send_extra_metadata[1].md)); - override_error = - grpc_error_set_str(override_error, GRPC_ERROR_STR_GRPC_MESSAGE, - grpc_slice_from_copied_string(msg)); - gpr_free(msg); - } - set_status_from_error(exec_ctx, call, STATUS_FROM_API_OVERRIDE, - override_error); - } - if (!prepare_application_metadata( - exec_ctx, call, - (int)op->data.send_status_from_server.trailing_metadata_count, - op->data.send_status_from_server.trailing_metadata, 1, 1, NULL, - 0)) { - for (int n = 0; n < call->send_extra_metadata_count; n++) { - GRPC_MDELEM_UNREF(exec_ctx, call->send_extra_metadata[n].md); - } - call->send_extra_metadata_count = 0; - error = GRPC_CALL_ERROR_INVALID_METADATA; - goto done_with_error; - } - stream_op_payload->send_trailing_metadata.send_trailing_metadata = - &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; - break; - } - case GRPC_OP_RECV_INITIAL_METADATA: { - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->received_initial_metadata) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->received_initial_metadata = true; - call->buffered_metadata[0] = - op->data.recv_initial_metadata.recv_initial_metadata; - GRPC_CLOSURE_INIT(&call->receiving_initial_metadata_ready, - receiving_initial_metadata_ready, bctl, - grpc_schedule_on_exec_ctx); - stream_op->recv_initial_metadata = true; - stream_op_payload->recv_initial_metadata.recv_initial_metadata = - &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; - stream_op_payload->recv_initial_metadata.recv_initial_metadata_ready = - &call->receiving_initial_metadata_ready; - if (!call->is_client) { - stream_op_payload->recv_initial_metadata.peer_string = - &call->peer_string; - } - num_completion_callbacks_needed++; - break; - } - case GRPC_OP_RECV_MESSAGE: { - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->receiving_message) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->receiving_message = true; - stream_op->recv_message = true; - call->receiving_buffer = op->data.recv_message.recv_message; - stream_op_payload->recv_message.recv_message = &call->receiving_stream; - GRPC_CLOSURE_INIT(&call->receiving_stream_ready, - receiving_stream_ready_in_call_combiner, bctl, - grpc_schedule_on_exec_ctx); - stream_op_payload->recv_message.recv_message_ready = - &call->receiving_stream_ready; - num_completion_callbacks_needed++; - break; - } - case GRPC_OP_RECV_STATUS_ON_CLIENT: { - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (!call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_SERVER; - goto done_with_error; - } - if (call->requested_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->requested_final_op = true; - call->buffered_metadata[1] = - op->data.recv_status_on_client.trailing_metadata; - call->final_op.client.status = op->data.recv_status_on_client.status; - call->final_op.client.status_details = - op->data.recv_status_on_client.status_details; - stream_op->recv_trailing_metadata = true; - stream_op->collect_stats = true; - stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - stream_op_payload->collect_stats.collect_stats = - &call->final_info.stats.transport_stream_stats; - break; - } - case GRPC_OP_RECV_CLOSE_ON_SERVER: { - /* Flag validation: currently allow no flags */ - if (op->flags != 0) { - error = GRPC_CALL_ERROR_INVALID_FLAGS; - goto done_with_error; - } - if (call->is_client) { - error = GRPC_CALL_ERROR_NOT_ON_CLIENT; - goto done_with_error; - } - if (call->requested_final_op) { - error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; - goto done_with_error; - } - call->requested_final_op = true; - call->final_op.server.cancelled = - op->data.recv_close_on_server.cancelled; - stream_op->recv_trailing_metadata = true; - stream_op->collect_stats = true; - stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = - &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; - stream_op_payload->collect_stats.collect_stats = - &call->final_info.stats.transport_stream_stats; - break; - } - } - } - - GRPC_CALL_INTERNAL_REF(call, "completion"); - if (!is_notify_tag_closure) { - GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); - } - gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); - - GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl, - grpc_schedule_on_exec_ctx); - stream_op->on_complete = &bctl->finish_batch; - gpr_atm_rel_store(&call->any_ops_sent_atm, 1); - - execute_batch(exec_ctx, call, stream_op, &bctl->start_batch); - -done: - GPR_TIMER_END("grpc_call_start_batch", 0); - return error; - -done_with_error: - /* reverse any mutations that occured */ - if (stream_op->send_initial_metadata) { - call->sent_initial_metadata = false; - grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][0]); - } - if (stream_op->send_message) { - call->sending_message = false; - grpc_byte_stream_destroy(exec_ctx, &call->sending_stream.base); - } - if (stream_op->send_trailing_metadata) { - call->sent_final_op = false; - grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][1]); - } - if (stream_op->recv_initial_metadata) { - call->received_initial_metadata = false; - } - if (stream_op->recv_message) { - call->receiving_message = false; - } - if (stream_op->recv_trailing_metadata) { - call->requested_final_op = false; - } - goto done; -} - -grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, - size_t nops, void *tag, void *reserved) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call_error err; - - GRPC_API_TRACE( - "grpc_call_start_batch(call=%p, ops=%p, nops=%lu, tag=%p, " - "reserved=%p)", - 5, (call, ops, (unsigned long)nops, tag, reserved)); - - if (reserved != NULL) { - err = GRPC_CALL_ERROR; - } else { - err = call_start_batch(&exec_ctx, call, ops, nops, tag, 0); - } - - grpc_exec_ctx_finish(&exec_ctx); - return err; -} - -grpc_call_error grpc_call_start_batch_and_execute(grpc_exec_ctx *exec_ctx, - grpc_call *call, - const grpc_op *ops, - size_t nops, - grpc_closure *closure) { - return call_start_batch(exec_ctx, call, ops, nops, closure, 1); -} - -void grpc_call_context_set(grpc_call *call, grpc_context_index elem, - void *value, void (*destroy)(void *value)) { - if (call->context[elem].destroy) { - call->context[elem].destroy(call->context[elem].value); - } - call->context[elem].value = value; - call->context[elem].destroy = destroy; -} - -void *grpc_call_context_get(grpc_call *call, grpc_context_index elem) { - return call->context[elem].value; -} - -uint8_t grpc_call_is_client(grpc_call *call) { return call->is_client; } - -grpc_compression_algorithm grpc_call_compression_for_level( - grpc_call *call, grpc_compression_level level) { - grpc_compression_algorithm algo = - compression_algorithm_for_level_locked(call, level); - return algo; -} - -const char *grpc_call_error_to_string(grpc_call_error error) { - switch (error) { - case GRPC_CALL_ERROR: - return "GRPC_CALL_ERROR"; - case GRPC_CALL_ERROR_ALREADY_ACCEPTED: - return "GRPC_CALL_ERROR_ALREADY_ACCEPTED"; - case GRPC_CALL_ERROR_ALREADY_FINISHED: - return "GRPC_CALL_ERROR_ALREADY_FINISHED"; - case GRPC_CALL_ERROR_ALREADY_INVOKED: - return "GRPC_CALL_ERROR_ALREADY_INVOKED"; - case GRPC_CALL_ERROR_BATCH_TOO_BIG: - return "GRPC_CALL_ERROR_BATCH_TOO_BIG"; - case GRPC_CALL_ERROR_INVALID_FLAGS: - return "GRPC_CALL_ERROR_INVALID_FLAGS"; - case GRPC_CALL_ERROR_INVALID_MESSAGE: - return "GRPC_CALL_ERROR_INVALID_MESSAGE"; - case GRPC_CALL_ERROR_INVALID_METADATA: - return "GRPC_CALL_ERROR_INVALID_METADATA"; - case GRPC_CALL_ERROR_NOT_INVOKED: - return "GRPC_CALL_ERROR_NOT_INVOKED"; - case GRPC_CALL_ERROR_NOT_ON_CLIENT: - return "GRPC_CALL_ERROR_NOT_ON_CLIENT"; - case GRPC_CALL_ERROR_NOT_ON_SERVER: - return "GRPC_CALL_ERROR_NOT_ON_SERVER"; - case GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE: - return "GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE"; - case GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH: - return "GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH"; - case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS: - return "GRPC_CALL_ERROR_TOO_MANY_OPERATIONS"; - case GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN: - return "GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN"; - case GRPC_CALL_OK: - return "GRPC_CALL_OK"; - } - GPR_UNREACHABLE_CODE(return "GRPC_CALL_ERROR_UNKNOW"); -} diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc new file mode 100644 index 0000000000..74e55d5741 --- /dev/null +++ b/src/core/lib/surface/call.cc @@ -0,0 +1,2160 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/compression/algorithm_metadata.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/arena.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/call_test_only.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/validate_metadata.h" +#include "src/core/lib/transport/error_utils.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" +#include "src/core/lib/transport/transport.h" + +/** The maximum number of concurrent batches possible. + Based upon the maximum number of individually queueable ops in the batch + api: + - initial metadata send + - message send + - status/close send (depending on client/server) + - initial metadata recv + - message recv + - status/close recv (depending on client/server) */ +#define MAX_CONCURRENT_BATCHES 6 + +#define MAX_SEND_EXTRA_METADATA_COUNT 3 + +/* Status data for a request can come from several sources; this + enumerates them all, and acts as a priority sorting for which + status to return to the application - earlier entries override + later ones */ +typedef enum { + /* Status came from the application layer overriding whatever + the wire says */ + STATUS_FROM_API_OVERRIDE = 0, + /* Status came from 'the wire' - or somewhere below the surface + layer */ + STATUS_FROM_WIRE, + /* Status was created by some internal channel stack operation: must come via + add_batch_error */ + STATUS_FROM_CORE, + /* Status was created by some surface error */ + STATUS_FROM_SURFACE, + /* Status came from the server sending status */ + STATUS_FROM_SERVER_STATUS, + STATUS_SOURCE_COUNT +} status_source; + +typedef struct { + bool is_set; + grpc_error *error; +} received_status; + +static gpr_atm pack_received_status(received_status r) { + return r.is_set ? (1 | (gpr_atm)r.error) : 0; +} + +static received_status unpack_received_status(gpr_atm atm) { + return (atm & 1) == 0 + ? (received_status){.is_set = false, .error = GRPC_ERROR_NONE} + : (received_status){.is_set = true, + .error = (grpc_error *)(atm & ~(gpr_atm)1)}; +} + +#define MAX_ERRORS_PER_BATCH 4 + +typedef struct batch_control { + grpc_call *call; + /* Share memory for cq_completion and notify_tag as they are never needed + simultaneously. Each byte used in this data structure count as six bytes + per call, so any savings we can make are worthwhile, + + We use notify_tag to determine whether or not to send notification to the + completion queue. Once we've made that determination, we can reuse the + memory for cq_completion. */ + union { + grpc_cq_completion cq_completion; + struct { + /* Any given op indicates completion by either (a) calling a closure or + (b) sending a notification on the call's completion queue. If + \a is_closure is true, \a tag indicates a closure to be invoked; + otherwise, \a tag indicates the tag to be used in the notification to + be sent to the completion queue. */ + void *tag; + bool is_closure; + } notify_tag; + } completion_data; + grpc_closure start_batch; + grpc_closure finish_batch; + gpr_refcount steps_to_complete; + + grpc_error *errors[MAX_ERRORS_PER_BATCH]; + gpr_atm num_errors; + + grpc_transport_stream_op_batch op; +} batch_control; + +typedef struct { + gpr_mu child_list_mu; + grpc_call *first_child; +} parent_call; + +typedef struct { + grpc_call *parent; + /** siblings: children of the same parent form a list, and this list is + protected under + parent->mu */ + grpc_call *sibling_next; + grpc_call *sibling_prev; +} child_call; + +#define RECV_NONE ((gpr_atm)0) +#define RECV_INITIAL_METADATA_FIRST ((gpr_atm)1) + +struct grpc_call { + gpr_refcount ext_ref; + gpr_arena *arena; + grpc_call_combiner call_combiner; + grpc_completion_queue *cq; + grpc_polling_entity pollent; + grpc_channel *channel; + gpr_timespec start_time; + /* parent_call* */ gpr_atm parent_call_atm; + child_call *child; + + /* client or server call */ + bool is_client; + /** has grpc_call_unref been called */ + bool destroy_called; + /** flag indicating that cancellation is inherited */ + bool cancellation_is_inherited; + /** which ops are in-flight */ + bool sent_initial_metadata; + bool sending_message; + bool sent_final_op; + bool received_initial_metadata; + bool receiving_message; + bool requested_final_op; + gpr_atm any_ops_sent_atm; + gpr_atm received_final_op_atm; + + batch_control *active_batches[MAX_CONCURRENT_BATCHES]; + grpc_transport_stream_op_batch_payload stream_op_payload; + + /* first idx: is_receiving, second idx: is_trailing */ + grpc_metadata_batch metadata_batch[2][2]; + + /* Buffered read metadata waiting to be returned to the application. + Element 0 is initial metadata, element 1 is trailing metadata. */ + grpc_metadata_array *buffered_metadata[2]; + + grpc_metadata compression_md; + + // A char* indicating the peer name. + gpr_atm peer_string; + + /* Packed received call statuses from various sources */ + gpr_atm status[STATUS_SOURCE_COUNT]; + + /* Call data useful used for reporting. Only valid after the call has + * completed */ + grpc_call_final_info final_info; + + /* Compression algorithm for *incoming* data */ + grpc_compression_algorithm incoming_compression_algorithm; + /* Stream compression algorithm for *incoming* data */ + grpc_stream_compression_algorithm incoming_stream_compression_algorithm; + /* Supported encodings (compression algorithms), a bitset */ + uint32_t encodings_accepted_by_peer; + /* Supported stream encodings (stream compression algorithms), a bitset */ + uint32_t stream_encodings_accepted_by_peer; + + /* Contexts for various subsystems (security, tracing, ...). */ + grpc_call_context_element context[GRPC_CONTEXT_COUNT]; + + /* for the client, extra metadata is initial metadata; for the + server, it's trailing metadata */ + grpc_linked_mdelem send_extra_metadata[MAX_SEND_EXTRA_METADATA_COUNT]; + int send_extra_metadata_count; + gpr_timespec send_deadline; + + grpc_slice_buffer_stream sending_stream; + + grpc_byte_stream *receiving_stream; + grpc_byte_buffer **receiving_buffer; + grpc_slice receiving_slice; + grpc_closure receiving_slice_ready; + grpc_closure receiving_stream_ready; + grpc_closure receiving_initial_metadata_ready; + uint32_t test_only_last_message_flags; + + grpc_closure release_call; + + union { + struct { + grpc_status_code *status; + grpc_slice *status_details; + } client; + struct { + int *cancelled; + } server; + } final_op; + + /* recv_state can contain one of the following values: + RECV_NONE : : no initial metadata and messages received + RECV_INITIAL_METADATA_FIRST : received initial metadata first + a batch_control* : received messages first + + +------1------RECV_NONE------3-----+ + | | + | | + v v + RECV_INITIAL_METADATA_FIRST receiving_stream_ready_bctlp + | ^ | ^ + | | | | + +-----2-----+ +-----4-----+ + + For 1, 4: See receiving_initial_metadata_ready() function + For 2, 3: See receiving_stream_ready() function */ + gpr_atm recv_state; +}; + +grpc_tracer_flag grpc_call_error_trace = + GRPC_TRACER_INITIALIZER(false, "call_error"); +grpc_tracer_flag grpc_compression_trace = + GRPC_TRACER_INITIALIZER(false, "compression"); + +#define CALL_STACK_FROM_CALL(call) ((grpc_call_stack *)((call) + 1)) +#define CALL_FROM_CALL_STACK(call_stack) (((grpc_call *)(call_stack)) - 1) +#define CALL_ELEM_FROM_CALL(call, idx) \ + grpc_call_stack_element(CALL_STACK_FROM_CALL(call), idx) +#define CALL_FROM_TOP_ELEM(top_elem) \ + CALL_FROM_CALL_STACK(grpc_call_stack_from_top_element(top_elem)) + +static void execute_batch(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_transport_stream_op_batch *op, + grpc_closure *start_batch_closure); +static void cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, + status_source source, grpc_status_code status, + const char *description); +static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c, + status_source source, grpc_error *error); +static void destroy_call(grpc_exec_ctx *exec_ctx, void *call_stack, + grpc_error *error); +static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, + grpc_error *error); +static void get_final_status(grpc_call *call, + void (*set_value)(grpc_status_code code, + void *user_data), + void *set_value_user_data, grpc_slice *details); +static void set_status_value_directly(grpc_status_code status, void *dest); +static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call, + status_source source, grpc_error *error); +static void process_data_after_md(grpc_exec_ctx *exec_ctx, batch_control *bctl); +static void post_batch_completion(grpc_exec_ctx *exec_ctx, batch_control *bctl); +static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl, + grpc_error *error, bool has_cancelled); + +static void add_init_error(grpc_error **composite, grpc_error *new_err) { + if (new_err == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) + *composite = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Call creation failed"); + *composite = grpc_error_add_child(*composite, new_err); +} + +void *grpc_call_arena_alloc(grpc_call *call, size_t size) { + return gpr_arena_alloc(call->arena, size); +} + +static parent_call *get_or_create_parent_call(grpc_call *call) { + parent_call *p = (parent_call *)gpr_atm_acq_load(&call->parent_call_atm); + if (p == NULL) { + p = (parent_call *)gpr_arena_alloc(call->arena, sizeof(*p)); + gpr_mu_init(&p->child_list_mu); + if (!gpr_atm_rel_cas(&call->parent_call_atm, (gpr_atm)NULL, (gpr_atm)p)) { + gpr_mu_destroy(&p->child_list_mu); + p = (parent_call *)gpr_atm_acq_load(&call->parent_call_atm); + } + } + return p; +} + +static parent_call *get_parent_call(grpc_call *call) { + return (parent_call *)gpr_atm_acq_load(&call->parent_call_atm); +} + +grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx, + const grpc_call_create_args *args, + grpc_call **out_call) { + size_t i, j; + grpc_error *error = GRPC_ERROR_NONE; + grpc_channel_stack *channel_stack = + grpc_channel_get_channel_stack(args->channel); + grpc_call *call; + GPR_TIMER_BEGIN("grpc_call_create", 0); + size_t initial_size = grpc_channel_get_call_size_estimate(args->channel); + GRPC_STATS_INC_CALL_INITIAL_SIZE(exec_ctx, initial_size); + gpr_arena *arena = gpr_arena_create(initial_size); + call = (grpc_call *)gpr_arena_alloc( + arena, sizeof(grpc_call) + channel_stack->call_stack_size); + gpr_ref_init(&call->ext_ref, 1); + call->arena = arena; + grpc_call_combiner_init(&call->call_combiner); + *out_call = call; + call->channel = args->channel; + call->cq = args->cq; + call->start_time = gpr_now(GPR_CLOCK_MONOTONIC); + /* Always support no compression */ + GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE); + call->is_client = args->server_transport_data == NULL; + if (call->is_client) { + GRPC_STATS_INC_CLIENT_CALLS_CREATED(exec_ctx); + } else { + GRPC_STATS_INC_SERVER_CALLS_CREATED(exec_ctx); + } + call->stream_op_payload.context = call->context; + grpc_slice path = grpc_empty_slice(); + if (call->is_client) { + GPR_ASSERT(args->add_initial_metadata_count < + MAX_SEND_EXTRA_METADATA_COUNT); + for (i = 0; i < args->add_initial_metadata_count; i++) { + call->send_extra_metadata[i].md = args->add_initial_metadata[i]; + if (grpc_slice_eq(GRPC_MDKEY(args->add_initial_metadata[i]), + GRPC_MDSTR_PATH)) { + path = grpc_slice_ref_internal( + GRPC_MDVALUE(args->add_initial_metadata[i])); + } + } + call->send_extra_metadata_count = (int)args->add_initial_metadata_count; + } else { + GPR_ASSERT(args->add_initial_metadata_count == 0); + call->send_extra_metadata_count = 0; + } + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) { + call->metadata_batch[i][j].deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + } + } + gpr_timespec send_deadline = + gpr_convert_clock_type(args->send_deadline, GPR_CLOCK_MONOTONIC); + + bool immediately_cancel = false; + + if (args->parent != NULL) { + child_call *cc = call->child = + (child_call *)gpr_arena_alloc(arena, sizeof(child_call)); + call->child->parent = args->parent; + + GRPC_CALL_INTERNAL_REF(args->parent, "child"); + GPR_ASSERT(call->is_client); + GPR_ASSERT(!args->parent->is_client); + + parent_call *pc = get_or_create_parent_call(args->parent); + + gpr_mu_lock(&pc->child_list_mu); + + if (args->propagation_mask & GRPC_PROPAGATE_DEADLINE) { + send_deadline = gpr_time_min( + gpr_convert_clock_type(send_deadline, + args->parent->send_deadline.clock_type), + args->parent->send_deadline); + } + /* for now GRPC_PROPAGATE_TRACING_CONTEXT *MUST* be passed with + * GRPC_PROPAGATE_STATS_CONTEXT */ + /* TODO(ctiller): This should change to use the appropriate census start_op + * call. */ + if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_TRACING_CONTEXT) { + if (0 == (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT)) { + add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Census tracing propagation requested " + "without Census context propagation")); + } + grpc_call_context_set(call, GRPC_CONTEXT_TRACING, + args->parent->context[GRPC_CONTEXT_TRACING].value, + NULL); + } else if (args->propagation_mask & GRPC_PROPAGATE_CENSUS_STATS_CONTEXT) { + add_init_error(&error, GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Census context propagation requested " + "without Census tracing propagation")); + } + if (args->propagation_mask & GRPC_PROPAGATE_CANCELLATION) { + call->cancellation_is_inherited = 1; + if (gpr_atm_acq_load(&args->parent->received_final_op_atm)) { + immediately_cancel = true; + } + } + + if (pc->first_child == NULL) { + pc->first_child = call; + cc->sibling_next = cc->sibling_prev = call; + } else { + cc->sibling_next = pc->first_child; + cc->sibling_prev = pc->first_child->child->sibling_prev; + cc->sibling_next->child->sibling_prev = + cc->sibling_prev->child->sibling_next = call; + } + + gpr_mu_unlock(&pc->child_list_mu); + } + + call->send_deadline = send_deadline; + + GRPC_CHANNEL_INTERNAL_REF(args->channel, "call"); + /* initial refcount dropped by grpc_call_unref */ + grpc_call_element_args call_args = { + .call_stack = CALL_STACK_FROM_CALL(call), + .server_transport_data = args->server_transport_data, + .context = call->context, + .path = path, + .start_time = call->start_time, + .deadline = send_deadline, + .arena = call->arena, + .call_combiner = &call->call_combiner}; + add_init_error(&error, grpc_call_stack_init(exec_ctx, channel_stack, 1, + destroy_call, call, &call_args)); + if (error != GRPC_ERROR_NONE) { + cancel_with_error(exec_ctx, call, STATUS_FROM_SURFACE, + GRPC_ERROR_REF(error)); + } + if (immediately_cancel) { + cancel_with_error(exec_ctx, call, STATUS_FROM_API_OVERRIDE, + GRPC_ERROR_CANCELLED); + } + if (args->cq != NULL) { + GPR_ASSERT( + args->pollset_set_alternative == NULL && + "Only one of 'cq' and 'pollset_set_alternative' should be non-NULL."); + GRPC_CQ_INTERNAL_REF(args->cq, "bind"); + call->pollent = + grpc_polling_entity_create_from_pollset(grpc_cq_pollset(args->cq)); + } + if (args->pollset_set_alternative != NULL) { + call->pollent = grpc_polling_entity_create_from_pollset_set( + args->pollset_set_alternative); + } + if (!grpc_polling_entity_is_empty(&call->pollent)) { + grpc_call_stack_set_pollset_or_pollset_set( + exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent); + } + + grpc_slice_unref_internal(exec_ctx, path); + + GPR_TIMER_END("grpc_call_create", 0); + return error; +} + +void grpc_call_set_completion_queue(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_completion_queue *cq) { + GPR_ASSERT(cq); + + if (grpc_polling_entity_pollset_set(&call->pollent) != NULL) { + gpr_log(GPR_ERROR, "A pollset_set is already registered for this call."); + abort(); + } + call->cq = cq; + GRPC_CQ_INTERNAL_REF(cq, "bind"); + call->pollent = grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)); + grpc_call_stack_set_pollset_or_pollset_set( + exec_ctx, CALL_STACK_FROM_CALL(call), &call->pollent); +} + +#ifndef NDEBUG +#define REF_REASON reason +#define REF_ARG , const char *reason +#else +#define REF_REASON "" +#define REF_ARG +#endif +void grpc_call_internal_ref(grpc_call *c REF_ARG) { + GRPC_CALL_STACK_REF(CALL_STACK_FROM_CALL(c), REF_REASON); +} +void grpc_call_internal_unref(grpc_exec_ctx *exec_ctx, grpc_call *c REF_ARG) { + GRPC_CALL_STACK_UNREF(exec_ctx, CALL_STACK_FROM_CALL(c), REF_REASON); +} + +static void release_call(grpc_exec_ctx *exec_ctx, void *call, + grpc_error *error) { + grpc_call *c = (grpc_call *)call; + grpc_channel *channel = c->channel; + grpc_call_combiner_destroy(&c->call_combiner); + gpr_free((char *)c->peer_string); + grpc_channel_update_call_size_estimate(channel, gpr_arena_destroy(c->arena)); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, channel, "call"); +} + +static void set_status_value_directly(grpc_status_code status, void *dest); +static void destroy_call(grpc_exec_ctx *exec_ctx, void *call, + grpc_error *error) { + size_t i; + int ii; + grpc_call *c = (grpc_call *)call; + GPR_TIMER_BEGIN("destroy_call", 0); + for (i = 0; i < 2; i++) { + grpc_metadata_batch_destroy( + exec_ctx, &c->metadata_batch[1 /* is_receiving */][i /* is_initial */]); + } + if (c->receiving_stream != NULL) { + grpc_byte_stream_destroy(exec_ctx, c->receiving_stream); + } + parent_call *pc = get_parent_call(c); + if (pc != NULL) { + gpr_mu_destroy(&pc->child_list_mu); + } + for (ii = 0; ii < c->send_extra_metadata_count; ii++) { + GRPC_MDELEM_UNREF(exec_ctx, c->send_extra_metadata[ii].md); + } + for (i = 0; i < GRPC_CONTEXT_COUNT; i++) { + if (c->context[i].destroy) { + c->context[i].destroy(c->context[i].value); + } + } + if (c->cq) { + GRPC_CQ_INTERNAL_UNREF(exec_ctx, c->cq, "bind"); + } + + get_final_status(c, set_status_value_directly, &c->final_info.final_status, + NULL); + c->final_info.stats.latency = + gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), c->start_time); + + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + GRPC_ERROR_UNREF( + unpack_received_status(gpr_atm_acq_load(&c->status[i])).error); + } + + grpc_call_stack_destroy(exec_ctx, CALL_STACK_FROM_CALL(c), &c->final_info, + GRPC_CLOSURE_INIT(&c->release_call, release_call, c, + grpc_schedule_on_exec_ctx)); + GPR_TIMER_END("destroy_call", 0); +} + +void grpc_call_ref(grpc_call *c) { gpr_ref(&c->ext_ref); } + +void grpc_call_unref(grpc_call *c) { + if (!gpr_unref(&c->ext_ref)) return; + + child_call *cc = c->child; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GPR_TIMER_BEGIN("grpc_call_unref", 0); + GRPC_API_TRACE("grpc_call_unref(c=%p)", 1, (c)); + + if (cc) { + parent_call *pc = get_parent_call(cc->parent); + gpr_mu_lock(&pc->child_list_mu); + if (c == pc->first_child) { + pc->first_child = cc->sibling_next; + if (c == pc->first_child) { + pc->first_child = NULL; + } + } + cc->sibling_prev->child->sibling_next = cc->sibling_next; + cc->sibling_next->child->sibling_prev = cc->sibling_prev; + gpr_mu_unlock(&pc->child_list_mu); + GRPC_CALL_INTERNAL_UNREF(&exec_ctx, cc->parent, "child"); + } + + GPR_ASSERT(!c->destroy_called); + c->destroy_called = 1; + bool cancel = gpr_atm_acq_load(&c->any_ops_sent_atm) != 0 && + gpr_atm_acq_load(&c->received_final_op_atm) == 0; + if (cancel) { + cancel_with_error(&exec_ctx, c, STATUS_FROM_API_OVERRIDE, + GRPC_ERROR_CANCELLED); + } else { + // Unset the call combiner cancellation closure. This has the + // effect of scheduling the previously set cancellation closure, if + // any, so that it can release any internal references it may be + // holding to the call stack. + grpc_call_combiner_set_notify_on_cancel(&exec_ctx, &c->call_combiner, NULL); + } + GRPC_CALL_INTERNAL_UNREF(&exec_ctx, c, "destroy"); + grpc_exec_ctx_finish(&exec_ctx); + GPR_TIMER_END("grpc_call_unref", 0); +} + +grpc_call_error grpc_call_cancel(grpc_call *call, void *reserved) { + GRPC_API_TRACE("grpc_call_cancel(call=%p, reserved=%p)", 2, (call, reserved)); + GPR_ASSERT(!reserved); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + cancel_with_error(&exec_ctx, call, STATUS_FROM_API_OVERRIDE, + GRPC_ERROR_CANCELLED); + grpc_exec_ctx_finish(&exec_ctx); + return GRPC_CALL_OK; +} + +// This is called via the call combiner to start sending a batch down +// the filter stack. +static void execute_batch_in_call_combiner(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *ignored) { + grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; + grpc_call *call = (grpc_call *)batch->handler_private.extra_arg; + GPR_TIMER_BEGIN("execute_batch", 0); + grpc_call_element *elem = CALL_ELEM_FROM_CALL(call, 0); + GRPC_CALL_LOG_OP(GPR_INFO, elem, batch); + elem->filter->start_transport_stream_op_batch(exec_ctx, elem, batch); + GPR_TIMER_END("execute_batch", 0); +} + +// start_batch_closure points to a caller-allocated closure to be used +// for entering the call combiner. +static void execute_batch(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_transport_stream_op_batch *batch, + grpc_closure *start_batch_closure) { + batch->handler_private.extra_arg = call; + GRPC_CLOSURE_INIT(start_batch_closure, execute_batch_in_call_combiner, batch, + grpc_schedule_on_exec_ctx); + GRPC_CALL_COMBINER_START(exec_ctx, &call->call_combiner, start_batch_closure, + GRPC_ERROR_NONE, "executing batch"); +} + +char *grpc_call_get_peer(grpc_call *call) { + char *peer_string = (char *)gpr_atm_acq_load(&call->peer_string); + if (peer_string != NULL) return gpr_strdup(peer_string); + peer_string = grpc_channel_get_target(call->channel); + if (peer_string != NULL) return peer_string; + return gpr_strdup("unknown"); +} + +grpc_call *grpc_call_from_top_element(grpc_call_element *elem) { + return CALL_FROM_TOP_ELEM(elem); +} + +/******************************************************************************* + * CANCELLATION + */ + +grpc_call_error grpc_call_cancel_with_status(grpc_call *c, + grpc_status_code status, + const char *description, + void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE( + "grpc_call_cancel_with_status(" + "c=%p, status=%d, description=%s, reserved=%p)", + 4, (c, (int)status, description, reserved)); + GPR_ASSERT(reserved == NULL); + cancel_with_status(&exec_ctx, c, STATUS_FROM_API_OVERRIDE, status, + description); + grpc_exec_ctx_finish(&exec_ctx); + return GRPC_CALL_OK; +} + +typedef struct { + grpc_call *call; + grpc_closure start_batch; + grpc_closure finish_batch; +} cancel_state; + +// The on_complete callback used when sending a cancel_stream batch down +// the filter stack. Yields the call combiner when the batch is done. +static void done_termination(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + cancel_state *state = (cancel_state *)arg; + GRPC_CALL_COMBINER_STOP(exec_ctx, &state->call->call_combiner, + "on_complete for cancel_stream op"); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, state->call, "termination"); + gpr_free(state); +} + +static void cancel_with_error(grpc_exec_ctx *exec_ctx, grpc_call *c, + status_source source, grpc_error *error) { + GRPC_CALL_INTERNAL_REF(c, "termination"); + // Inform the call combiner of the cancellation, so that it can cancel + // any in-flight asynchronous actions that may be holding the call + // combiner. This ensures that the cancel_stream batch can be sent + // down the filter stack in a timely manner. + grpc_call_combiner_cancel(exec_ctx, &c->call_combiner, GRPC_ERROR_REF(error)); + set_status_from_error(exec_ctx, c, source, GRPC_ERROR_REF(error)); + cancel_state *state = (cancel_state *)gpr_malloc(sizeof(*state)); + state->call = c; + GRPC_CLOSURE_INIT(&state->finish_batch, done_termination, state, + grpc_schedule_on_exec_ctx); + grpc_transport_stream_op_batch *op = + grpc_make_transport_stream_op(&state->finish_batch); + op->cancel_stream = true; + op->payload->cancel_stream.cancel_error = error; + execute_batch(exec_ctx, c, op, &state->start_batch); +} + +static grpc_error *error_from_status(grpc_status_code status, + const char *description) { + // copying 'description' is needed to ensure the grpc_call_cancel_with_status + // guarantee that can be short-lived. + return grpc_error_set_int( + grpc_error_set_str(GRPC_ERROR_CREATE_FROM_COPIED_STRING(description), + GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_copied_string(description)), + GRPC_ERROR_INT_GRPC_STATUS, status); +} + +static void cancel_with_status(grpc_exec_ctx *exec_ctx, grpc_call *c, + status_source source, grpc_status_code status, + const char *description) { + cancel_with_error(exec_ctx, c, source, + error_from_status(status, description)); +} + +/******************************************************************************* + * FINAL STATUS CODE MANIPULATION + */ + +static bool get_final_status_from( + grpc_call *call, grpc_error *error, bool allow_ok_status, + void (*set_value)(grpc_status_code code, void *user_data), + void *set_value_user_data, grpc_slice *details) { + grpc_status_code code; + grpc_slice slice = grpc_empty_slice(); + grpc_error_get_status(error, call->send_deadline, &code, &slice, NULL); + if (code == GRPC_STATUS_OK && !allow_ok_status) { + return false; + } + + set_value(code, set_value_user_data); + if (details != NULL) { + *details = grpc_slice_ref_internal(slice); + } + return true; +} + +static void get_final_status(grpc_call *call, + void (*set_value)(grpc_status_code code, + void *user_data), + void *set_value_user_data, grpc_slice *details) { + int i; + received_status status[STATUS_SOURCE_COUNT]; + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + status[i] = unpack_received_status(gpr_atm_acq_load(&call->status[i])); + } + if (GRPC_TRACER_ON(grpc_call_error_trace)) { + gpr_log(GPR_DEBUG, "get_final_status %s", call->is_client ? "CLI" : "SVR"); + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (status[i].is_set) { + gpr_log(GPR_DEBUG, " %d: %s", i, grpc_error_string(status[i].error)); + } + } + } + /* first search through ignoring "OK" statuses: if something went wrong, + * ensure we report it */ + for (int allow_ok_status = 0; allow_ok_status < 2; allow_ok_status++) { + /* search for the best status we can present: ideally the error we use has a + clearly defined grpc-status, and we'll prefer that. */ + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (status[i].is_set && + grpc_error_has_clear_grpc_status(status[i].error)) { + if (get_final_status_from(call, status[i].error, allow_ok_status != 0, + set_value, set_value_user_data, details)) { + return; + } + } + } + /* If no clearly defined status exists, search for 'anything' */ + for (i = 0; i < STATUS_SOURCE_COUNT; i++) { + if (status[i].is_set) { + if (get_final_status_from(call, status[i].error, allow_ok_status != 0, + set_value, set_value_user_data, details)) { + return; + } + } + } + } + /* If nothing exists, set some default */ + if (call->is_client) { + set_value(GRPC_STATUS_UNKNOWN, set_value_user_data); + } else { + set_value(GRPC_STATUS_OK, set_value_user_data); + } +} + +static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call, + status_source source, grpc_error *error) { + if (!gpr_atm_rel_cas(&call->status[source], + pack_received_status((received_status){ + .is_set = false, .error = GRPC_ERROR_NONE}), + pack_received_status((received_status){ + .is_set = true, .error = error}))) { + GRPC_ERROR_UNREF(error); + } +} + +/******************************************************************************* + * COMPRESSION + */ + +static void set_incoming_compression_algorithm( + grpc_call *call, grpc_compression_algorithm algo) { + GPR_ASSERT(algo < GRPC_COMPRESS_ALGORITHMS_COUNT); + call->incoming_compression_algorithm = algo; +} + +static void set_incoming_stream_compression_algorithm( + grpc_call *call, grpc_stream_compression_algorithm algo) { + GPR_ASSERT(algo < GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT); + call->incoming_stream_compression_algorithm = algo; +} + +grpc_compression_algorithm grpc_call_test_only_get_compression_algorithm( + grpc_call *call) { + grpc_compression_algorithm algorithm; + algorithm = call->incoming_compression_algorithm; + return algorithm; +} + +static grpc_compression_algorithm compression_algorithm_for_level_locked( + grpc_call *call, grpc_compression_level level) { + return grpc_compression_algorithm_for_level(level, + call->encodings_accepted_by_peer); +} + +static grpc_stream_compression_algorithm +stream_compression_algorithm_for_level_locked( + grpc_call *call, grpc_stream_compression_level level) { + return grpc_stream_compression_algorithm_for_level( + level, call->stream_encodings_accepted_by_peer); +} + +uint32_t grpc_call_test_only_get_message_flags(grpc_call *call) { + uint32_t flags; + flags = call->test_only_last_message_flags; + return flags; +} + +static void destroy_encodings_accepted_by_peer(void *p) { return; } + +static void set_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx, + grpc_call *call, grpc_mdelem mdel) { + size_t i; + grpc_compression_algorithm algorithm; + grpc_slice_buffer accept_encoding_parts; + grpc_slice accept_encoding_slice; + void *accepted_user_data; + + accepted_user_data = + grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); + if (accepted_user_data != NULL) { + call->encodings_accepted_by_peer = + (uint32_t)(((uintptr_t)accepted_user_data) - 1); + return; + } + + accept_encoding_slice = GRPC_MDVALUE(mdel); + grpc_slice_buffer_init(&accept_encoding_parts); + grpc_slice_split(accept_encoding_slice, ",", &accept_encoding_parts); + + /* No need to zero call->encodings_accepted_by_peer: grpc_call_create already + * zeroes the whole grpc_call */ + /* Always support no compression */ + GPR_BITSET(&call->encodings_accepted_by_peer, GRPC_COMPRESS_NONE); + for (i = 0; i < accept_encoding_parts.count; i++) { + grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i]; + if (grpc_compression_algorithm_parse(accept_encoding_entry_slice, + &algorithm)) { + GPR_BITSET(&call->encodings_accepted_by_peer, algorithm); + } else { + char *accept_encoding_entry_str = + grpc_slice_to_c_string(accept_encoding_entry_slice); + gpr_log(GPR_ERROR, + "Invalid entry in accept encoding metadata: '%s'. Ignoring.", + accept_encoding_entry_str); + gpr_free(accept_encoding_entry_str); + } + } + + grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts); + + grpc_mdelem_set_user_data( + mdel, destroy_encodings_accepted_by_peer, + (void *)(((uintptr_t)call->encodings_accepted_by_peer) + 1)); +} + +static void set_stream_encodings_accepted_by_peer(grpc_exec_ctx *exec_ctx, + grpc_call *call, + grpc_mdelem mdel) { + size_t i; + grpc_stream_compression_algorithm algorithm; + grpc_slice_buffer accept_encoding_parts; + grpc_slice accept_encoding_slice; + void *accepted_user_data; + + accepted_user_data = + grpc_mdelem_get_user_data(mdel, destroy_encodings_accepted_by_peer); + if (accepted_user_data != NULL) { + call->stream_encodings_accepted_by_peer = + (uint32_t)(((uintptr_t)accepted_user_data) - 1); + return; + } + + accept_encoding_slice = GRPC_MDVALUE(mdel); + grpc_slice_buffer_init(&accept_encoding_parts); + grpc_slice_split(accept_encoding_slice, ",", &accept_encoding_parts); + + /* Always support no compression */ + GPR_BITSET(&call->stream_encodings_accepted_by_peer, + GRPC_STREAM_COMPRESS_NONE); + for (i = 0; i < accept_encoding_parts.count; i++) { + grpc_slice accept_encoding_entry_slice = accept_encoding_parts.slices[i]; + if (grpc_stream_compression_algorithm_parse(accept_encoding_entry_slice, + &algorithm)) { + GPR_BITSET(&call->stream_encodings_accepted_by_peer, algorithm); + } else { + char *accept_encoding_entry_str = + grpc_slice_to_c_string(accept_encoding_entry_slice); + gpr_log(GPR_ERROR, + "Invalid entry in accept encoding metadata: '%s'. Ignoring.", + accept_encoding_entry_str); + gpr_free(accept_encoding_entry_str); + } + } + + grpc_slice_buffer_destroy_internal(exec_ctx, &accept_encoding_parts); + + grpc_mdelem_set_user_data( + mdel, destroy_encodings_accepted_by_peer, + (void *)(((uintptr_t)call->stream_encodings_accepted_by_peer) + 1)); +} + +uint32_t grpc_call_test_only_get_encodings_accepted_by_peer(grpc_call *call) { + uint32_t encodings_accepted_by_peer; + encodings_accepted_by_peer = call->encodings_accepted_by_peer; + return encodings_accepted_by_peer; +} + +uint32_t grpc_call_test_only_get_stream_encodings_accepted_by_peer( + grpc_call *call) { + uint32_t stream_encodings_accepted_by_peer; + stream_encodings_accepted_by_peer = call->stream_encodings_accepted_by_peer; + return stream_encodings_accepted_by_peer; +} + +grpc_stream_compression_algorithm +grpc_call_test_only_get_incoming_stream_encodings(grpc_call *call) { + return call->incoming_stream_compression_algorithm; +} + +static grpc_linked_mdelem *linked_from_md(const grpc_metadata *md) { + return (grpc_linked_mdelem *)&md->internal_data; +} + +static grpc_metadata *get_md_elem(grpc_metadata *metadata, + grpc_metadata *additional_metadata, int i, + int count) { + grpc_metadata *res = + i < count ? &metadata[i] : &additional_metadata[i - count]; + GPR_ASSERT(res); + return res; +} + +static int prepare_application_metadata( + grpc_exec_ctx *exec_ctx, grpc_call *call, int count, + grpc_metadata *metadata, int is_trailing, int prepend_extra_metadata, + grpc_metadata *additional_metadata, int additional_metadata_count) { + int total_count = count + additional_metadata_count; + int i; + grpc_metadata_batch *batch = + &call->metadata_batch[0 /* is_receiving */][is_trailing]; + for (i = 0; i < total_count; i++) { + const grpc_metadata *md = + get_md_elem(metadata, additional_metadata, i, count); + grpc_linked_mdelem *l = linked_from_md(md); + GPR_ASSERT(sizeof(grpc_linked_mdelem) == sizeof(md->internal_data)); + if (!GRPC_LOG_IF_ERROR("validate_metadata", + grpc_validate_header_key_is_legal(md->key))) { + break; + } else if (!grpc_is_binary_header(md->key) && + !GRPC_LOG_IF_ERROR( + "validate_metadata", + grpc_validate_header_nonbin_value_is_legal(md->value))) { + break; + } + l->md = grpc_mdelem_from_grpc_metadata(exec_ctx, (grpc_metadata *)md); + } + if (i != total_count) { + for (int j = 0; j < i; j++) { + const grpc_metadata *md = + get_md_elem(metadata, additional_metadata, j, count); + grpc_linked_mdelem *l = linked_from_md(md); + GRPC_MDELEM_UNREF(exec_ctx, l->md); + } + return 0; + } + if (prepend_extra_metadata) { + if (call->send_extra_metadata_count == 0) { + prepend_extra_metadata = 0; + } else { + for (i = 0; i < call->send_extra_metadata_count; i++) { + GRPC_LOG_IF_ERROR("prepare_application_metadata", + grpc_metadata_batch_link_tail( + exec_ctx, batch, &call->send_extra_metadata[i])); + } + } + } + for (i = 0; i < total_count; i++) { + grpc_metadata *md = get_md_elem(metadata, additional_metadata, i, count); + grpc_linked_mdelem *l = linked_from_md(md); + grpc_error *error = grpc_metadata_batch_link_tail(exec_ctx, batch, l); + if (error != GRPC_ERROR_NONE) { + GRPC_MDELEM_UNREF(exec_ctx, l->md); + } + GRPC_LOG_IF_ERROR("prepare_application_metadata", error); + } + call->send_extra_metadata_count = 0; + + return 1; +} + +/* we offset status by a small amount when storing it into transport metadata + as metadata cannot store a 0 value (which is used as OK for grpc_status_codes + */ +#define STATUS_OFFSET 1 +static void destroy_status(void *ignored) {} + +static uint32_t decode_status(grpc_mdelem md) { + uint32_t status; + void *user_data; + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_0)) return 0; + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_1)) return 1; + if (grpc_mdelem_eq(md, GRPC_MDELEM_GRPC_STATUS_2)) return 2; + user_data = grpc_mdelem_get_user_data(md, destroy_status); + if (user_data != NULL) { + status = ((uint32_t)(intptr_t)user_data) - STATUS_OFFSET; + } else { + if (!grpc_parse_slice_to_uint32(GRPC_MDVALUE(md), &status)) { + status = GRPC_STATUS_UNKNOWN; /* could not parse status code */ + } + grpc_mdelem_set_user_data(md, destroy_status, + (void *)(intptr_t)(status + STATUS_OFFSET)); + } + return status; +} + +static grpc_compression_algorithm decode_compression(grpc_mdelem md) { + grpc_compression_algorithm algorithm = + grpc_compression_algorithm_from_slice(GRPC_MDVALUE(md)); + if (algorithm == GRPC_COMPRESS_ALGORITHMS_COUNT) { + char *md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid incoming compression algorithm: '%s'. Interpreting " + "incoming data as uncompressed.", + md_c_str); + gpr_free(md_c_str); + return GRPC_COMPRESS_NONE; + } + return algorithm; +} + +static grpc_stream_compression_algorithm decode_stream_compression( + grpc_mdelem md) { + grpc_stream_compression_algorithm algorithm = + grpc_stream_compression_algorithm_from_slice(GRPC_MDVALUE(md)); + if (algorithm == GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) { + char *md_c_str = grpc_slice_to_c_string(GRPC_MDVALUE(md)); + gpr_log(GPR_ERROR, + "Invalid incoming stream compression algorithm: '%s'. Interpreting " + "incoming data as uncompressed.", + md_c_str); + gpr_free(md_c_str); + return GRPC_STREAM_COMPRESS_NONE; + } + return algorithm; +} + +static void publish_app_metadata(grpc_call *call, grpc_metadata_batch *b, + int is_trailing) { + if (b->list.count == 0) return; + GPR_TIMER_BEGIN("publish_app_metadata", 0); + grpc_metadata_array *dest; + grpc_metadata *mdusr; + dest = call->buffered_metadata[is_trailing]; + if (dest->count + b->list.count > dest->capacity) { + dest->capacity = + GPR_MAX(dest->capacity + b->list.count, dest->capacity * 3 / 2); + dest->metadata = (grpc_metadata *)gpr_realloc( + dest->metadata, sizeof(grpc_metadata) * dest->capacity); + } + for (grpc_linked_mdelem *l = b->list.head; l != NULL; l = l->next) { + mdusr = &dest->metadata[dest->count++]; + /* we pass back borrowed slices that are valid whilst the call is valid */ + mdusr->key = GRPC_MDKEY(l->md); + mdusr->value = GRPC_MDVALUE(l->md); + } + GPR_TIMER_END("publish_app_metadata", 0); +} + +static void recv_initial_filter(grpc_exec_ctx *exec_ctx, grpc_call *call, + grpc_metadata_batch *b) { + if (b->idx.named.content_encoding != NULL) { + if (b->idx.named.grpc_encoding != NULL) { + gpr_log(GPR_ERROR, + "Received both content-encoding and grpc-encoding header. " + "Ignoring grpc-encoding."); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_encoding); + } + GPR_TIMER_BEGIN("incoming_stream_compression_algorithm", 0); + set_incoming_stream_compression_algorithm( + call, decode_stream_compression(b->idx.named.content_encoding->md)); + GPR_TIMER_END("incoming_stream_compression_algorithm", 0); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.content_encoding); + } else if (b->idx.named.grpc_encoding != NULL) { + GPR_TIMER_BEGIN("incoming_compression_algorithm", 0); + set_incoming_compression_algorithm( + call, decode_compression(b->idx.named.grpc_encoding->md)); + GPR_TIMER_END("incoming_compression_algorithm", 0); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_encoding); + } + if (b->idx.named.grpc_accept_encoding != NULL) { + GPR_TIMER_BEGIN("encodings_accepted_by_peer", 0); + set_encodings_accepted_by_peer(exec_ctx, call, + b->idx.named.grpc_accept_encoding->md); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_accept_encoding); + GPR_TIMER_END("encodings_accepted_by_peer", 0); + } + if (b->idx.named.accept_encoding != NULL) { + GPR_TIMER_BEGIN("stream_encodings_accepted_by_peer", 0); + set_stream_encodings_accepted_by_peer(exec_ctx, call, + b->idx.named.accept_encoding->md); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.accept_encoding); + GPR_TIMER_END("stream_encodings_accepted_by_peer", 0); + } + publish_app_metadata(call, b, false); +} + +static void recv_trailing_filter(grpc_exec_ctx *exec_ctx, void *args, + grpc_metadata_batch *b) { + grpc_call *call = (grpc_call *)args; + if (b->idx.named.grpc_status != NULL) { + uint32_t status_code = decode_status(b->idx.named.grpc_status->md); + grpc_error *error = + status_code == GRPC_STATUS_OK + ? GRPC_ERROR_NONE + : grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Error received from peer"), + GRPC_ERROR_INT_GRPC_STATUS, + (intptr_t)status_code); + if (b->idx.named.grpc_message != NULL) { + error = grpc_error_set_str( + error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_ref_internal(GRPC_MDVALUE(b->idx.named.grpc_message->md))); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_message); + } else if (error != GRPC_ERROR_NONE) { + error = grpc_error_set_str(error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_empty_slice()); + } + set_status_from_error(exec_ctx, call, STATUS_FROM_WIRE, error); + grpc_metadata_batch_remove(exec_ctx, b, b->idx.named.grpc_status); + } + publish_app_metadata(call, b, true); +} + +grpc_call_stack *grpc_call_get_call_stack(grpc_call *call) { + return CALL_STACK_FROM_CALL(call); +} + +/******************************************************************************* + * BATCH API IMPLEMENTATION + */ + +static void set_status_value_directly(grpc_status_code status, void *dest) { + *(grpc_status_code *)dest = status; +} + +static void set_cancelled_value(grpc_status_code status, void *dest) { + *(int *)dest = (status != GRPC_STATUS_OK); +} + +static bool are_write_flags_valid(uint32_t flags) { + /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ + const uint32_t allowed_write_positions = + (GRPC_WRITE_USED_MASK | GRPC_WRITE_INTERNAL_USED_MASK); + const uint32_t invalid_positions = ~allowed_write_positions; + return !(flags & invalid_positions); +} + +static bool are_initial_metadata_flags_valid(uint32_t flags, bool is_client) { + /* check that only bits in GRPC_WRITE_(INTERNAL?)_USED_MASK are set */ + uint32_t invalid_positions = ~GRPC_INITIAL_METADATA_USED_MASK; + if (!is_client) { + invalid_positions |= GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST; + } + return !(flags & invalid_positions); +} + +static int batch_slot_for_op(grpc_op_type type) { + switch (type) { + case GRPC_OP_SEND_INITIAL_METADATA: + return 0; + case GRPC_OP_SEND_MESSAGE: + return 1; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + case GRPC_OP_SEND_STATUS_FROM_SERVER: + return 2; + case GRPC_OP_RECV_INITIAL_METADATA: + return 3; + case GRPC_OP_RECV_MESSAGE: + return 4; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + case GRPC_OP_RECV_STATUS_ON_CLIENT: + return 5; + } + GPR_UNREACHABLE_CODE(return 123456789); +} + +static batch_control *allocate_batch_control(grpc_call *call, + const grpc_op *ops, + size_t num_ops) { + int slot = batch_slot_for_op(ops[0].op); + batch_control **pslot = &call->active_batches[slot]; + if (*pslot == NULL) { + *pslot = + (batch_control *)gpr_arena_alloc(call->arena, sizeof(batch_control)); + } + batch_control *bctl = *pslot; + if (bctl->call != NULL) { + return NULL; + } + memset(bctl, 0, sizeof(*bctl)); + bctl->call = call; + bctl->op.payload = &call->stream_op_payload; + return bctl; +} + +static void finish_batch_completion(grpc_exec_ctx *exec_ctx, void *user_data, + grpc_cq_completion *storage) { + batch_control *bctl = (batch_control *)user_data; + grpc_call *call = bctl->call; + bctl->call = NULL; + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); +} + +static grpc_error *consolidate_batch_errors(batch_control *bctl) { + size_t n = (size_t)gpr_atm_acq_load(&bctl->num_errors); + if (n == 0) { + return GRPC_ERROR_NONE; + } else if (n == 1) { + /* Skip creating a composite error in the case that only one error was + logged */ + grpc_error *e = bctl->errors[0]; + bctl->errors[0] = NULL; + return e; + } else { + grpc_error *error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Call batch failed", bctl->errors, n); + for (size_t i = 0; i < n; i++) { + GRPC_ERROR_UNREF(bctl->errors[i]); + bctl->errors[i] = NULL; + } + return error; + } +} + +static void post_batch_completion(grpc_exec_ctx *exec_ctx, + batch_control *bctl) { + grpc_call *next_child_call; + grpc_call *call = bctl->call; + grpc_error *error = consolidate_batch_errors(bctl); + + if (bctl->op.send_initial_metadata) { + grpc_metadata_batch_destroy( + exec_ctx, + &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]); + } + if (bctl->op.send_message) { + call->sending_message = false; + } + if (bctl->op.send_trailing_metadata) { + grpc_metadata_batch_destroy( + exec_ctx, + &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]); + } + if (bctl->op.recv_trailing_metadata) { + grpc_metadata_batch *md = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + recv_trailing_filter(exec_ctx, call, md); + + /* propagate cancellation to any interested children */ + gpr_atm_rel_store(&call->received_final_op_atm, 1); + parent_call *pc = get_parent_call(call); + if (pc != NULL) { + grpc_call *child; + gpr_mu_lock(&pc->child_list_mu); + child = pc->first_child; + if (child != NULL) { + do { + next_child_call = child->child->sibling_next; + if (child->cancellation_is_inherited) { + GRPC_CALL_INTERNAL_REF(child, "propagate_cancel"); + cancel_with_error(exec_ctx, child, STATUS_FROM_API_OVERRIDE, + GRPC_ERROR_CANCELLED); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, child, "propagate_cancel"); + } + child = next_child_call; + } while (child != pc->first_child); + } + gpr_mu_unlock(&pc->child_list_mu); + } + + if (call->is_client) { + get_final_status(call, set_status_value_directly, + call->final_op.client.status, + call->final_op.client.status_details); + } else { + get_final_status(call, set_cancelled_value, + call->final_op.server.cancelled, NULL); + } + + GRPC_ERROR_UNREF(error); + error = GRPC_ERROR_NONE; + } + + if (bctl->completion_data.notify_tag.is_closure) { + /* unrefs bctl->error */ + bctl->call = NULL; + GRPC_CLOSURE_RUN( + exec_ctx, (grpc_closure *)bctl->completion_data.notify_tag.tag, error); + GRPC_CALL_INTERNAL_UNREF(exec_ctx, call, "completion"); + } else { + /* unrefs bctl->error */ + grpc_cq_end_op( + exec_ctx, bctl->call->cq, bctl->completion_data.notify_tag.tag, error, + finish_batch_completion, bctl, &bctl->completion_data.cq_completion); + } +} + +static void finish_batch_step(grpc_exec_ctx *exec_ctx, batch_control *bctl) { + if (gpr_unref(&bctl->steps_to_complete)) { + post_batch_completion(exec_ctx, bctl); + } +} + +static void continue_receiving_slices(grpc_exec_ctx *exec_ctx, + batch_control *bctl) { + grpc_error *error; + grpc_call *call = bctl->call; + for (;;) { + size_t remaining = call->receiving_stream->length - + (*call->receiving_buffer)->data.raw.slice_buffer.length; + if (remaining == 0) { + call->receiving_message = 0; + grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); + call->receiving_stream = NULL; + finish_batch_step(exec_ctx, bctl); + return; + } + if (grpc_byte_stream_next(exec_ctx, call->receiving_stream, remaining, + &call->receiving_slice_ready)) { + error = grpc_byte_stream_pull(exec_ctx, call->receiving_stream, + &call->receiving_slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, + call->receiving_slice); + } else { + grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); + call->receiving_stream = NULL; + grpc_byte_buffer_destroy(*call->receiving_buffer); + *call->receiving_buffer = NULL; + call->receiving_message = 0; + finish_batch_step(exec_ctx, bctl); + return; + } + } else { + return; + } + } +} + +static void receiving_slice_ready(grpc_exec_ctx *exec_ctx, void *bctlp, + grpc_error *error) { + batch_control *bctl = (batch_control *)bctlp; + grpc_call *call = bctl->call; + grpc_byte_stream *bs = call->receiving_stream; + bool release_error = false; + + if (error == GRPC_ERROR_NONE) { + grpc_slice slice; + error = grpc_byte_stream_pull(exec_ctx, bs, &slice); + if (error == GRPC_ERROR_NONE) { + grpc_slice_buffer_add(&(*call->receiving_buffer)->data.raw.slice_buffer, + slice); + continue_receiving_slices(exec_ctx, bctl); + } else { + /* Error returned by grpc_byte_stream_pull needs to be released manually + */ + release_error = true; + } + } + + if (error != GRPC_ERROR_NONE) { + if (GRPC_TRACER_ON(grpc_trace_operation_failures)) { + GRPC_LOG_IF_ERROR("receiving_slice_ready", GRPC_ERROR_REF(error)); + } + grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); + call->receiving_stream = NULL; + grpc_byte_buffer_destroy(*call->receiving_buffer); + *call->receiving_buffer = NULL; + call->receiving_message = 0; + finish_batch_step(exec_ctx, bctl); + if (release_error) { + GRPC_ERROR_UNREF(error); + } + } +} + +static void process_data_after_md(grpc_exec_ctx *exec_ctx, + batch_control *bctl) { + grpc_call *call = bctl->call; + if (call->receiving_stream == NULL) { + *call->receiving_buffer = NULL; + call->receiving_message = 0; + finish_batch_step(exec_ctx, bctl); + } else { + call->test_only_last_message_flags = call->receiving_stream->flags; + if ((call->receiving_stream->flags & GRPC_WRITE_INTERNAL_COMPRESS) && + (call->incoming_compression_algorithm > GRPC_COMPRESS_NONE)) { + *call->receiving_buffer = grpc_raw_compressed_byte_buffer_create( + NULL, 0, call->incoming_compression_algorithm); + } else { + *call->receiving_buffer = grpc_raw_byte_buffer_create(NULL, 0); + } + GRPC_CLOSURE_INIT(&call->receiving_slice_ready, receiving_slice_ready, bctl, + grpc_schedule_on_exec_ctx); + continue_receiving_slices(exec_ctx, bctl); + } +} + +static void receiving_stream_ready(grpc_exec_ctx *exec_ctx, void *bctlp, + grpc_error *error) { + batch_control *bctl = (batch_control *)bctlp; + grpc_call *call = bctl->call; + if (error != GRPC_ERROR_NONE) { + if (call->receiving_stream != NULL) { + grpc_byte_stream_destroy(exec_ctx, call->receiving_stream); + call->receiving_stream = NULL; + } + add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), true); + cancel_with_error(exec_ctx, call, STATUS_FROM_SURFACE, + GRPC_ERROR_REF(error)); + } + /* If recv_state is RECV_NONE, we will save the batch_control + * object with rel_cas, and will not use it after the cas. Its corresponding + * acq_load is in receiving_initial_metadata_ready() */ + if (error != GRPC_ERROR_NONE || call->receiving_stream == NULL || + !gpr_atm_rel_cas(&call->recv_state, RECV_NONE, (gpr_atm)bctlp)) { + process_data_after_md(exec_ctx, bctl); + } +} + +// The recv_message_ready callback used when sending a batch containing +// a recv_message op down the filter stack. Yields the call combiner +// before processing the received message. +static void receiving_stream_ready_in_call_combiner(grpc_exec_ctx *exec_ctx, + void *bctlp, + grpc_error *error) { + batch_control *bctl = (batch_control *)bctlp; + grpc_call *call = bctl->call; + GRPC_CALL_COMBINER_STOP(exec_ctx, &call->call_combiner, "recv_message_ready"); + receiving_stream_ready(exec_ctx, bctlp, error); +} + +static void validate_filtered_metadata(grpc_exec_ctx *exec_ctx, + batch_control *bctl) { + grpc_call *call = bctl->call; + /* validate compression algorithms */ + if (call->incoming_stream_compression_algorithm != + GRPC_STREAM_COMPRESS_NONE) { + const grpc_stream_compression_algorithm algo = + call->incoming_stream_compression_algorithm; + char *error_msg = NULL; + const grpc_compression_options compression_options = + grpc_channel_compression_options(call->channel); + if (algo >= GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT) { + gpr_asprintf(&error_msg, + "Invalid stream compression algorithm value '%d'.", algo); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, + GRPC_STATUS_UNIMPLEMENTED, error_msg); + } else if (grpc_compression_options_is_stream_compression_algorithm_enabled( + &compression_options, algo) == 0) { + /* check if algorithm is supported by current channel config */ + const char *algo_name = NULL; + grpc_stream_compression_algorithm_name(algo, &algo_name); + gpr_asprintf(&error_msg, "Stream compression algorithm '%s' is disabled.", + algo_name); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, + GRPC_STATUS_UNIMPLEMENTED, error_msg); + } + gpr_free(error_msg); + + GPR_ASSERT(call->stream_encodings_accepted_by_peer != 0); + if (!GPR_BITGET(call->stream_encodings_accepted_by_peer, + call->incoming_stream_compression_algorithm)) { + if (GRPC_TRACER_ON(grpc_compression_trace)) { + const char *algo_name = NULL; + grpc_stream_compression_algorithm_name( + call->incoming_stream_compression_algorithm, &algo_name); + gpr_log( + GPR_ERROR, + "Stream compression algorithm (content-encoding = '%s') not " + "present in the bitset of accepted encodings (accept-encodings: " + "'0x%x')", + algo_name, call->stream_encodings_accepted_by_peer); + } + } + } else if (call->incoming_compression_algorithm != GRPC_COMPRESS_NONE) { + const grpc_compression_algorithm algo = + call->incoming_compression_algorithm; + char *error_msg = NULL; + const grpc_compression_options compression_options = + grpc_channel_compression_options(call->channel); + /* check if algorithm is known */ + if (algo >= GRPC_COMPRESS_ALGORITHMS_COUNT) { + gpr_asprintf(&error_msg, "Invalid compression algorithm value '%d'.", + algo); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, + GRPC_STATUS_UNIMPLEMENTED, error_msg); + } else if (grpc_compression_options_is_algorithm_enabled( + &compression_options, algo) == 0) { + /* check if algorithm is supported by current channel config */ + const char *algo_name = NULL; + grpc_compression_algorithm_name(algo, &algo_name); + gpr_asprintf(&error_msg, "Compression algorithm '%s' is disabled.", + algo_name); + gpr_log(GPR_ERROR, "%s", error_msg); + cancel_with_status(exec_ctx, call, STATUS_FROM_SURFACE, + GRPC_STATUS_UNIMPLEMENTED, error_msg); + } else { + call->incoming_compression_algorithm = algo; + } + gpr_free(error_msg); + + GPR_ASSERT(call->encodings_accepted_by_peer != 0); + if (!GPR_BITGET(call->encodings_accepted_by_peer, + call->incoming_compression_algorithm)) { + if (GRPC_TRACER_ON(grpc_compression_trace)) { + const char *algo_name = NULL; + grpc_compression_algorithm_name(call->incoming_compression_algorithm, + &algo_name); + gpr_log(GPR_ERROR, + "Compression algorithm (grpc-encoding = '%s') not present in " + "the bitset of accepted encodings (grpc-accept-encodings: " + "'0x%x')", + algo_name, call->encodings_accepted_by_peer); + } + } + } +} + +static void add_batch_error(grpc_exec_ctx *exec_ctx, batch_control *bctl, + grpc_error *error, bool has_cancelled) { + if (error == GRPC_ERROR_NONE) return; + int idx = (int)gpr_atm_full_fetch_add(&bctl->num_errors, 1); + if (idx == 0 && !has_cancelled) { + cancel_with_error(exec_ctx, bctl->call, STATUS_FROM_CORE, + GRPC_ERROR_REF(error)); + } + bctl->errors[idx] = error; +} + +static void receiving_initial_metadata_ready(grpc_exec_ctx *exec_ctx, + void *bctlp, grpc_error *error) { + batch_control *bctl = (batch_control *)bctlp; + grpc_call *call = bctl->call; + + GRPC_CALL_COMBINER_STOP(exec_ctx, &call->call_combiner, + "recv_initial_metadata_ready"); + + add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), false); + if (error == GRPC_ERROR_NONE) { + grpc_metadata_batch *md = + &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; + recv_initial_filter(exec_ctx, call, md); + + /* TODO(ctiller): this could be moved into recv_initial_filter now */ + GPR_TIMER_BEGIN("validate_filtered_metadata", 0); + validate_filtered_metadata(exec_ctx, bctl); + GPR_TIMER_END("validate_filtered_metadata", 0); + + if (gpr_time_cmp(md->deadline, gpr_inf_future(md->deadline.clock_type)) != + 0 && + !call->is_client) { + call->send_deadline = + gpr_convert_clock_type(md->deadline, GPR_CLOCK_MONOTONIC); + } + } + + grpc_closure *saved_rsr_closure = NULL; + while (true) { + gpr_atm rsr_bctlp = gpr_atm_acq_load(&call->recv_state); + /* Should only receive initial metadata once */ + GPR_ASSERT(rsr_bctlp != 1); + if (rsr_bctlp == 0) { + /* We haven't seen initial metadata and messages before, thus initial + * metadata is received first. + * no_barrier_cas is used, as this function won't access the batch_control + * object saved by receiving_stream_ready() if the initial metadata is + * received first. */ + if (gpr_atm_no_barrier_cas(&call->recv_state, RECV_NONE, + RECV_INITIAL_METADATA_FIRST)) { + break; + } + } else { + /* Already received messages */ + saved_rsr_closure = GRPC_CLOSURE_CREATE(receiving_stream_ready, + (batch_control *)rsr_bctlp, + grpc_schedule_on_exec_ctx); + /* No need to modify recv_state */ + break; + } + } + if (saved_rsr_closure != NULL) { + GRPC_CLOSURE_RUN(exec_ctx, saved_rsr_closure, GRPC_ERROR_REF(error)); + } + + finish_batch_step(exec_ctx, bctl); +} + +static void finish_batch(grpc_exec_ctx *exec_ctx, void *bctlp, + grpc_error *error) { + batch_control *bctl = (batch_control *)bctlp; + grpc_call *call = bctl->call; + GRPC_CALL_COMBINER_STOP(exec_ctx, &call->call_combiner, "on_complete"); + add_batch_error(exec_ctx, bctl, GRPC_ERROR_REF(error), false); + finish_batch_step(exec_ctx, bctl); +} + +static void free_no_op_completion(grpc_exec_ctx *exec_ctx, void *p, + grpc_cq_completion *completion) { + gpr_free(completion); +} + +static grpc_call_error call_start_batch(grpc_exec_ctx *exec_ctx, + grpc_call *call, const grpc_op *ops, + size_t nops, void *notify_tag, + int is_notify_tag_closure) { + size_t i; + const grpc_op *op; + batch_control *bctl; + int num_completion_callbacks_needed = 1; + grpc_call_error error = GRPC_CALL_OK; + grpc_transport_stream_op_batch *stream_op; + grpc_transport_stream_op_batch_payload *stream_op_payload; + + GPR_TIMER_BEGIN("grpc_call_start_batch", 0); + GRPC_CALL_LOG_BATCH(GPR_INFO, call, ops, nops, notify_tag); + + if (nops == 0) { + if (!is_notify_tag_closure) { + GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); + grpc_cq_end_op( + exec_ctx, call->cq, notify_tag, GRPC_ERROR_NONE, + free_no_op_completion, NULL, + (grpc_cq_completion *)gpr_malloc(sizeof(grpc_cq_completion))); + } else { + GRPC_CLOSURE_SCHED(exec_ctx, (grpc_closure *)notify_tag, GRPC_ERROR_NONE); + } + error = GRPC_CALL_OK; + goto done; + } + + bctl = allocate_batch_control(call, ops, nops); + if (bctl == NULL) { + return GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + } + bctl->completion_data.notify_tag.tag = notify_tag; + bctl->completion_data.notify_tag.is_closure = + (uint8_t)(is_notify_tag_closure != 0); + + stream_op = &bctl->op; + stream_op_payload = &call->stream_op_payload; + + /* rewrite batch ops into a transport op */ + for (i = 0; i < nops; i++) { + op = &ops[i]; + if (op->reserved != NULL) { + error = GRPC_CALL_ERROR; + goto done_with_error; + } + switch (op->op) { + case GRPC_OP_SEND_INITIAL_METADATA: { + /* Flag validation: currently allow no flags */ + if (!are_initial_metadata_flags_valid(op->flags, call->is_client)) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->sent_initial_metadata) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + /* process compression level */ + memset(&call->compression_md, 0, sizeof(call->compression_md)); + size_t additional_metadata_count = 0; + grpc_compression_level effective_compression_level = + GRPC_COMPRESS_LEVEL_NONE; + grpc_stream_compression_level effective_stream_compression_level = + GRPC_STREAM_COMPRESS_LEVEL_NONE; + bool level_set = false; + bool stream_compression = false; + if (op->data.send_initial_metadata.maybe_stream_compression_level + .is_set) { + effective_stream_compression_level = + op->data.send_initial_metadata.maybe_stream_compression_level + .level; + level_set = true; + stream_compression = true; + } else if (op->data.send_initial_metadata.maybe_compression_level + .is_set) { + effective_compression_level = + op->data.send_initial_metadata.maybe_compression_level.level; + level_set = true; + } else { + const grpc_compression_options copts = + grpc_channel_compression_options(call->channel); + if (copts.default_stream_compression_level.is_set) { + level_set = true; + effective_stream_compression_level = + copts.default_stream_compression_level.level; + stream_compression = true; + } else if (copts.default_level.is_set) { + level_set = true; + effective_compression_level = copts.default_level.level; + } + } + if (level_set && !call->is_client) { + if (stream_compression) { + const grpc_stream_compression_algorithm calgo = + stream_compression_algorithm_for_level_locked( + call, effective_stream_compression_level); + call->compression_md.key = + GRPC_MDSTR_GRPC_INTERNAL_STREAM_ENCODING_REQUEST; + call->compression_md.value = + grpc_stream_compression_algorithm_slice(calgo); + } else { + const grpc_compression_algorithm calgo = + compression_algorithm_for_level_locked( + call, effective_compression_level); + /* the following will be picked up by the compress filter and used + * as the call's compression algorithm. */ + call->compression_md.key = + GRPC_MDSTR_GRPC_INTERNAL_ENCODING_REQUEST; + call->compression_md.value = + grpc_compression_algorithm_slice(calgo); + additional_metadata_count++; + } + } + + if (op->data.send_initial_metadata.count + additional_metadata_count > + INT_MAX) { + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + stream_op->send_initial_metadata = true; + call->sent_initial_metadata = true; + if (!prepare_application_metadata( + exec_ctx, call, (int)op->data.send_initial_metadata.count, + op->data.send_initial_metadata.metadata, 0, call->is_client, + &call->compression_md, (int)additional_metadata_count)) { + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + /* TODO(ctiller): just make these the same variable? */ + if (call->is_client) { + call->metadata_batch[0][0].deadline = call->send_deadline; + } + stream_op_payload->send_initial_metadata.send_initial_metadata = + &call->metadata_batch[0 /* is_receiving */][0 /* is_trailing */]; + stream_op_payload->send_initial_metadata.send_initial_metadata_flags = + op->flags; + if (call->is_client) { + stream_op_payload->send_initial_metadata.peer_string = + &call->peer_string; + } + break; + } + case GRPC_OP_SEND_MESSAGE: { + if (!are_write_flags_valid(op->flags)) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (op->data.send_message.send_message == NULL) { + error = GRPC_CALL_ERROR_INVALID_MESSAGE; + goto done_with_error; + } + if (call->sending_message) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + stream_op->send_message = true; + call->sending_message = true; + grpc_slice_buffer_stream_init( + &call->sending_stream, + &op->data.send_message.send_message->data.raw.slice_buffer, + op->flags); + /* If the outgoing buffer is already compressed, mark it as so in the + flags. These will be picked up by the compression filter and further + (wasteful) attempts at compression skipped. */ + if (op->data.send_message.send_message->data.raw.compression > + GRPC_COMPRESS_NONE) { + call->sending_stream.base.flags |= GRPC_WRITE_INTERNAL_COMPRESS; + } + stream_op_payload->send_message.send_message = + &call->sending_stream.base; + break; + } + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (!call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done_with_error; + } + if (call->sent_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + stream_op->send_trailing_metadata = true; + call->sent_final_op = true; + stream_op_payload->send_trailing_metadata.send_trailing_metadata = + &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; + break; + } + case GRPC_OP_SEND_STATUS_FROM_SERVER: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_CLIENT; + goto done_with_error; + } + if (call->sent_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + if (op->data.send_status_from_server.trailing_metadata_count > + INT_MAX) { + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + stream_op->send_trailing_metadata = true; + call->sent_final_op = true; + GPR_ASSERT(call->send_extra_metadata_count == 0); + call->send_extra_metadata_count = 1; + call->send_extra_metadata[0].md = grpc_channel_get_reffed_status_elem( + exec_ctx, call->channel, op->data.send_status_from_server.status); + { + grpc_error *override_error = GRPC_ERROR_NONE; + if (op->data.send_status_from_server.status != GRPC_STATUS_OK) { + override_error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Error from server send status"); + } + if (op->data.send_status_from_server.status_details != NULL) { + call->send_extra_metadata[1].md = grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_GRPC_MESSAGE, + grpc_slice_ref_internal( + *op->data.send_status_from_server.status_details)); + call->send_extra_metadata_count++; + char *msg = grpc_slice_to_c_string( + GRPC_MDVALUE(call->send_extra_metadata[1].md)); + override_error = + grpc_error_set_str(override_error, GRPC_ERROR_STR_GRPC_MESSAGE, + grpc_slice_from_copied_string(msg)); + gpr_free(msg); + } + set_status_from_error(exec_ctx, call, STATUS_FROM_API_OVERRIDE, + override_error); + } + if (!prepare_application_metadata( + exec_ctx, call, + (int)op->data.send_status_from_server.trailing_metadata_count, + op->data.send_status_from_server.trailing_metadata, 1, 1, NULL, + 0)) { + for (int n = 0; n < call->send_extra_metadata_count; n++) { + GRPC_MDELEM_UNREF(exec_ctx, call->send_extra_metadata[n].md); + } + call->send_extra_metadata_count = 0; + error = GRPC_CALL_ERROR_INVALID_METADATA; + goto done_with_error; + } + stream_op_payload->send_trailing_metadata.send_trailing_metadata = + &call->metadata_batch[0 /* is_receiving */][1 /* is_trailing */]; + break; + } + case GRPC_OP_RECV_INITIAL_METADATA: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->received_initial_metadata) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->received_initial_metadata = true; + call->buffered_metadata[0] = + op->data.recv_initial_metadata.recv_initial_metadata; + GRPC_CLOSURE_INIT(&call->receiving_initial_metadata_ready, + receiving_initial_metadata_ready, bctl, + grpc_schedule_on_exec_ctx); + stream_op->recv_initial_metadata = true; + stream_op_payload->recv_initial_metadata.recv_initial_metadata = + &call->metadata_batch[1 /* is_receiving */][0 /* is_trailing */]; + stream_op_payload->recv_initial_metadata.recv_initial_metadata_ready = + &call->receiving_initial_metadata_ready; + if (!call->is_client) { + stream_op_payload->recv_initial_metadata.peer_string = + &call->peer_string; + } + num_completion_callbacks_needed++; + break; + } + case GRPC_OP_RECV_MESSAGE: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->receiving_message) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->receiving_message = true; + stream_op->recv_message = true; + call->receiving_buffer = op->data.recv_message.recv_message; + stream_op_payload->recv_message.recv_message = &call->receiving_stream; + GRPC_CLOSURE_INIT(&call->receiving_stream_ready, + receiving_stream_ready_in_call_combiner, bctl, + grpc_schedule_on_exec_ctx); + stream_op_payload->recv_message.recv_message_ready = + &call->receiving_stream_ready; + num_completion_callbacks_needed++; + break; + } + case GRPC_OP_RECV_STATUS_ON_CLIENT: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (!call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_SERVER; + goto done_with_error; + } + if (call->requested_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->requested_final_op = true; + call->buffered_metadata[1] = + op->data.recv_status_on_client.trailing_metadata; + call->final_op.client.status = op->data.recv_status_on_client.status; + call->final_op.client.status_details = + op->data.recv_status_on_client.status_details; + stream_op->recv_trailing_metadata = true; + stream_op->collect_stats = true; + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + stream_op_payload->collect_stats.collect_stats = + &call->final_info.stats.transport_stream_stats; + break; + } + case GRPC_OP_RECV_CLOSE_ON_SERVER: { + /* Flag validation: currently allow no flags */ + if (op->flags != 0) { + error = GRPC_CALL_ERROR_INVALID_FLAGS; + goto done_with_error; + } + if (call->is_client) { + error = GRPC_CALL_ERROR_NOT_ON_CLIENT; + goto done_with_error; + } + if (call->requested_final_op) { + error = GRPC_CALL_ERROR_TOO_MANY_OPERATIONS; + goto done_with_error; + } + call->requested_final_op = true; + call->final_op.server.cancelled = + op->data.recv_close_on_server.cancelled; + stream_op->recv_trailing_metadata = true; + stream_op->collect_stats = true; + stream_op_payload->recv_trailing_metadata.recv_trailing_metadata = + &call->metadata_batch[1 /* is_receiving */][1 /* is_trailing */]; + stream_op_payload->collect_stats.collect_stats = + &call->final_info.stats.transport_stream_stats; + break; + } + } + } + + GRPC_CALL_INTERNAL_REF(call, "completion"); + if (!is_notify_tag_closure) { + GPR_ASSERT(grpc_cq_begin_op(call->cq, notify_tag)); + } + gpr_ref_init(&bctl->steps_to_complete, num_completion_callbacks_needed); + + GRPC_CLOSURE_INIT(&bctl->finish_batch, finish_batch, bctl, + grpc_schedule_on_exec_ctx); + stream_op->on_complete = &bctl->finish_batch; + gpr_atm_rel_store(&call->any_ops_sent_atm, 1); + + execute_batch(exec_ctx, call, stream_op, &bctl->start_batch); + +done: + GPR_TIMER_END("grpc_call_start_batch", 0); + return error; + +done_with_error: + /* reverse any mutations that occured */ + if (stream_op->send_initial_metadata) { + call->sent_initial_metadata = false; + grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][0]); + } + if (stream_op->send_message) { + call->sending_message = false; + grpc_byte_stream_destroy(exec_ctx, &call->sending_stream.base); + } + if (stream_op->send_trailing_metadata) { + call->sent_final_op = false; + grpc_metadata_batch_clear(exec_ctx, &call->metadata_batch[0][1]); + } + if (stream_op->recv_initial_metadata) { + call->received_initial_metadata = false; + } + if (stream_op->recv_message) { + call->receiving_message = false; + } + if (stream_op->recv_trailing_metadata) { + call->requested_final_op = false; + } + goto done; +} + +grpc_call_error grpc_call_start_batch(grpc_call *call, const grpc_op *ops, + size_t nops, void *tag, void *reserved) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call_error err; + + GRPC_API_TRACE( + "grpc_call_start_batch(call=%p, ops=%p, nops=%lu, tag=%p, " + "reserved=%p)", + 5, (call, ops, (unsigned long)nops, tag, reserved)); + + if (reserved != NULL) { + err = GRPC_CALL_ERROR; + } else { + err = call_start_batch(&exec_ctx, call, ops, nops, tag, 0); + } + + grpc_exec_ctx_finish(&exec_ctx); + return err; +} + +grpc_call_error grpc_call_start_batch_and_execute(grpc_exec_ctx *exec_ctx, + grpc_call *call, + const grpc_op *ops, + size_t nops, + grpc_closure *closure) { + return call_start_batch(exec_ctx, call, ops, nops, closure, 1); +} + +void grpc_call_context_set(grpc_call *call, grpc_context_index elem, + void *value, void (*destroy)(void *value)) { + if (call->context[elem].destroy) { + call->context[elem].destroy(call->context[elem].value); + } + call->context[elem].value = value; + call->context[elem].destroy = destroy; +} + +void *grpc_call_context_get(grpc_call *call, grpc_context_index elem) { + return call->context[elem].value; +} + +uint8_t grpc_call_is_client(grpc_call *call) { return call->is_client; } + +grpc_compression_algorithm grpc_call_compression_for_level( + grpc_call *call, grpc_compression_level level) { + grpc_compression_algorithm algo = + compression_algorithm_for_level_locked(call, level); + return algo; +} + +const char *grpc_call_error_to_string(grpc_call_error error) { + switch (error) { + case GRPC_CALL_ERROR: + return "GRPC_CALL_ERROR"; + case GRPC_CALL_ERROR_ALREADY_ACCEPTED: + return "GRPC_CALL_ERROR_ALREADY_ACCEPTED"; + case GRPC_CALL_ERROR_ALREADY_FINISHED: + return "GRPC_CALL_ERROR_ALREADY_FINISHED"; + case GRPC_CALL_ERROR_ALREADY_INVOKED: + return "GRPC_CALL_ERROR_ALREADY_INVOKED"; + case GRPC_CALL_ERROR_BATCH_TOO_BIG: + return "GRPC_CALL_ERROR_BATCH_TOO_BIG"; + case GRPC_CALL_ERROR_INVALID_FLAGS: + return "GRPC_CALL_ERROR_INVALID_FLAGS"; + case GRPC_CALL_ERROR_INVALID_MESSAGE: + return "GRPC_CALL_ERROR_INVALID_MESSAGE"; + case GRPC_CALL_ERROR_INVALID_METADATA: + return "GRPC_CALL_ERROR_INVALID_METADATA"; + case GRPC_CALL_ERROR_NOT_INVOKED: + return "GRPC_CALL_ERROR_NOT_INVOKED"; + case GRPC_CALL_ERROR_NOT_ON_CLIENT: + return "GRPC_CALL_ERROR_NOT_ON_CLIENT"; + case GRPC_CALL_ERROR_NOT_ON_SERVER: + return "GRPC_CALL_ERROR_NOT_ON_SERVER"; + case GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE: + return "GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE"; + case GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH: + return "GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH"; + case GRPC_CALL_ERROR_TOO_MANY_OPERATIONS: + return "GRPC_CALL_ERROR_TOO_MANY_OPERATIONS"; + case GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN: + return "GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN"; + case GRPC_CALL_OK: + return "GRPC_CALL_OK"; + } + GPR_UNREACHABLE_CODE(return "GRPC_CALL_ERROR_UNKNOW"); +} diff --git a/src/core/lib/surface/call_details.c b/src/core/lib/surface/call_details.c deleted file mode 100644 index ea9208c7e3..0000000000 --- a/src/core/lib/surface/call_details.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include - -#include "src/core/lib/iomgr/exec_ctx.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/api_trace.h" - -void grpc_call_details_init(grpc_call_details* cd) { - GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd)); - memset(cd, 0, sizeof(*cd)); - cd->method = grpc_empty_slice(); - cd->host = grpc_empty_slice(); -} - -void grpc_call_details_destroy(grpc_call_details* cd) { - GRPC_API_TRACE("grpc_call_details_destroy(cd=%p)", 1, (cd)); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_slice_unref_internal(&exec_ctx, cd->method); - grpc_slice_unref_internal(&exec_ctx, cd->host); - grpc_exec_ctx_finish(&exec_ctx); -} diff --git a/src/core/lib/surface/call_details.cc b/src/core/lib/surface/call_details.cc new file mode 100644 index 0000000000..ea9208c7e3 --- /dev/null +++ b/src/core/lib/surface/call_details.cc @@ -0,0 +1,41 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include + +#include "src/core/lib/iomgr/exec_ctx.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/api_trace.h" + +void grpc_call_details_init(grpc_call_details* cd) { + GRPC_API_TRACE("grpc_call_details_init(cd=%p)", 1, (cd)); + memset(cd, 0, sizeof(*cd)); + cd->method = grpc_empty_slice(); + cd->host = grpc_empty_slice(); +} + +void grpc_call_details_destroy(grpc_call_details* cd) { + GRPC_API_TRACE("grpc_call_details_destroy(cd=%p)", 1, (cd)); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_slice_unref_internal(&exec_ctx, cd->method); + grpc_slice_unref_internal(&exec_ctx, cd->host); + grpc_exec_ctx_finish(&exec_ctx); +} diff --git a/src/core/lib/surface/call_log_batch.c b/src/core/lib/surface/call_log_batch.c deleted file mode 100644 index 4a1c265817..0000000000 --- a/src/core/lib/surface/call_log_batch.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/call.h" - -#include -#include -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) { - size_t i; - if (md == NULL) { - gpr_strvec_add(b, gpr_strdup("(nil)")); - return; - } - for (i = 0; i < count; i++) { - gpr_strvec_add(b, gpr_strdup("\nkey=")); - gpr_strvec_add(b, grpc_slice_to_c_string(md[i].key)); - - gpr_strvec_add(b, gpr_strdup(" value=")); - gpr_strvec_add(b, - grpc_dump_slice(md[i].value, GPR_DUMP_HEX | GPR_DUMP_ASCII)); - } -} - -char *grpc_op_string(const grpc_op *op) { - char *tmp; - char *out; - - gpr_strvec b; - gpr_strvec_init(&b); - - switch (op->op) { - case GRPC_OP_SEND_INITIAL_METADATA: - gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA")); - add_metadata(&b, op->data.send_initial_metadata.metadata, - op->data.send_initial_metadata.count); - break; - case GRPC_OP_SEND_MESSAGE: - gpr_asprintf(&tmp, "SEND_MESSAGE ptr=%p", - op->data.send_message.send_message); - gpr_strvec_add(&b, tmp); - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - gpr_strvec_add(&b, gpr_strdup("SEND_CLOSE_FROM_CLIENT")); - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - gpr_asprintf(&tmp, "SEND_STATUS_FROM_SERVER status=%d details=", - op->data.send_status_from_server.status); - gpr_strvec_add(&b, tmp); - if (op->data.send_status_from_server.status_details != NULL) { - gpr_strvec_add(&b, grpc_dump_slice( - *op->data.send_status_from_server.status_details, - GPR_DUMP_ASCII)); - } else { - gpr_strvec_add(&b, gpr_strdup("(null)")); - } - add_metadata(&b, op->data.send_status_from_server.trailing_metadata, - op->data.send_status_from_server.trailing_metadata_count); - break; - case GRPC_OP_RECV_INITIAL_METADATA: - gpr_asprintf(&tmp, "RECV_INITIAL_METADATA ptr=%p", - op->data.recv_initial_metadata.recv_initial_metadata); - gpr_strvec_add(&b, tmp); - break; - case GRPC_OP_RECV_MESSAGE: - gpr_asprintf(&tmp, "RECV_MESSAGE ptr=%p", - op->data.recv_message.recv_message); - gpr_strvec_add(&b, tmp); - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - gpr_asprintf(&tmp, - "RECV_STATUS_ON_CLIENT metadata=%p status=%p details=%p", - op->data.recv_status_on_client.trailing_metadata, - op->data.recv_status_on_client.status, - op->data.recv_status_on_client.status_details); - gpr_strvec_add(&b, tmp); - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - gpr_asprintf(&tmp, "RECV_CLOSE_ON_SERVER cancelled=%p", - op->data.recv_close_on_server.cancelled); - gpr_strvec_add(&b, tmp); - } - out = gpr_strvec_flatten(&b, NULL); - gpr_strvec_destroy(&b); - - return out; -} - -void grpc_call_log_batch(const char *file, int line, gpr_log_severity severity, - grpc_call *call, const grpc_op *ops, size_t nops, - void *tag) { - char *tmp; - size_t i; - for (i = 0; i < nops; i++) { - tmp = grpc_op_string(&ops[i]); - gpr_log(file, line, severity, "ops[%" PRIuPTR "]: %s", i, tmp); - gpr_free(tmp); - } -} diff --git a/src/core/lib/surface/call_log_batch.cc b/src/core/lib/surface/call_log_batch.cc new file mode 100644 index 0000000000..4a1c265817 --- /dev/null +++ b/src/core/lib/surface/call_log_batch.cc @@ -0,0 +1,116 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/call.h" + +#include +#include +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +static void add_metadata(gpr_strvec *b, const grpc_metadata *md, size_t count) { + size_t i; + if (md == NULL) { + gpr_strvec_add(b, gpr_strdup("(nil)")); + return; + } + for (i = 0; i < count; i++) { + gpr_strvec_add(b, gpr_strdup("\nkey=")); + gpr_strvec_add(b, grpc_slice_to_c_string(md[i].key)); + + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add(b, + grpc_dump_slice(md[i].value, GPR_DUMP_HEX | GPR_DUMP_ASCII)); + } +} + +char *grpc_op_string(const grpc_op *op) { + char *tmp; + char *out; + + gpr_strvec b; + gpr_strvec_init(&b); + + switch (op->op) { + case GRPC_OP_SEND_INITIAL_METADATA: + gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA")); + add_metadata(&b, op->data.send_initial_metadata.metadata, + op->data.send_initial_metadata.count); + break; + case GRPC_OP_SEND_MESSAGE: + gpr_asprintf(&tmp, "SEND_MESSAGE ptr=%p", + op->data.send_message.send_message); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + gpr_strvec_add(&b, gpr_strdup("SEND_CLOSE_FROM_CLIENT")); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + gpr_asprintf(&tmp, "SEND_STATUS_FROM_SERVER status=%d details=", + op->data.send_status_from_server.status); + gpr_strvec_add(&b, tmp); + if (op->data.send_status_from_server.status_details != NULL) { + gpr_strvec_add(&b, grpc_dump_slice( + *op->data.send_status_from_server.status_details, + GPR_DUMP_ASCII)); + } else { + gpr_strvec_add(&b, gpr_strdup("(null)")); + } + add_metadata(&b, op->data.send_status_from_server.trailing_metadata, + op->data.send_status_from_server.trailing_metadata_count); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + gpr_asprintf(&tmp, "RECV_INITIAL_METADATA ptr=%p", + op->data.recv_initial_metadata.recv_initial_metadata); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_RECV_MESSAGE: + gpr_asprintf(&tmp, "RECV_MESSAGE ptr=%p", + op->data.recv_message.recv_message); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + gpr_asprintf(&tmp, + "RECV_STATUS_ON_CLIENT metadata=%p status=%p details=%p", + op->data.recv_status_on_client.trailing_metadata, + op->data.recv_status_on_client.status, + op->data.recv_status_on_client.status_details); + gpr_strvec_add(&b, tmp); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + gpr_asprintf(&tmp, "RECV_CLOSE_ON_SERVER cancelled=%p", + op->data.recv_close_on_server.cancelled); + gpr_strvec_add(&b, tmp); + } + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); + + return out; +} + +void grpc_call_log_batch(const char *file, int line, gpr_log_severity severity, + grpc_call *call, const grpc_op *ops, size_t nops, + void *tag) { + char *tmp; + size_t i; + for (i = 0; i < nops; i++) { + tmp = grpc_op_string(&ops[i]); + gpr_log(file, line, severity, "ops[%" PRIuPTR "]: %s", i, tmp); + gpr_free(tmp); + } +} diff --git a/src/core/lib/surface/channel.c b/src/core/lib/surface/channel.c deleted file mode 100644 index 48962e5e45..0000000000 --- a/src/core/lib/surface/channel.c +++ /dev/null @@ -1,454 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/channel.h" - -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/transport/static_metadata.h" - -/** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS. - * Avoids needing to take a metadata context lock for sending status - * if the status code is <= NUM_CACHED_STATUS_ELEMS. - * Sized to allow the most commonly used codes to fit in - * (OK, Cancelled, Unknown). */ -#define NUM_CACHED_STATUS_ELEMS 3 - -typedef struct registered_call { - grpc_mdelem path; - grpc_mdelem authority; - struct registered_call *next; -} registered_call; - -struct grpc_channel { - int is_client; - grpc_compression_options compression_options; - grpc_mdelem default_authority; - - gpr_atm call_size_estimate; - - gpr_mu registered_call_mu; - registered_call *registered_calls; - - char *target; -}; - -#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) -#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) \ - (((grpc_channel *)(channel_stack)) - 1) -#define CHANNEL_FROM_TOP_ELEM(top_elem) \ - CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem)) - -static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error); - -grpc_channel *grpc_channel_create_with_builder( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, - grpc_channel_stack_type channel_stack_type) { - char *target = gpr_strdup(grpc_channel_stack_builder_get_target(builder)); - grpc_channel_args *args = grpc_channel_args_copy( - grpc_channel_stack_builder_get_channel_arguments(builder)); - grpc_channel *channel; - if (channel_stack_type == GRPC_SERVER_CHANNEL) { - GRPC_STATS_INC_SERVER_CHANNELS_CREATED(exec_ctx); - } else { - GRPC_STATS_INC_CLIENT_CHANNELS_CREATED(exec_ctx); - } - grpc_error *error = grpc_channel_stack_builder_finish( - exec_ctx, builder, sizeof(grpc_channel), 1, destroy_channel, NULL, - (void **)&channel); - if (error != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "channel stack builder failed: %s", - grpc_error_string(error)); - GRPC_ERROR_UNREF(error); - gpr_free(target); - goto done; - } - - memset(channel, 0, sizeof(*channel)); - channel->target = target; - channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type); - gpr_mu_init(&channel->registered_call_mu); - channel->registered_calls = NULL; - - gpr_atm_no_barrier_store( - &channel->call_size_estimate, - (gpr_atm)CHANNEL_STACK_FROM_CHANNEL(channel)->call_stack_size); - - grpc_compression_options_init(&channel->compression_options); - for (size_t i = 0; i < args->num_args; i++) { - if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) { - if (args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "%s ignored: it must be a string", - GRPC_ARG_DEFAULT_AUTHORITY); - } else { - if (!GRPC_MDISNULL(channel->default_authority)) { - /* setting this takes precedence over anything else */ - GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority); - } - channel->default_authority = grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_AUTHORITY, - grpc_slice_intern( - grpc_slice_from_static_string(args->args[i].value.string))); - } - } else if (0 == - strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { - if (args->args[i].type != GRPC_ARG_STRING) { - gpr_log(GPR_ERROR, "%s ignored: it must be a string", - GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); - } else { - if (!GRPC_MDISNULL(channel->default_authority)) { - /* other ways of setting this (notably ssl) take precedence */ - gpr_log(GPR_ERROR, - "%s ignored: default host already set some other way", - GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); - } else { - channel->default_authority = grpc_mdelem_from_slices( - exec_ctx, GRPC_MDSTR_AUTHORITY, - grpc_slice_intern( - grpc_slice_from_static_string(args->args[i].value.string))); - } - } - } else if (0 == strcmp(args->args[i].key, - GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) { - channel->compression_options.default_level.is_set = true; - channel->compression_options.default_level.level = - (grpc_compression_level)grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){GRPC_COMPRESS_LEVEL_NONE, - GRPC_COMPRESS_LEVEL_NONE, - GRPC_COMPRESS_LEVEL_COUNT - 1}); - } else if (0 == strcmp(args->args[i].key, - GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) { - channel->compression_options.default_stream_compression_level.is_set = - true; - channel->compression_options.default_stream_compression_level.level = - (grpc_stream_compression_level)grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){GRPC_STREAM_COMPRESS_LEVEL_NONE, - GRPC_STREAM_COMPRESS_LEVEL_NONE, - GRPC_STREAM_COMPRESS_LEVEL_COUNT - 1}); - } else if (0 == strcmp(args->args[i].key, - GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { - channel->compression_options.default_algorithm.is_set = true; - channel->compression_options.default_algorithm.algorithm = - (grpc_compression_algorithm)grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, - GRPC_COMPRESS_ALGORITHMS_COUNT - 1}); - } else if (0 == strcmp(args->args[i].key, - GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { - channel->compression_options.default_stream_compression_algorithm.is_set = - true; - channel->compression_options.default_stream_compression_algorithm - .algorithm = - (grpc_stream_compression_algorithm)grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){ - GRPC_STREAM_COMPRESS_NONE, GRPC_STREAM_COMPRESS_NONE, - GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT - 1}); - } else if (0 == - strcmp(args->args[i].key, - GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) { - channel->compression_options.enabled_algorithms_bitset = - (uint32_t)args->args[i].value.integer | - 0x1; /* always support no compression */ - } else if (0 == - strcmp( - args->args[i].key, - GRPC_STREAM_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) { - channel->compression_options - .enabled_stream_compression_algorithms_bitset = - (uint32_t)args->args[i].value.integer | - 0x1; /* always support no compression */ - } - } - -done: - grpc_channel_args_destroy(exec_ctx, args); - return channel; -} - -grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target, - const grpc_channel_args *input_args, - grpc_channel_stack_type channel_stack_type, - grpc_transport *optional_transport) { - grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create(); - grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder, - input_args); - grpc_channel_stack_builder_set_target(builder, target); - grpc_channel_stack_builder_set_transport(builder, optional_transport); - if (!grpc_channel_init_create_stack(exec_ctx, builder, channel_stack_type)) { - grpc_channel_stack_builder_destroy(exec_ctx, builder); - return NULL; - } - return grpc_channel_create_with_builder(exec_ctx, builder, - channel_stack_type); -} - -size_t grpc_channel_get_call_size_estimate(grpc_channel *channel) { -#define ROUND_UP_SIZE 256 - /* We round up our current estimate to the NEXT value of ROUND_UP_SIZE. - This ensures: - 1. a consistent size allocation when our estimate is drifting slowly - (which is common) - which tends to help most allocators reuse memory - 2. a small amount of allowed growth over the estimate without hitting - the arena size doubling case, reducing overall memory usage */ - return ((size_t)gpr_atm_no_barrier_load(&channel->call_size_estimate) + - 2 * ROUND_UP_SIZE) & - ~(size_t)(ROUND_UP_SIZE - 1); -} - -void grpc_channel_update_call_size_estimate(grpc_channel *channel, - size_t size) { - size_t cur = (size_t)gpr_atm_no_barrier_load(&channel->call_size_estimate); - if (cur < size) { - /* size grew: update estimate */ - gpr_atm_no_barrier_cas(&channel->call_size_estimate, (gpr_atm)cur, - (gpr_atm)size); - /* if we lose: never mind, something else will likely update soon enough */ - } else if (cur == size) { - /* no change: holding pattern */ - } else if (cur > 0) { - /* size shrank: decrease estimate */ - gpr_atm_no_barrier_cas( - &channel->call_size_estimate, (gpr_atm)cur, - (gpr_atm)(GPR_MIN(cur - 1, (255 * cur + size) / 256))); - /* if we lose: never mind, something else will likely update soon enough */ - } -} - -char *grpc_channel_get_target(grpc_channel *channel) { - GRPC_API_TRACE("grpc_channel_get_target(channel=%p)", 1, (channel)); - return gpr_strdup(channel->target); -} - -void grpc_channel_get_info(grpc_channel *channel, - const grpc_channel_info *channel_info) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_channel_element *elem = - grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); - elem->filter->get_channel_info(&exec_ctx, elem, channel_info); - grpc_exec_ctx_finish(&exec_ctx); -} - -static grpc_call *grpc_channel_create_call_internal( - grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call, - uint32_t propagation_mask, grpc_completion_queue *cq, - grpc_pollset_set *pollset_set_alternative, grpc_mdelem path_mdelem, - grpc_mdelem authority_mdelem, gpr_timespec deadline) { - grpc_mdelem send_metadata[2]; - size_t num_metadata = 0; - - GPR_ASSERT(channel->is_client); - GPR_ASSERT(!(cq != NULL && pollset_set_alternative != NULL)); - - send_metadata[num_metadata++] = path_mdelem; - if (!GRPC_MDISNULL(authority_mdelem)) { - send_metadata[num_metadata++] = authority_mdelem; - } else if (!GRPC_MDISNULL(channel->default_authority)) { - send_metadata[num_metadata++] = GRPC_MDELEM_REF(channel->default_authority); - } - - grpc_call_create_args args; - memset(&args, 0, sizeof(args)); - args.channel = channel; - args.parent = parent_call; - args.propagation_mask = propagation_mask; - args.cq = cq; - args.pollset_set_alternative = pollset_set_alternative; - args.server_transport_data = NULL; - args.add_initial_metadata = send_metadata; - args.add_initial_metadata_count = num_metadata; - args.send_deadline = deadline; - - grpc_call *call; - GRPC_LOG_IF_ERROR("call_create", grpc_call_create(exec_ctx, &args, &call)); - return call; -} - -grpc_call *grpc_channel_create_call(grpc_channel *channel, - grpc_call *parent_call, - uint32_t propagation_mask, - grpc_completion_queue *cq, - grpc_slice method, const grpc_slice *host, - gpr_timespec deadline, void *reserved) { - GPR_ASSERT(!reserved); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call *call = grpc_channel_create_call_internal( - &exec_ctx, channel, parent_call, propagation_mask, cq, NULL, - grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_PATH, - grpc_slice_ref_internal(method)), - host != NULL ? grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_AUTHORITY, - grpc_slice_ref_internal(*host)) - : GRPC_MDNULL, - deadline); - grpc_exec_ctx_finish(&exec_ctx); - return call; -} - -grpc_call *grpc_channel_create_pollset_set_call( - grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call, - uint32_t propagation_mask, grpc_pollset_set *pollset_set, grpc_slice method, - const grpc_slice *host, gpr_timespec deadline, void *reserved) { - GPR_ASSERT(!reserved); - return grpc_channel_create_call_internal( - exec_ctx, channel, parent_call, propagation_mask, NULL, pollset_set, - grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_PATH, - grpc_slice_ref_internal(method)), - host != NULL ? grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY, - grpc_slice_ref_internal(*host)) - : GRPC_MDNULL, - deadline); -} - -void *grpc_channel_register_call(grpc_channel *channel, const char *method, - const char *host, void *reserved) { - registered_call *rc = (registered_call *)gpr_malloc(sizeof(registered_call)); - GRPC_API_TRACE( - "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)", - 4, (channel, method, host, reserved)); - GPR_ASSERT(!reserved); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - rc->path = grpc_mdelem_from_slices( - &exec_ctx, GRPC_MDSTR_PATH, - grpc_slice_intern(grpc_slice_from_static_string(method))); - rc->authority = - host ? grpc_mdelem_from_slices( - &exec_ctx, GRPC_MDSTR_AUTHORITY, - grpc_slice_intern(grpc_slice_from_static_string(host))) - : GRPC_MDNULL; - gpr_mu_lock(&channel->registered_call_mu); - rc->next = channel->registered_calls; - channel->registered_calls = rc; - gpr_mu_unlock(&channel->registered_call_mu); - grpc_exec_ctx_finish(&exec_ctx); - return rc; -} - -grpc_call *grpc_channel_create_registered_call( - grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, - grpc_completion_queue *completion_queue, void *registered_call_handle, - gpr_timespec deadline, void *reserved) { - registered_call *rc = (registered_call *)registered_call_handle; - GRPC_API_TRACE( - "grpc_channel_create_registered_call(" - "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " - "registered_call_handle=%p, " - "deadline=gpr_timespec { tv_sec: %" PRId64 - ", tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 9, (channel, parent_call, (unsigned)propagation_mask, completion_queue, - registered_call_handle, deadline.tv_sec, deadline.tv_nsec, - (int)deadline.clock_type, reserved)); - GPR_ASSERT(!reserved); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - grpc_call *call = grpc_channel_create_call_internal( - &exec_ctx, channel, parent_call, propagation_mask, completion_queue, NULL, - GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority), deadline); - grpc_exec_ctx_finish(&exec_ctx); - return call; -} - -#ifndef NDEBUG -#define REF_REASON reason -#define REF_ARG , const char *reason -#else -#define REF_REASON "" -#define REF_ARG -#endif -void grpc_channel_internal_ref(grpc_channel *c REF_ARG) { - GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); -} - -void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, - grpc_channel *c REF_ARG) { - GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); -} - -static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_channel *channel = (grpc_channel *)arg; - grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel)); - while (channel->registered_calls) { - registered_call *rc = channel->registered_calls; - channel->registered_calls = rc->next; - GRPC_MDELEM_UNREF(exec_ctx, rc->path); - GRPC_MDELEM_UNREF(exec_ctx, rc->authority); - gpr_free(rc); - } - GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority); - gpr_mu_destroy(&channel->registered_call_mu); - gpr_free(channel->target); - gpr_free(channel); -} - -void grpc_channel_destroy(grpc_channel *channel) { - grpc_transport_op *op = grpc_make_transport_op(NULL); - grpc_channel_element *elem; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel)); - op->disconnect_with_error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Destroyed"); - elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); - elem->filter->start_transport_op(&exec_ctx, elem, op); - - GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "channel"); - - grpc_exec_ctx_finish(&exec_ctx); -} - -grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { - return CHANNEL_STACK_FROM_CHANNEL(channel); -} - -grpc_compression_options grpc_channel_compression_options( - const grpc_channel *channel) { - return channel->compression_options; -} - -grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx, - grpc_channel *channel, int i) { - char tmp[GPR_LTOA_MIN_BUFSIZE]; - switch (i) { - case 0: - return GRPC_MDELEM_GRPC_STATUS_0; - case 1: - return GRPC_MDELEM_GRPC_STATUS_1; - case 2: - return GRPC_MDELEM_GRPC_STATUS_2; - } - gpr_ltoa(i, tmp); - return grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_STATUS, - grpc_slice_from_copied_string(tmp)); -} diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc new file mode 100644 index 0000000000..48962e5e45 --- /dev/null +++ b/src/core/lib/surface/channel.cc @@ -0,0 +1,454 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/channel.h" + +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/static_metadata.h" + +/** Cache grpc-status: X mdelems for X = 0..NUM_CACHED_STATUS_ELEMS. + * Avoids needing to take a metadata context lock for sending status + * if the status code is <= NUM_CACHED_STATUS_ELEMS. + * Sized to allow the most commonly used codes to fit in + * (OK, Cancelled, Unknown). */ +#define NUM_CACHED_STATUS_ELEMS 3 + +typedef struct registered_call { + grpc_mdelem path; + grpc_mdelem authority; + struct registered_call *next; +} registered_call; + +struct grpc_channel { + int is_client; + grpc_compression_options compression_options; + grpc_mdelem default_authority; + + gpr_atm call_size_estimate; + + gpr_mu registered_call_mu; + registered_call *registered_calls; + + char *target; +}; + +#define CHANNEL_STACK_FROM_CHANNEL(c) ((grpc_channel_stack *)((c) + 1)) +#define CHANNEL_FROM_CHANNEL_STACK(channel_stack) \ + (((grpc_channel *)(channel_stack)) - 1) +#define CHANNEL_FROM_TOP_ELEM(top_elem) \ + CHANNEL_FROM_CHANNEL_STACK(grpc_channel_stack_from_top_element(top_elem)) + +static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error); + +grpc_channel *grpc_channel_create_with_builder( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, + grpc_channel_stack_type channel_stack_type) { + char *target = gpr_strdup(grpc_channel_stack_builder_get_target(builder)); + grpc_channel_args *args = grpc_channel_args_copy( + grpc_channel_stack_builder_get_channel_arguments(builder)); + grpc_channel *channel; + if (channel_stack_type == GRPC_SERVER_CHANNEL) { + GRPC_STATS_INC_SERVER_CHANNELS_CREATED(exec_ctx); + } else { + GRPC_STATS_INC_CLIENT_CHANNELS_CREATED(exec_ctx); + } + grpc_error *error = grpc_channel_stack_builder_finish( + exec_ctx, builder, sizeof(grpc_channel), 1, destroy_channel, NULL, + (void **)&channel); + if (error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "channel stack builder failed: %s", + grpc_error_string(error)); + GRPC_ERROR_UNREF(error); + gpr_free(target); + goto done; + } + + memset(channel, 0, sizeof(*channel)); + channel->target = target; + channel->is_client = grpc_channel_stack_type_is_client(channel_stack_type); + gpr_mu_init(&channel->registered_call_mu); + channel->registered_calls = NULL; + + gpr_atm_no_barrier_store( + &channel->call_size_estimate, + (gpr_atm)CHANNEL_STACK_FROM_CHANNEL(channel)->call_stack_size); + + grpc_compression_options_init(&channel->compression_options); + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(args->args[i].key, GRPC_ARG_DEFAULT_AUTHORITY)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s ignored: it must be a string", + GRPC_ARG_DEFAULT_AUTHORITY); + } else { + if (!GRPC_MDISNULL(channel->default_authority)) { + /* setting this takes precedence over anything else */ + GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority); + } + channel->default_authority = grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_intern( + grpc_slice_from_static_string(args->args[i].value.string))); + } + } else if (0 == + strcmp(args->args[i].key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG)) { + if (args->args[i].type != GRPC_ARG_STRING) { + gpr_log(GPR_ERROR, "%s ignored: it must be a string", + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); + } else { + if (!GRPC_MDISNULL(channel->default_authority)) { + /* other ways of setting this (notably ssl) take precedence */ + gpr_log(GPR_ERROR, + "%s ignored: default host already set some other way", + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG); + } else { + channel->default_authority = grpc_mdelem_from_slices( + exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_intern( + grpc_slice_from_static_string(args->args[i].value.string))); + } + } + } else if (0 == strcmp(args->args[i].key, + GRPC_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) { + channel->compression_options.default_level.is_set = true; + channel->compression_options.default_level.level = + (grpc_compression_level)grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){GRPC_COMPRESS_LEVEL_NONE, + GRPC_COMPRESS_LEVEL_NONE, + GRPC_COMPRESS_LEVEL_COUNT - 1}); + } else if (0 == strcmp(args->args[i].key, + GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) { + channel->compression_options.default_stream_compression_level.is_set = + true; + channel->compression_options.default_stream_compression_level.level = + (grpc_stream_compression_level)grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){GRPC_STREAM_COMPRESS_LEVEL_NONE, + GRPC_STREAM_COMPRESS_LEVEL_NONE, + GRPC_STREAM_COMPRESS_LEVEL_COUNT - 1}); + } else if (0 == strcmp(args->args[i].key, + GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { + channel->compression_options.default_algorithm.is_set = true; + channel->compression_options.default_algorithm.algorithm = + (grpc_compression_algorithm)grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, + GRPC_COMPRESS_ALGORITHMS_COUNT - 1}); + } else if (0 == strcmp(args->args[i].key, + GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { + channel->compression_options.default_stream_compression_algorithm.is_set = + true; + channel->compression_options.default_stream_compression_algorithm + .algorithm = + (grpc_stream_compression_algorithm)grpc_channel_arg_get_integer( + &args->args[i], + (grpc_integer_options){ + GRPC_STREAM_COMPRESS_NONE, GRPC_STREAM_COMPRESS_NONE, + GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT - 1}); + } else if (0 == + strcmp(args->args[i].key, + GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) { + channel->compression_options.enabled_algorithms_bitset = + (uint32_t)args->args[i].value.integer | + 0x1; /* always support no compression */ + } else if (0 == + strcmp( + args->args[i].key, + GRPC_STREAM_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) { + channel->compression_options + .enabled_stream_compression_algorithms_bitset = + (uint32_t)args->args[i].value.integer | + 0x1; /* always support no compression */ + } + } + +done: + grpc_channel_args_destroy(exec_ctx, args); + return channel; +} + +grpc_channel *grpc_channel_create(grpc_exec_ctx *exec_ctx, const char *target, + const grpc_channel_args *input_args, + grpc_channel_stack_type channel_stack_type, + grpc_transport *optional_transport) { + grpc_channel_stack_builder *builder = grpc_channel_stack_builder_create(); + grpc_channel_stack_builder_set_channel_arguments(exec_ctx, builder, + input_args); + grpc_channel_stack_builder_set_target(builder, target); + grpc_channel_stack_builder_set_transport(builder, optional_transport); + if (!grpc_channel_init_create_stack(exec_ctx, builder, channel_stack_type)) { + grpc_channel_stack_builder_destroy(exec_ctx, builder); + return NULL; + } + return grpc_channel_create_with_builder(exec_ctx, builder, + channel_stack_type); +} + +size_t grpc_channel_get_call_size_estimate(grpc_channel *channel) { +#define ROUND_UP_SIZE 256 + /* We round up our current estimate to the NEXT value of ROUND_UP_SIZE. + This ensures: + 1. a consistent size allocation when our estimate is drifting slowly + (which is common) - which tends to help most allocators reuse memory + 2. a small amount of allowed growth over the estimate without hitting + the arena size doubling case, reducing overall memory usage */ + return ((size_t)gpr_atm_no_barrier_load(&channel->call_size_estimate) + + 2 * ROUND_UP_SIZE) & + ~(size_t)(ROUND_UP_SIZE - 1); +} + +void grpc_channel_update_call_size_estimate(grpc_channel *channel, + size_t size) { + size_t cur = (size_t)gpr_atm_no_barrier_load(&channel->call_size_estimate); + if (cur < size) { + /* size grew: update estimate */ + gpr_atm_no_barrier_cas(&channel->call_size_estimate, (gpr_atm)cur, + (gpr_atm)size); + /* if we lose: never mind, something else will likely update soon enough */ + } else if (cur == size) { + /* no change: holding pattern */ + } else if (cur > 0) { + /* size shrank: decrease estimate */ + gpr_atm_no_barrier_cas( + &channel->call_size_estimate, (gpr_atm)cur, + (gpr_atm)(GPR_MIN(cur - 1, (255 * cur + size) / 256))); + /* if we lose: never mind, something else will likely update soon enough */ + } +} + +char *grpc_channel_get_target(grpc_channel *channel) { + GRPC_API_TRACE("grpc_channel_get_target(channel=%p)", 1, (channel)); + return gpr_strdup(channel->target); +} + +void grpc_channel_get_info(grpc_channel *channel, + const grpc_channel_info *channel_info) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_channel_element *elem = + grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); + elem->filter->get_channel_info(&exec_ctx, elem, channel_info); + grpc_exec_ctx_finish(&exec_ctx); +} + +static grpc_call *grpc_channel_create_call_internal( + grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call, + uint32_t propagation_mask, grpc_completion_queue *cq, + grpc_pollset_set *pollset_set_alternative, grpc_mdelem path_mdelem, + grpc_mdelem authority_mdelem, gpr_timespec deadline) { + grpc_mdelem send_metadata[2]; + size_t num_metadata = 0; + + GPR_ASSERT(channel->is_client); + GPR_ASSERT(!(cq != NULL && pollset_set_alternative != NULL)); + + send_metadata[num_metadata++] = path_mdelem; + if (!GRPC_MDISNULL(authority_mdelem)) { + send_metadata[num_metadata++] = authority_mdelem; + } else if (!GRPC_MDISNULL(channel->default_authority)) { + send_metadata[num_metadata++] = GRPC_MDELEM_REF(channel->default_authority); + } + + grpc_call_create_args args; + memset(&args, 0, sizeof(args)); + args.channel = channel; + args.parent = parent_call; + args.propagation_mask = propagation_mask; + args.cq = cq; + args.pollset_set_alternative = pollset_set_alternative; + args.server_transport_data = NULL; + args.add_initial_metadata = send_metadata; + args.add_initial_metadata_count = num_metadata; + args.send_deadline = deadline; + + grpc_call *call; + GRPC_LOG_IF_ERROR("call_create", grpc_call_create(exec_ctx, &args, &call)); + return call; +} + +grpc_call *grpc_channel_create_call(grpc_channel *channel, + grpc_call *parent_call, + uint32_t propagation_mask, + grpc_completion_queue *cq, + grpc_slice method, const grpc_slice *host, + gpr_timespec deadline, void *reserved) { + GPR_ASSERT(!reserved); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call *call = grpc_channel_create_call_internal( + &exec_ctx, channel, parent_call, propagation_mask, cq, NULL, + grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_PATH, + grpc_slice_ref_internal(method)), + host != NULL ? grpc_mdelem_from_slices(&exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_ref_internal(*host)) + : GRPC_MDNULL, + deadline); + grpc_exec_ctx_finish(&exec_ctx); + return call; +} + +grpc_call *grpc_channel_create_pollset_set_call( + grpc_exec_ctx *exec_ctx, grpc_channel *channel, grpc_call *parent_call, + uint32_t propagation_mask, grpc_pollset_set *pollset_set, grpc_slice method, + const grpc_slice *host, gpr_timespec deadline, void *reserved) { + GPR_ASSERT(!reserved); + return grpc_channel_create_call_internal( + exec_ctx, channel, parent_call, propagation_mask, NULL, pollset_set, + grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_PATH, + grpc_slice_ref_internal(method)), + host != NULL ? grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_ref_internal(*host)) + : GRPC_MDNULL, + deadline); +} + +void *grpc_channel_register_call(grpc_channel *channel, const char *method, + const char *host, void *reserved) { + registered_call *rc = (registered_call *)gpr_malloc(sizeof(registered_call)); + GRPC_API_TRACE( + "grpc_channel_register_call(channel=%p, method=%s, host=%s, reserved=%p)", + 4, (channel, method, host, reserved)); + GPR_ASSERT(!reserved); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + rc->path = grpc_mdelem_from_slices( + &exec_ctx, GRPC_MDSTR_PATH, + grpc_slice_intern(grpc_slice_from_static_string(method))); + rc->authority = + host ? grpc_mdelem_from_slices( + &exec_ctx, GRPC_MDSTR_AUTHORITY, + grpc_slice_intern(grpc_slice_from_static_string(host))) + : GRPC_MDNULL; + gpr_mu_lock(&channel->registered_call_mu); + rc->next = channel->registered_calls; + channel->registered_calls = rc; + gpr_mu_unlock(&channel->registered_call_mu); + grpc_exec_ctx_finish(&exec_ctx); + return rc; +} + +grpc_call *grpc_channel_create_registered_call( + grpc_channel *channel, grpc_call *parent_call, uint32_t propagation_mask, + grpc_completion_queue *completion_queue, void *registered_call_handle, + gpr_timespec deadline, void *reserved) { + registered_call *rc = (registered_call *)registered_call_handle; + GRPC_API_TRACE( + "grpc_channel_create_registered_call(" + "channel=%p, parent_call=%p, propagation_mask=%x, completion_queue=%p, " + "registered_call_handle=%p, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 9, (channel, parent_call, (unsigned)propagation_mask, completion_queue, + registered_call_handle, deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, reserved)); + GPR_ASSERT(!reserved); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call *call = grpc_channel_create_call_internal( + &exec_ctx, channel, parent_call, propagation_mask, completion_queue, NULL, + GRPC_MDELEM_REF(rc->path), GRPC_MDELEM_REF(rc->authority), deadline); + grpc_exec_ctx_finish(&exec_ctx); + return call; +} + +#ifndef NDEBUG +#define REF_REASON reason +#define REF_ARG , const char *reason +#else +#define REF_REASON "" +#define REF_ARG +#endif +void grpc_channel_internal_ref(grpc_channel *c REF_ARG) { + GRPC_CHANNEL_STACK_REF(CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); +} + +void grpc_channel_internal_unref(grpc_exec_ctx *exec_ctx, + grpc_channel *c REF_ARG) { + GRPC_CHANNEL_STACK_UNREF(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(c), REF_REASON); +} + +static void destroy_channel(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_channel *channel = (grpc_channel *)arg; + grpc_channel_stack_destroy(exec_ctx, CHANNEL_STACK_FROM_CHANNEL(channel)); + while (channel->registered_calls) { + registered_call *rc = channel->registered_calls; + channel->registered_calls = rc->next; + GRPC_MDELEM_UNREF(exec_ctx, rc->path); + GRPC_MDELEM_UNREF(exec_ctx, rc->authority); + gpr_free(rc); + } + GRPC_MDELEM_UNREF(exec_ctx, channel->default_authority); + gpr_mu_destroy(&channel->registered_call_mu); + gpr_free(channel->target); + gpr_free(channel); +} + +void grpc_channel_destroy(grpc_channel *channel) { + grpc_transport_op *op = grpc_make_transport_op(NULL); + grpc_channel_element *elem; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_API_TRACE("grpc_channel_destroy(channel=%p)", 1, (channel)); + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Channel Destroyed"); + elem = grpc_channel_stack_element(CHANNEL_STACK_FROM_CHANNEL(channel), 0); + elem->filter->start_transport_op(&exec_ctx, elem, op); + + GRPC_CHANNEL_INTERNAL_UNREF(&exec_ctx, channel, "channel"); + + grpc_exec_ctx_finish(&exec_ctx); +} + +grpc_channel_stack *grpc_channel_get_channel_stack(grpc_channel *channel) { + return CHANNEL_STACK_FROM_CHANNEL(channel); +} + +grpc_compression_options grpc_channel_compression_options( + const grpc_channel *channel) { + return channel->compression_options; +} + +grpc_mdelem grpc_channel_get_reffed_status_elem(grpc_exec_ctx *exec_ctx, + grpc_channel *channel, int i) { + char tmp[GPR_LTOA_MIN_BUFSIZE]; + switch (i) { + case 0: + return GRPC_MDELEM_GRPC_STATUS_0; + case 1: + return GRPC_MDELEM_GRPC_STATUS_1; + case 2: + return GRPC_MDELEM_GRPC_STATUS_2; + } + gpr_ltoa(i, tmp); + return grpc_mdelem_from_slices(exec_ctx, GRPC_MDSTR_GRPC_STATUS, + grpc_slice_from_copied_string(tmp)); +} diff --git a/src/core/lib/surface/channel_init.c b/src/core/lib/surface/channel_init.c deleted file mode 100644 index 33f444b89e..0000000000 --- a/src/core/lib/surface/channel_init.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/channel_init.h" - -#include -#include - -typedef struct stage_slot { - grpc_channel_init_stage fn; - void *arg; - int priority; - size_t insertion_order; -} stage_slot; - -typedef struct stage_slots { - stage_slot *slots; - size_t num_slots; - size_t cap_slots; -} stage_slots; - -static stage_slots g_slots[GRPC_NUM_CHANNEL_STACK_TYPES]; -static bool g_finalized; - -void grpc_channel_init_init(void) { - for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { - g_slots[i].slots = NULL; - g_slots[i].num_slots = 0; - g_slots[i].cap_slots = 0; - } - g_finalized = false; -} - -void grpc_channel_init_register_stage(grpc_channel_stack_type type, - int priority, - grpc_channel_init_stage stage, - void *stage_arg) { - GPR_ASSERT(!g_finalized); - if (g_slots[type].cap_slots == g_slots[type].num_slots) { - g_slots[type].cap_slots = GPR_MAX(8, 3 * g_slots[type].cap_slots / 2); - g_slots[type].slots = (stage_slot *)gpr_realloc( - g_slots[type].slots, - g_slots[type].cap_slots * sizeof(*g_slots[type].slots)); - } - stage_slot *s = &g_slots[type].slots[g_slots[type].num_slots++]; - s->insertion_order = g_slots[type].num_slots; - s->priority = priority; - s->fn = stage; - s->arg = stage_arg; -} - -static int compare_slots(const void *a, const void *b) { - const stage_slot *sa = (const stage_slot *)a; - const stage_slot *sb = (const stage_slot *)b; - - int c = GPR_ICMP(sa->priority, sb->priority); - if (c != 0) return c; - return GPR_ICMP(sa->insertion_order, sb->insertion_order); -} - -void grpc_channel_init_finalize(void) { - GPR_ASSERT(!g_finalized); - for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { - qsort(g_slots[i].slots, g_slots[i].num_slots, sizeof(*g_slots[i].slots), - compare_slots); - } - g_finalized = true; -} - -void grpc_channel_init_shutdown(void) { - for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { - gpr_free(g_slots[i].slots); - g_slots[i].slots = (stage_slot *)(void *)(uintptr_t)0xdeadbeef; - } -} - -bool grpc_channel_init_create_stack(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, - grpc_channel_stack_type type) { - GPR_ASSERT(g_finalized); - - grpc_channel_stack_builder_set_name(builder, - grpc_channel_stack_type_string(type)); - - for (size_t i = 0; i < g_slots[type].num_slots; i++) { - const stage_slot *slot = &g_slots[type].slots[i]; - if (!slot->fn(exec_ctx, builder, slot->arg)) { - return false; - } - } - - return true; -} diff --git a/src/core/lib/surface/channel_init.cc b/src/core/lib/surface/channel_init.cc new file mode 100644 index 0000000000..33f444b89e --- /dev/null +++ b/src/core/lib/surface/channel_init.cc @@ -0,0 +1,108 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/channel_init.h" + +#include +#include + +typedef struct stage_slot { + grpc_channel_init_stage fn; + void *arg; + int priority; + size_t insertion_order; +} stage_slot; + +typedef struct stage_slots { + stage_slot *slots; + size_t num_slots; + size_t cap_slots; +} stage_slots; + +static stage_slots g_slots[GRPC_NUM_CHANNEL_STACK_TYPES]; +static bool g_finalized; + +void grpc_channel_init_init(void) { + for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { + g_slots[i].slots = NULL; + g_slots[i].num_slots = 0; + g_slots[i].cap_slots = 0; + } + g_finalized = false; +} + +void grpc_channel_init_register_stage(grpc_channel_stack_type type, + int priority, + grpc_channel_init_stage stage, + void *stage_arg) { + GPR_ASSERT(!g_finalized); + if (g_slots[type].cap_slots == g_slots[type].num_slots) { + g_slots[type].cap_slots = GPR_MAX(8, 3 * g_slots[type].cap_slots / 2); + g_slots[type].slots = (stage_slot *)gpr_realloc( + g_slots[type].slots, + g_slots[type].cap_slots * sizeof(*g_slots[type].slots)); + } + stage_slot *s = &g_slots[type].slots[g_slots[type].num_slots++]; + s->insertion_order = g_slots[type].num_slots; + s->priority = priority; + s->fn = stage; + s->arg = stage_arg; +} + +static int compare_slots(const void *a, const void *b) { + const stage_slot *sa = (const stage_slot *)a; + const stage_slot *sb = (const stage_slot *)b; + + int c = GPR_ICMP(sa->priority, sb->priority); + if (c != 0) return c; + return GPR_ICMP(sa->insertion_order, sb->insertion_order); +} + +void grpc_channel_init_finalize(void) { + GPR_ASSERT(!g_finalized); + for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { + qsort(g_slots[i].slots, g_slots[i].num_slots, sizeof(*g_slots[i].slots), + compare_slots); + } + g_finalized = true; +} + +void grpc_channel_init_shutdown(void) { + for (int i = 0; i < GRPC_NUM_CHANNEL_STACK_TYPES; i++) { + gpr_free(g_slots[i].slots); + g_slots[i].slots = (stage_slot *)(void *)(uintptr_t)0xdeadbeef; + } +} + +bool grpc_channel_init_create_stack(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, + grpc_channel_stack_type type) { + GPR_ASSERT(g_finalized); + + grpc_channel_stack_builder_set_name(builder, + grpc_channel_stack_type_string(type)); + + for (size_t i = 0; i < g_slots[type].num_slots; i++) { + const stage_slot *slot = &g_slots[type].slots[i]; + if (!slot->fn(exec_ctx, builder, slot->arg)) { + return false; + } + } + + return true; +} diff --git a/src/core/lib/surface/channel_ping.c b/src/core/lib/surface/channel_ping.c deleted file mode 100644 index f45b568958..0000000000 --- a/src/core/lib/surface/channel_ping.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/channel.h" - -#include - -#include -#include - -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/completion_queue.h" - -typedef struct { - grpc_closure closure; - void *tag; - grpc_completion_queue *cq; - grpc_cq_completion completion_storage; -} ping_result; - -static void ping_destroy(grpc_exec_ctx *exec_ctx, void *arg, - grpc_cq_completion *storage) { - gpr_free(arg); -} - -static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - ping_result *pr = (ping_result *)arg; - grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, GRPC_ERROR_REF(error), ping_destroy, - pr, &pr->completion_storage); -} - -void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq, - void *tag, void *reserved) { - GRPC_API_TRACE("grpc_channel_ping(channel=%p, cq=%p, tag=%p, reserved=%p)", 4, - (channel, cq, tag, reserved)); - grpc_transport_op *op = grpc_make_transport_op(NULL); - ping_result *pr = (ping_result *)gpr_malloc(sizeof(*pr)); - grpc_channel_element *top_elem = - grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GPR_ASSERT(reserved == NULL); - pr->tag = tag; - pr->cq = cq; - GRPC_CLOSURE_INIT(&pr->closure, ping_done, pr, grpc_schedule_on_exec_ctx); - op->send_ping = &pr->closure; - op->bind_pollset = grpc_cq_pollset(cq); - GPR_ASSERT(grpc_cq_begin_op(cq, tag)); - top_elem->filter->start_transport_op(&exec_ctx, top_elem, op); - grpc_exec_ctx_finish(&exec_ctx); -} diff --git a/src/core/lib/surface/channel_ping.cc b/src/core/lib/surface/channel_ping.cc new file mode 100644 index 0000000000..f45b568958 --- /dev/null +++ b/src/core/lib/surface/channel_ping.cc @@ -0,0 +1,65 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/channel.h" + +#include + +#include +#include + +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/completion_queue.h" + +typedef struct { + grpc_closure closure; + void *tag; + grpc_completion_queue *cq; + grpc_cq_completion completion_storage; +} ping_result; + +static void ping_destroy(grpc_exec_ctx *exec_ctx, void *arg, + grpc_cq_completion *storage) { + gpr_free(arg); +} + +static void ping_done(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + ping_result *pr = (ping_result *)arg; + grpc_cq_end_op(exec_ctx, pr->cq, pr->tag, GRPC_ERROR_REF(error), ping_destroy, + pr, &pr->completion_storage); +} + +void grpc_channel_ping(grpc_channel *channel, grpc_completion_queue *cq, + void *tag, void *reserved) { + GRPC_API_TRACE("grpc_channel_ping(channel=%p, cq=%p, tag=%p, reserved=%p)", 4, + (channel, cq, tag, reserved)); + grpc_transport_op *op = grpc_make_transport_op(NULL); + ping_result *pr = (ping_result *)gpr_malloc(sizeof(*pr)); + grpc_channel_element *top_elem = + grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GPR_ASSERT(reserved == NULL); + pr->tag = tag; + pr->cq = cq; + GRPC_CLOSURE_INIT(&pr->closure, ping_done, pr, grpc_schedule_on_exec_ctx); + op->send_ping = &pr->closure; + op->bind_pollset = grpc_cq_pollset(cq); + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + top_elem->filter->start_transport_op(&exec_ctx, top_elem, op); + grpc_exec_ctx_finish(&exec_ctx); +} diff --git a/src/core/lib/surface/channel_stack_type.c b/src/core/lib/surface/channel_stack_type.c deleted file mode 100644 index 5f5c877727..0000000000 --- a/src/core/lib/surface/channel_stack_type.c +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/channel_stack_type.h" -#include -#include - -bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type) { - switch (type) { - case GRPC_CLIENT_CHANNEL: - return true; - case GRPC_CLIENT_SUBCHANNEL: - return true; - case GRPC_CLIENT_LAME_CHANNEL: - return true; - case GRPC_CLIENT_DIRECT_CHANNEL: - return true; - case GRPC_SERVER_CHANNEL: - return false; - case GRPC_NUM_CHANNEL_STACK_TYPES: - break; - } - GPR_UNREACHABLE_CODE(return true;); -} - -const char *grpc_channel_stack_type_string(grpc_channel_stack_type type) { - switch (type) { - case GRPC_CLIENT_CHANNEL: - return "CLIENT_CHANNEL"; - case GRPC_CLIENT_SUBCHANNEL: - return "CLIENT_SUBCHANNEL"; - case GRPC_SERVER_CHANNEL: - return "SERVER_CHANNEL"; - case GRPC_CLIENT_LAME_CHANNEL: - return "CLIENT_LAME_CHANNEL"; - case GRPC_CLIENT_DIRECT_CHANNEL: - return "CLIENT_DIRECT_CHANNEL"; - case GRPC_NUM_CHANNEL_STACK_TYPES: - break; - } - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} diff --git a/src/core/lib/surface/channel_stack_type.cc b/src/core/lib/surface/channel_stack_type.cc new file mode 100644 index 0000000000..5f5c877727 --- /dev/null +++ b/src/core/lib/surface/channel_stack_type.cc @@ -0,0 +1,57 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/channel_stack_type.h" +#include +#include + +bool grpc_channel_stack_type_is_client(grpc_channel_stack_type type) { + switch (type) { + case GRPC_CLIENT_CHANNEL: + return true; + case GRPC_CLIENT_SUBCHANNEL: + return true; + case GRPC_CLIENT_LAME_CHANNEL: + return true; + case GRPC_CLIENT_DIRECT_CHANNEL: + return true; + case GRPC_SERVER_CHANNEL: + return false; + case GRPC_NUM_CHANNEL_STACK_TYPES: + break; + } + GPR_UNREACHABLE_CODE(return true;); +} + +const char *grpc_channel_stack_type_string(grpc_channel_stack_type type) { + switch (type) { + case GRPC_CLIENT_CHANNEL: + return "CLIENT_CHANNEL"; + case GRPC_CLIENT_SUBCHANNEL: + return "CLIENT_SUBCHANNEL"; + case GRPC_SERVER_CHANNEL: + return "SERVER_CHANNEL"; + case GRPC_CLIENT_LAME_CHANNEL: + return "CLIENT_LAME_CHANNEL"; + case GRPC_CLIENT_DIRECT_CHANNEL: + return "CLIENT_DIRECT_CHANNEL"; + case GRPC_NUM_CHANNEL_STACK_TYPES: + break; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} diff --git a/src/core/lib/surface/completion_queue.c b/src/core/lib/surface/completion_queue.c deleted file mode 100644 index fed66e3a20..0000000000 --- a/src/core/lib/surface/completion_queue.c +++ /dev/null @@ -1,1249 +0,0 @@ -/* - * - * Copyright 2015-2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -#include "src/core/lib/surface/completion_queue.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/pollset.h" -#include "src/core/lib/iomgr/timer.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/support/spinlock.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/event_string.h" - -grpc_tracer_flag grpc_trace_operation_failures = - GRPC_TRACER_INITIALIZER(false, "op_failure"); -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_pending_tags = - GRPC_TRACER_INITIALIZER(false, "pending_tags"); -grpc_tracer_flag grpc_trace_cq_refcount = - GRPC_TRACER_INITIALIZER(false, "cq_refcount"); -#endif - -typedef struct { - grpc_pollset_worker **worker; - void *tag; -} plucker; - -typedef struct { - bool can_get_pollset; - bool can_listen; - size_t (*size)(void); - void (*init)(grpc_pollset *pollset, gpr_mu **mu); - grpc_error *(*kick)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *specific_worker); - grpc_error *(*work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker **worker, gpr_timespec now, - gpr_timespec deadline); - void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_closure *closure); - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset); -} cq_poller_vtable; - -typedef struct non_polling_worker { - gpr_cv cv; - bool kicked; - struct non_polling_worker *next; - struct non_polling_worker *prev; -} non_polling_worker; - -typedef struct { - gpr_mu mu; - non_polling_worker *root; - grpc_closure *shutdown; -} non_polling_poller; - -static size_t non_polling_poller_size(void) { - return sizeof(non_polling_poller); -} - -static void non_polling_poller_init(grpc_pollset *pollset, gpr_mu **mu) { - non_polling_poller *npp = (non_polling_poller *)pollset; - gpr_mu_init(&npp->mu); - *mu = &npp->mu; -} - -static void non_polling_poller_destroy(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset) { - non_polling_poller *npp = (non_polling_poller *)pollset; - gpr_mu_destroy(&npp->mu); -} - -static grpc_error *non_polling_poller_work(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_pollset_worker **worker, - gpr_timespec now, - gpr_timespec deadline) { - non_polling_poller *npp = (non_polling_poller *)pollset; - if (npp->shutdown) return GRPC_ERROR_NONE; - non_polling_worker w; - gpr_cv_init(&w.cv); - if (worker != NULL) *worker = (grpc_pollset_worker *)&w; - if (npp->root == NULL) { - npp->root = w.next = w.prev = &w; - } else { - w.next = npp->root; - w.prev = w.next->prev; - w.next->prev = w.prev->next = &w; - } - w.kicked = false; - while (!npp->shutdown && !w.kicked && !gpr_cv_wait(&w.cv, &npp->mu, deadline)) - ; - if (&w == npp->root) { - npp->root = w.next; - if (&w == npp->root) { - if (npp->shutdown) { - GRPC_CLOSURE_SCHED(exec_ctx, npp->shutdown, GRPC_ERROR_NONE); - } - npp->root = NULL; - } - } - w.next->prev = w.prev; - w.prev->next = w.next; - gpr_cv_destroy(&w.cv); - if (worker != NULL) *worker = NULL; - return GRPC_ERROR_NONE; -} - -static grpc_error *non_polling_poller_kick( - grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, - grpc_pollset_worker *specific_worker) { - non_polling_poller *p = (non_polling_poller *)pollset; - if (specific_worker == NULL) specific_worker = (grpc_pollset_worker *)p->root; - if (specific_worker != NULL) { - non_polling_worker *w = (non_polling_worker *)specific_worker; - if (!w->kicked) { - w->kicked = true; - gpr_cv_signal(&w->cv); - } - } - return GRPC_ERROR_NONE; -} - -static void non_polling_poller_shutdown(grpc_exec_ctx *exec_ctx, - grpc_pollset *pollset, - grpc_closure *closure) { - non_polling_poller *p = (non_polling_poller *)pollset; - GPR_ASSERT(closure != NULL); - p->shutdown = closure; - if (p->root == NULL) { - GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); - } else { - non_polling_worker *w = p->root; - do { - gpr_cv_signal(&w->cv); - w = w->next; - } while (w != p->root); - } -} - -static const cq_poller_vtable g_poller_vtable_by_poller_type[] = { - /* GRPC_CQ_DEFAULT_POLLING */ - {.can_get_pollset = true, - .can_listen = true, - .size = grpc_pollset_size, - .init = grpc_pollset_init, - .kick = grpc_pollset_kick, - .work = grpc_pollset_work, - .shutdown = grpc_pollset_shutdown, - .destroy = grpc_pollset_destroy}, - /* GRPC_CQ_NON_LISTENING */ - {.can_get_pollset = true, - .can_listen = false, - .size = grpc_pollset_size, - .init = grpc_pollset_init, - .kick = grpc_pollset_kick, - .work = grpc_pollset_work, - .shutdown = grpc_pollset_shutdown, - .destroy = grpc_pollset_destroy}, - /* GRPC_CQ_NON_POLLING */ - {.can_get_pollset = false, - .can_listen = false, - .size = non_polling_poller_size, - .init = non_polling_poller_init, - .kick = non_polling_poller_kick, - .work = non_polling_poller_work, - .shutdown = non_polling_poller_shutdown, - .destroy = non_polling_poller_destroy}, -}; - -typedef struct cq_vtable { - grpc_cq_completion_type cq_completion_type; - size_t data_size; - void (*init)(void *data); - void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq); - void (*destroy)(void *data); - bool (*begin_op)(grpc_completion_queue *cq, void *tag); - void (*end_op)(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq, void *tag, - grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage); - grpc_event (*next)(grpc_completion_queue *cq, gpr_timespec deadline, - void *reserved); - grpc_event (*pluck)(grpc_completion_queue *cq, void *tag, - gpr_timespec deadline, void *reserved); -} cq_vtable; - -/* Queue that holds the cq_completion_events. Internally uses gpr_mpscq queue - * (a lockfree multiproducer single consumer queue). It uses a queue_lock - * to support multiple consumers. - * Only used in completion queues whose completion_type is GRPC_CQ_NEXT */ -typedef struct grpc_cq_event_queue { - /* Spinlock to serialize consumers i.e pop() operations */ - gpr_spinlock queue_lock; - - gpr_mpscq queue; - - /* A lazy counter of number of items in the queue. This is NOT atomically - incremented/decremented along with push/pop operations and hence is only - eventually consistent */ - gpr_atm num_queue_items; -} grpc_cq_event_queue; - -typedef struct cq_next_data { - /** Completed events for completion-queues of type GRPC_CQ_NEXT */ - grpc_cq_event_queue queue; - - /** Counter of how many things have ever been queued on this completion queue - useful for avoiding locks to check the queue */ - gpr_atm things_queued_ever; - - /* Number of outstanding events (+1 if not shut down) */ - gpr_atm pending_events; - - /** 0 initially. 1 once we initiated shutdown */ - bool shutdown_called; -} cq_next_data; - -typedef struct cq_pluck_data { - /** Completed events for completion-queues of type GRPC_CQ_PLUCK */ - grpc_cq_completion completed_head; - grpc_cq_completion *completed_tail; - - /** Number of pending events (+1 if we're not shutdown) */ - gpr_atm pending_events; - - /** Counter of how many things have ever been queued on this completion queue - useful for avoiding locks to check the queue */ - gpr_atm things_queued_ever; - - /** 0 initially. 1 once we completed shutting */ - /* TODO: (sreek) This is not needed since (shutdown == 1) if and only if - * (pending_events == 0). So consider removing this in future and use - * pending_events */ - gpr_atm shutdown; - - /** 0 initially. 1 once we initiated shutdown */ - bool shutdown_called; - - int num_pluckers; - plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; -} cq_pluck_data; - -/* Completion queue structure */ -struct grpc_completion_queue { - /** Once owning_refs drops to zero, we will destroy the cq */ - gpr_refcount owning_refs; - - gpr_mu *mu; - - const cq_vtable *vtable; - const cq_poller_vtable *poller_vtable; - -#ifndef NDEBUG - void **outstanding_tags; - size_t outstanding_tag_count; - size_t outstanding_tag_capacity; -#endif - - grpc_closure pollset_shutdown_done; - int num_polls; -}; - -/* Forward declarations */ -static void cq_finish_shutdown_next(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq); -static void cq_finish_shutdown_pluck(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq); -static void cq_shutdown_next(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq); -static void cq_shutdown_pluck(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq); - -static bool cq_begin_op_for_next(grpc_completion_queue *cq, void *tag); -static bool cq_begin_op_for_pluck(grpc_completion_queue *cq, void *tag); - -static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq, void *tag, - grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, - void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage); - -static void cq_end_op_for_pluck(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq, void *tag, - grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, - void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage); - -static grpc_event cq_next(grpc_completion_queue *cq, gpr_timespec deadline, - void *reserved); - -static grpc_event cq_pluck(grpc_completion_queue *cq, void *tag, - gpr_timespec deadline, void *reserved); - -static void cq_init_next(void *data); -static void cq_init_pluck(void *data); -static void cq_destroy_next(void *data); -static void cq_destroy_pluck(void *data); - -/* Completion queue vtables based on the completion-type */ -static const cq_vtable g_cq_vtable[] = { - /* GRPC_CQ_NEXT */ - {GRPC_CQ_NEXT, sizeof(cq_next_data), cq_init_next, cq_shutdown_next, - cq_destroy_next, cq_begin_op_for_next, cq_end_op_for_next, cq_next, NULL}, - /* GRPC_CQ_PLUCK */ - {GRPC_CQ_PLUCK, sizeof(cq_pluck_data), cq_init_pluck, cq_shutdown_pluck, - cq_destroy_pluck, cq_begin_op_for_pluck, cq_end_op_for_pluck, NULL, - cq_pluck}, -}; - -#define DATA_FROM_CQ(cq) ((void *)(cq + 1)) -#define POLLSET_FROM_CQ(cq) \ - ((grpc_pollset *)(cq->vtable->data_size + (char *)DATA_FROM_CQ(cq))) - -grpc_tracer_flag grpc_cq_pluck_trace = - GRPC_TRACER_INITIALIZER(true, "queue_pluck"); -grpc_tracer_flag grpc_cq_event_timeout_trace = - GRPC_TRACER_INITIALIZER(true, "queue_timeout"); - -#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \ - if (GRPC_TRACER_ON(grpc_api_trace) && \ - (GRPC_TRACER_ON(grpc_cq_pluck_trace) || \ - (event)->type != GRPC_QUEUE_TIMEOUT)) { \ - char *_ev = grpc_event_string(event); \ - gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \ - gpr_free(_ev); \ - } - -static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cq, - grpc_error *error); - -static void cq_event_queue_init(grpc_cq_event_queue *q) { - gpr_mpscq_init(&q->queue); - q->queue_lock = GPR_SPINLOCK_INITIALIZER; - gpr_atm_no_barrier_store(&q->num_queue_items, 0); -} - -static void cq_event_queue_destroy(grpc_cq_event_queue *q) { - gpr_mpscq_destroy(&q->queue); -} - -static bool cq_event_queue_push(grpc_cq_event_queue *q, grpc_cq_completion *c) { - gpr_mpscq_push(&q->queue, (gpr_mpscq_node *)c); - return gpr_atm_no_barrier_fetch_add(&q->num_queue_items, 1) == 0; -} - -static grpc_cq_completion *cq_event_queue_pop(grpc_cq_event_queue *q) { - grpc_cq_completion *c = NULL; - if (gpr_spinlock_trylock(&q->queue_lock)) { - c = (grpc_cq_completion *)gpr_mpscq_pop(&q->queue); - gpr_spinlock_unlock(&q->queue_lock); - } - - if (c) { - gpr_atm_no_barrier_fetch_add(&q->num_queue_items, -1); - } - - return c; -} - -/* Note: The counter is not incremented/decremented atomically with push/pop. - * The count is only eventually consistent */ -static long cq_event_queue_num_items(grpc_cq_event_queue *q) { - return (long)gpr_atm_no_barrier_load(&q->num_queue_items); -} - -grpc_completion_queue *grpc_completion_queue_create_internal( - grpc_cq_completion_type completion_type, - grpc_cq_polling_type polling_type) { - grpc_completion_queue *cq; - - GPR_TIMER_BEGIN("grpc_completion_queue_create_internal", 0); - - GRPC_API_TRACE( - "grpc_completion_queue_create_internal(completion_type=%d, " - "polling_type=%d)", - 2, (completion_type, polling_type)); - - const cq_vtable *vtable = &g_cq_vtable[completion_type]; - const cq_poller_vtable *poller_vtable = - &g_poller_vtable_by_poller_type[polling_type]; - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_STATS_INC_CQS_CREATED(&exec_ctx); - grpc_exec_ctx_finish(&exec_ctx); - - cq = (grpc_completion_queue *)gpr_zalloc(sizeof(grpc_completion_queue) + - vtable->data_size + - poller_vtable->size()); - - cq->vtable = vtable; - cq->poller_vtable = poller_vtable; - - /* One for destroy(), one for pollset_shutdown */ - gpr_ref_init(&cq->owning_refs, 2); - - poller_vtable->init(POLLSET_FROM_CQ(cq), &cq->mu); - vtable->init(DATA_FROM_CQ(cq)); - - GRPC_CLOSURE_INIT(&cq->pollset_shutdown_done, on_pollset_shutdown_done, cq, - grpc_schedule_on_exec_ctx); - - GPR_TIMER_END("grpc_completion_queue_create_internal", 0); - - return cq; -} - -static void cq_init_next(void *ptr) { - cq_next_data *cqd = (cq_next_data *)ptr; - /* Initial count is dropped by grpc_completion_queue_shutdown */ - gpr_atm_no_barrier_store(&cqd->pending_events, 1); - cqd->shutdown_called = false; - gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); - cq_event_queue_init(&cqd->queue); -} - -static void cq_destroy_next(void *ptr) { - cq_next_data *cqd = (cq_next_data *)ptr; - GPR_ASSERT(cq_event_queue_num_items(&cqd->queue) == 0); - cq_event_queue_destroy(&cqd->queue); -} - -static void cq_init_pluck(void *ptr) { - cq_pluck_data *cqd = (cq_pluck_data *)ptr; - /* Initial count is dropped by grpc_completion_queue_shutdown */ - gpr_atm_no_barrier_store(&cqd->pending_events, 1); - cqd->completed_tail = &cqd->completed_head; - cqd->completed_head.next = (uintptr_t)cqd->completed_tail; - gpr_atm_no_barrier_store(&cqd->shutdown, 0); - cqd->shutdown_called = false; - cqd->num_pluckers = 0; - gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); -} - -static void cq_destroy_pluck(void *ptr) { - cq_pluck_data *cqd = (cq_pluck_data *)ptr; - GPR_ASSERT(cqd->completed_head.next == (uintptr_t)&cqd->completed_head); -} - -grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue *cq) { - return cq->vtable->cq_completion_type; -} - -int grpc_get_cq_poll_num(grpc_completion_queue *cq) { - int cur_num_polls; - gpr_mu_lock(cq->mu); - cur_num_polls = cq->num_polls; - gpr_mu_unlock(cq->mu); - return cur_num_polls; -} - -#ifndef NDEBUG -void grpc_cq_internal_ref(grpc_completion_queue *cq, const char *reason, - const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "CQ:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1, - reason); - } -#else -void grpc_cq_internal_ref(grpc_completion_queue *cq) { -#endif - gpr_ref(&cq->owning_refs); -} - -static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_completion_queue *cq = (grpc_completion_queue *)arg; - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "pollset_destroy"); -} - -#ifndef NDEBUG -void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq, - const char *reason, const char *file, int line) { - if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1, - reason); - } -#else -void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq) { -#endif - if (gpr_unref(&cq->owning_refs)) { - cq->vtable->destroy(DATA_FROM_CQ(cq)); - cq->poller_vtable->destroy(exec_ctx, POLLSET_FROM_CQ(cq)); -#ifndef NDEBUG - gpr_free(cq->outstanding_tags); -#endif - gpr_free(cq); - } -} - -#ifndef NDEBUG -static void cq_check_tag(grpc_completion_queue *cq, void *tag, bool lock_cq) { - int found = 0; - if (lock_cq) { - gpr_mu_lock(cq->mu); - } - - for (int i = 0; i < (int)cq->outstanding_tag_count; i++) { - if (cq->outstanding_tags[i] == tag) { - cq->outstanding_tag_count--; - GPR_SWAP(void *, cq->outstanding_tags[i], - cq->outstanding_tags[cq->outstanding_tag_count]); - found = 1; - break; - } - } - - if (lock_cq) { - gpr_mu_unlock(cq->mu); - } - - GPR_ASSERT(found); -} -#else -static void cq_check_tag(grpc_completion_queue *cq, void *tag, bool lock_cq) {} -#endif - -/* Atomically increments a counter only if the counter is not zero. Returns - * true if the increment was successful; false if the counter is zero */ -static bool atm_inc_if_nonzero(gpr_atm *counter) { - while (true) { - gpr_atm count = gpr_atm_acq_load(counter); - /* If zero, we are done. If not, we must to a CAS (instead of an atomic - * increment) to maintain the contract: do not increment the counter if it - * is zero. */ - if (count == 0) { - return false; - } else if (gpr_atm_full_cas(counter, count, count + 1)) { - break; - } - } - - return true; -} - -static bool cq_begin_op_for_next(grpc_completion_queue *cq, void *tag) { - cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); - return atm_inc_if_nonzero(&cqd->pending_events); -} - -static bool cq_begin_op_for_pluck(grpc_completion_queue *cq, void *tag) { - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - return atm_inc_if_nonzero(&cqd->pending_events); -} - -bool grpc_cq_begin_op(grpc_completion_queue *cq, void *tag) { -#ifndef NDEBUG - gpr_mu_lock(cq->mu); - if (cq->outstanding_tag_count == cq->outstanding_tag_capacity) { - cq->outstanding_tag_capacity = GPR_MAX(4, 2 * cq->outstanding_tag_capacity); - cq->outstanding_tags = (void **)gpr_realloc( - cq->outstanding_tags, - sizeof(*cq->outstanding_tags) * cq->outstanding_tag_capacity); - } - cq->outstanding_tags[cq->outstanding_tag_count++] = tag; - gpr_mu_unlock(cq->mu); -#endif - return cq->vtable->begin_op(cq, tag); -} - -/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a - * completion - * type of GRPC_CQ_NEXT) */ -static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq, void *tag, - grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, - void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage) { - GPR_TIMER_BEGIN("cq_end_op_for_next", 0); - - if (GRPC_TRACER_ON(grpc_api_trace) || - (GRPC_TRACER_ON(grpc_trace_operation_failures) && - error != GRPC_ERROR_NONE)) { - const char *errmsg = grpc_error_string(error); - GRPC_API_TRACE( - "cq_end_op_for_next(exec_ctx=%p, cq=%p, tag=%p, error=%s, " - "done=%p, done_arg=%p, storage=%p)", - 7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage)); - if (GRPC_TRACER_ON(grpc_trace_operation_failures) && - error != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); - } - } - - cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); - int is_success = (error == GRPC_ERROR_NONE); - - storage->tag = tag; - storage->done = done; - storage->done_arg = done_arg; - storage->next = (uintptr_t)(is_success); - - cq_check_tag(cq, tag, true); /* Used in debug builds only */ - - /* Add the completion to the queue */ - bool is_first = cq_event_queue_push(&cqd->queue, storage); - gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); - - /* Since we do not hold the cq lock here, it is important to do an 'acquire' - load here (instead of a 'no_barrier' load) to match with the release store - (done via gpr_atm_full_fetch_add(pending_events, -1)) in cq_shutdown_next - */ - bool will_definitely_shutdown = gpr_atm_acq_load(&cqd->pending_events) == 1; - - if (!will_definitely_shutdown) { - /* Only kick if this is the first item queued */ - if (is_first) { - gpr_mu_lock(cq->mu); - grpc_error *kick_error = - cq->poller_vtable->kick(exec_ctx, POLLSET_FROM_CQ(cq), NULL); - gpr_mu_unlock(cq->mu); - - if (kick_error != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(kick_error); - gpr_log(GPR_ERROR, "Kick failed: %s", msg); - GRPC_ERROR_UNREF(kick_error); - } - } - if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { - GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); - gpr_mu_lock(cq->mu); - cq_finish_shutdown_next(exec_ctx, cq); - gpr_mu_unlock(cq->mu); - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); - } - } else { - GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); - gpr_atm_rel_store(&cqd->pending_events, 0); - gpr_mu_lock(cq->mu); - cq_finish_shutdown_next(exec_ctx, cq); - gpr_mu_unlock(cq->mu); - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); - } - - GPR_TIMER_END("cq_end_op_for_next", 0); - - GRPC_ERROR_UNREF(error); -} - -/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a - * completion - * type of GRPC_CQ_PLUCK) */ -static void cq_end_op_for_pluck(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq, void *tag, - grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, - void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage) { - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - int is_success = (error == GRPC_ERROR_NONE); - - GPR_TIMER_BEGIN("cq_end_op_for_pluck", 0); - - if (GRPC_TRACER_ON(grpc_api_trace) || - (GRPC_TRACER_ON(grpc_trace_operation_failures) && - error != GRPC_ERROR_NONE)) { - const char *errmsg = grpc_error_string(error); - GRPC_API_TRACE( - "cq_end_op_for_pluck(exec_ctx=%p, cq=%p, tag=%p, error=%s, " - "done=%p, done_arg=%p, storage=%p)", - 7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage)); - if (GRPC_TRACER_ON(grpc_trace_operation_failures) && - error != GRPC_ERROR_NONE) { - gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); - } - } - - storage->tag = tag; - storage->done = done; - storage->done_arg = done_arg; - storage->next = ((uintptr_t)&cqd->completed_head) | ((uintptr_t)(is_success)); - - gpr_mu_lock(cq->mu); - cq_check_tag(cq, tag, false); /* Used in debug builds only */ - - /* Add to the list of completions */ - gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); - cqd->completed_tail->next = - ((uintptr_t)storage) | (1u & (uintptr_t)cqd->completed_tail->next); - cqd->completed_tail = storage; - - if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { - cq_finish_shutdown_pluck(exec_ctx, cq); - gpr_mu_unlock(cq->mu); - } else { - grpc_pollset_worker *pluck_worker = NULL; - for (int i = 0; i < cqd->num_pluckers; i++) { - if (cqd->pluckers[i].tag == tag) { - pluck_worker = *cqd->pluckers[i].worker; - break; - } - } - - grpc_error *kick_error = - cq->poller_vtable->kick(exec_ctx, POLLSET_FROM_CQ(cq), pluck_worker); - - gpr_mu_unlock(cq->mu); - - if (kick_error != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(kick_error); - gpr_log(GPR_ERROR, "Kick failed: %s", msg); - - GRPC_ERROR_UNREF(kick_error); - } - } - - GPR_TIMER_END("cq_end_op_for_pluck", 0); - - GRPC_ERROR_UNREF(error); -} - -void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq, - void *tag, grpc_error *error, - void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, - grpc_cq_completion *storage), - void *done_arg, grpc_cq_completion *storage) { - cq->vtable->end_op(exec_ctx, cq, tag, error, done, done_arg, storage); -} - -typedef struct { - gpr_atm last_seen_things_queued_ever; - grpc_completion_queue *cq; - gpr_timespec deadline; - grpc_cq_completion *stolen_completion; - void *tag; /* for pluck */ - bool first_loop; -} cq_is_finished_arg; - -static bool cq_is_next_finished(grpc_exec_ctx *exec_ctx, void *arg) { - cq_is_finished_arg *a = (cq_is_finished_arg *)arg; - grpc_completion_queue *cq = a->cq; - cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); - GPR_ASSERT(a->stolen_completion == NULL); - - gpr_atm current_last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever); - - if (current_last_seen_things_queued_ever != a->last_seen_things_queued_ever) { - a->last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever); - - /* Pop a cq_completion from the queue. Returns NULL if the queue is empty - * might return NULL in some cases even if the queue is not empty; but - * that - * is ok and doesn't affect correctness. Might effect the tail latencies a - * bit) */ - a->stolen_completion = cq_event_queue_pop(&cqd->queue); - if (a->stolen_completion != NULL) { - return true; - } - } - return !a->first_loop && - gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0; -} - -#ifndef NDEBUG -static void dump_pending_tags(grpc_completion_queue *cq) { - if (!GRPC_TRACER_ON(grpc_trace_pending_tags)) return; - - gpr_strvec v; - gpr_strvec_init(&v); - gpr_strvec_add(&v, gpr_strdup("PENDING TAGS:")); - gpr_mu_lock(cq->mu); - for (size_t i = 0; i < cq->outstanding_tag_count; i++) { - char *s; - gpr_asprintf(&s, " %p", cq->outstanding_tags[i]); - gpr_strvec_add(&v, s); - } - gpr_mu_unlock(cq->mu); - char *out = gpr_strvec_flatten(&v, NULL); - gpr_strvec_destroy(&v); - gpr_log(GPR_DEBUG, "%s", out); - gpr_free(out); -} -#else -static void dump_pending_tags(grpc_completion_queue *cq) {} -#endif - -static grpc_event cq_next(grpc_completion_queue *cq, gpr_timespec deadline, - void *reserved) { - grpc_event ret; - gpr_timespec now; - cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); - - GPR_TIMER_BEGIN("grpc_completion_queue_next", 0); - - GRPC_API_TRACE( - "grpc_completion_queue_next(" - "cq=%p, " - "deadline=gpr_timespec { tv_sec: %" PRId64 - ", tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 5, (cq, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, - reserved)); - GPR_ASSERT(!reserved); - - dump_pending_tags(cq); - - deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); - - GRPC_CQ_INTERNAL_REF(cq, "next"); - - cq_is_finished_arg is_finished_arg = { - .last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever), - .cq = cq, - .deadline = deadline, - .stolen_completion = NULL, - .tag = NULL, - .first_loop = true}; - grpc_exec_ctx exec_ctx = - GRPC_EXEC_CTX_INITIALIZER(0, cq_is_next_finished, &is_finished_arg); - - for (;;) { - gpr_timespec iteration_deadline = deadline; - - if (is_finished_arg.stolen_completion != NULL) { - grpc_cq_completion *c = is_finished_arg.stolen_completion; - is_finished_arg.stolen_completion = NULL; - ret.type = GRPC_OP_COMPLETE; - ret.success = c->next & 1u; - ret.tag = c->tag; - c->done(&exec_ctx, c->done_arg, c); - break; - } - - grpc_cq_completion *c = cq_event_queue_pop(&cqd->queue); - - if (c != NULL) { - ret.type = GRPC_OP_COMPLETE; - ret.success = c->next & 1u; - ret.tag = c->tag; - c->done(&exec_ctx, c->done_arg, c); - break; - } else { - /* If c == NULL it means either the queue is empty OR in an transient - inconsistent state. If it is the latter, we shold do a 0-timeout poll - so that the thread comes back quickly from poll to make a second - attempt at popping. Not doing this can potentially deadlock this - thread forever (if the deadline is infinity) */ - if (cq_event_queue_num_items(&cqd->queue) > 0) { - iteration_deadline = gpr_time_0(GPR_CLOCK_MONOTONIC); - } - } - - if (gpr_atm_acq_load(&cqd->pending_events) == 0) { - /* Before returning, check if the queue has any items left over (since - gpr_mpscq_pop() can sometimes return NULL even if the queue is not - empty. If so, keep retrying but do not return GRPC_QUEUE_SHUTDOWN */ - if (cq_event_queue_num_items(&cqd->queue) > 0) { - /* Go to the beginning of the loop. No point doing a poll because - (cq->shutdown == true) is only possible when there is no pending - work (i.e cq->pending_events == 0) and any outstanding completion - events should have already been queued on this cq */ - continue; - } - - memset(&ret, 0, sizeof(ret)); - ret.type = GRPC_QUEUE_SHUTDOWN; - break; - } - - now = gpr_now(GPR_CLOCK_MONOTONIC); - if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) { - memset(&ret, 0, sizeof(ret)); - ret.type = GRPC_QUEUE_TIMEOUT; - dump_pending_tags(cq); - break; - } - - /* The main polling work happens in grpc_pollset_work */ - gpr_mu_lock(cq->mu); - cq->num_polls++; - grpc_error *err = cq->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cq), - NULL, now, iteration_deadline); - gpr_mu_unlock(cq->mu); - - if (err != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(err); - gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg); - - GRPC_ERROR_UNREF(err); - memset(&ret, 0, sizeof(ret)); - ret.type = GRPC_QUEUE_TIMEOUT; - dump_pending_tags(cq); - break; - } - is_finished_arg.first_loop = false; - } - - if (cq_event_queue_num_items(&cqd->queue) > 0 && - gpr_atm_acq_load(&cqd->pending_events) > 0) { - gpr_mu_lock(cq->mu); - cq->poller_vtable->kick(&exec_ctx, POLLSET_FROM_CQ(cq), NULL); - gpr_mu_unlock(cq->mu); - } - - GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret); - GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "next"); - grpc_exec_ctx_finish(&exec_ctx); - GPR_ASSERT(is_finished_arg.stolen_completion == NULL); - - GPR_TIMER_END("grpc_completion_queue_next", 0); - - return ret; -} - -/* Finishes the completion queue shutdown. This means that there are no more - completion events / tags expected from the completion queue - - Must be called under completion queue lock - - Must be called only once in completion queue's lifetime - - grpc_completion_queue_shutdown() MUST have been called before calling - this function */ -static void cq_finish_shutdown_next(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq) { - cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); - - GPR_ASSERT(cqd->shutdown_called); - GPR_ASSERT(gpr_atm_no_barrier_load(&cqd->pending_events) == 0); - - cq->poller_vtable->shutdown(exec_ctx, POLLSET_FROM_CQ(cq), - &cq->pollset_shutdown_done); -} - -static void cq_shutdown_next(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq) { - cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); - - /* Need an extra ref for cq here because: - * We call cq_finish_shutdown_next() below, that would call pollset shutdown. - * Pollset shutdown decrements the cq ref count which can potentially destroy - * the cq (if that happens to be the last ref). - * Creating an extra ref here prevents the cq from getting destroyed while - * this function is still active */ - GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); - gpr_mu_lock(cq->mu); - if (cqd->shutdown_called) { - gpr_mu_unlock(cq->mu); - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); - return; - } - cqd->shutdown_called = true; - /* Doing a full_fetch_add (i.e acq/release) here to match with - * cq_begin_op_for_next and and cq_end_op_for_next functions which read/write - * on this counter without necessarily holding a lock on cq */ - if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { - cq_finish_shutdown_next(exec_ctx, cq); - } - gpr_mu_unlock(cq->mu); - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); -} - -grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, - gpr_timespec deadline, void *reserved) { - return cq->vtable->next(cq, deadline, reserved); -} - -static int add_plucker(grpc_completion_queue *cq, void *tag, - grpc_pollset_worker **worker) { - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - if (cqd->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) { - return 0; - } - cqd->pluckers[cqd->num_pluckers].tag = tag; - cqd->pluckers[cqd->num_pluckers].worker = worker; - cqd->num_pluckers++; - return 1; -} - -static void del_plucker(grpc_completion_queue *cq, void *tag, - grpc_pollset_worker **worker) { - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - for (int i = 0; i < cqd->num_pluckers; i++) { - if (cqd->pluckers[i].tag == tag && cqd->pluckers[i].worker == worker) { - cqd->num_pluckers--; - GPR_SWAP(plucker, cqd->pluckers[i], cqd->pluckers[cqd->num_pluckers]); - return; - } - } - GPR_UNREACHABLE_CODE(return ); -} - -static bool cq_is_pluck_finished(grpc_exec_ctx *exec_ctx, void *arg) { - cq_is_finished_arg *a = (cq_is_finished_arg *)arg; - grpc_completion_queue *cq = a->cq; - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - - GPR_ASSERT(a->stolen_completion == NULL); - gpr_atm current_last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever); - if (current_last_seen_things_queued_ever != a->last_seen_things_queued_ever) { - gpr_mu_lock(cq->mu); - a->last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever); - grpc_cq_completion *c; - grpc_cq_completion *prev = &cqd->completed_head; - while ((c = (grpc_cq_completion *)(prev->next & ~(uintptr_t)1)) != - &cqd->completed_head) { - if (c->tag == a->tag) { - prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)1); - if (c == cqd->completed_tail) { - cqd->completed_tail = prev; - } - gpr_mu_unlock(cq->mu); - a->stolen_completion = c; - return true; - } - prev = c; - } - gpr_mu_unlock(cq->mu); - } - return !a->first_loop && - gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0; -} - -static grpc_event cq_pluck(grpc_completion_queue *cq, void *tag, - gpr_timespec deadline, void *reserved) { - grpc_event ret; - grpc_cq_completion *c; - grpc_cq_completion *prev; - grpc_pollset_worker *worker = NULL; - gpr_timespec now; - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - - GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0); - - if (GRPC_TRACER_ON(grpc_cq_pluck_trace)) { - GRPC_API_TRACE( - "grpc_completion_queue_pluck(" - "cq=%p, tag=%p, " - "deadline=gpr_timespec { tv_sec: %" PRId64 - ", tv_nsec: %d, clock_type: %d }, " - "reserved=%p)", - 6, (cq, tag, deadline.tv_sec, deadline.tv_nsec, - (int)deadline.clock_type, reserved)); - } - GPR_ASSERT(!reserved); - - dump_pending_tags(cq); - - deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); - - GRPC_CQ_INTERNAL_REF(cq, "pluck"); - gpr_mu_lock(cq->mu); - cq_is_finished_arg is_finished_arg = { - .last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever), - .cq = cq, - .deadline = deadline, - .stolen_completion = NULL, - .tag = tag, - .first_loop = true}; - grpc_exec_ctx exec_ctx = - GRPC_EXEC_CTX_INITIALIZER(0, cq_is_pluck_finished, &is_finished_arg); - for (;;) { - if (is_finished_arg.stolen_completion != NULL) { - gpr_mu_unlock(cq->mu); - c = is_finished_arg.stolen_completion; - is_finished_arg.stolen_completion = NULL; - ret.type = GRPC_OP_COMPLETE; - ret.success = c->next & 1u; - ret.tag = c->tag; - c->done(&exec_ctx, c->done_arg, c); - break; - } - prev = &cqd->completed_head; - while ((c = (grpc_cq_completion *)(prev->next & ~(uintptr_t)1)) != - &cqd->completed_head) { - if (c->tag == tag) { - prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)1); - if (c == cqd->completed_tail) { - cqd->completed_tail = prev; - } - gpr_mu_unlock(cq->mu); - ret.type = GRPC_OP_COMPLETE; - ret.success = c->next & 1u; - ret.tag = c->tag; - c->done(&exec_ctx, c->done_arg, c); - goto done; - } - prev = c; - } - if (gpr_atm_no_barrier_load(&cqd->shutdown)) { - gpr_mu_unlock(cq->mu); - memset(&ret, 0, sizeof(ret)); - ret.type = GRPC_QUEUE_SHUTDOWN; - break; - } - if (!add_plucker(cq, tag, &worker)) { - gpr_log(GPR_DEBUG, - "Too many outstanding grpc_completion_queue_pluck calls: maximum " - "is %d", - GRPC_MAX_COMPLETION_QUEUE_PLUCKERS); - gpr_mu_unlock(cq->mu); - memset(&ret, 0, sizeof(ret)); - /* TODO(ctiller): should we use a different result here */ - ret.type = GRPC_QUEUE_TIMEOUT; - dump_pending_tags(cq); - break; - } - now = gpr_now(GPR_CLOCK_MONOTONIC); - if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) { - del_plucker(cq, tag, &worker); - gpr_mu_unlock(cq->mu); - memset(&ret, 0, sizeof(ret)); - ret.type = GRPC_QUEUE_TIMEOUT; - dump_pending_tags(cq); - break; - } - - cq->num_polls++; - grpc_error *err = cq->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cq), - &worker, now, deadline); - if (err != GRPC_ERROR_NONE) { - del_plucker(cq, tag, &worker); - gpr_mu_unlock(cq->mu); - const char *msg = grpc_error_string(err); - gpr_log(GPR_ERROR, "Completion queue pluck failed: %s", msg); - - GRPC_ERROR_UNREF(err); - memset(&ret, 0, sizeof(ret)); - ret.type = GRPC_QUEUE_TIMEOUT; - dump_pending_tags(cq); - break; - } - is_finished_arg.first_loop = false; - del_plucker(cq, tag, &worker); - } -done: - GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret); - GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "pluck"); - grpc_exec_ctx_finish(&exec_ctx); - GPR_ASSERT(is_finished_arg.stolen_completion == NULL); - - GPR_TIMER_END("grpc_completion_queue_pluck", 0); - - return ret; -} - -grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cq, void *tag, - gpr_timespec deadline, void *reserved) { - return cq->vtable->pluck(cq, tag, deadline, reserved); -} - -static void cq_finish_shutdown_pluck(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq) { - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - - GPR_ASSERT(cqd->shutdown_called); - GPR_ASSERT(!gpr_atm_no_barrier_load(&cqd->shutdown)); - gpr_atm_no_barrier_store(&cqd->shutdown, 1); - - cq->poller_vtable->shutdown(exec_ctx, POLLSET_FROM_CQ(cq), - &cq->pollset_shutdown_done); -} - -/* NOTE: This function is almost exactly identical to cq_shutdown_next() but - * merging them is a bit tricky and probably not worth it */ -static void cq_shutdown_pluck(grpc_exec_ctx *exec_ctx, - grpc_completion_queue *cq) { - cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); - - /* Need an extra ref for cq here because: - * We call cq_finish_shutdown_pluck() below, that would call pollset shutdown. - * Pollset shutdown decrements the cq ref count which can potentially destroy - * the cq (if that happens to be the last ref). - * Creating an extra ref here prevents the cq from getting destroyed while - * this function is still active */ - GRPC_CQ_INTERNAL_REF(cq, "shutting_down (pluck cq)"); - gpr_mu_lock(cq->mu); - if (cqd->shutdown_called) { - gpr_mu_unlock(cq->mu); - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down (pluck cq)"); - return; - } - cqd->shutdown_called = true; - if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { - cq_finish_shutdown_pluck(exec_ctx, cq); - } - gpr_mu_unlock(cq->mu); - GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down (pluck cq)"); -} - -/* Shutdown simply drops a ref that we reserved at creation time; if we drop - to zero here, then enter shutdown mode and wake up any waiters */ -void grpc_completion_queue_shutdown(grpc_completion_queue *cq) { - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GPR_TIMER_BEGIN("grpc_completion_queue_shutdown", 0); - GRPC_API_TRACE("grpc_completion_queue_shutdown(cq=%p)", 1, (cq)); - cq->vtable->shutdown(&exec_ctx, cq); - grpc_exec_ctx_finish(&exec_ctx); - GPR_TIMER_END("grpc_completion_queue_shutdown", 0); -} - -void grpc_completion_queue_destroy(grpc_completion_queue *cq) { - GRPC_API_TRACE("grpc_completion_queue_destroy(cq=%p)", 1, (cq)); - GPR_TIMER_BEGIN("grpc_completion_queue_destroy", 0); - grpc_completion_queue_shutdown(cq); - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "destroy"); - grpc_exec_ctx_finish(&exec_ctx); - GPR_TIMER_END("grpc_completion_queue_destroy", 0); -} - -grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cq) { - return cq->poller_vtable->can_get_pollset ? POLLSET_FROM_CQ(cq) : NULL; -} - -bool grpc_cq_can_listen(grpc_completion_queue *cq) { - return cq->poller_vtable->can_listen; -} diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc new file mode 100644 index 0000000000..fed66e3a20 --- /dev/null +++ b/src/core/lib/surface/completion_queue.cc @@ -0,0 +1,1249 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "src/core/lib/surface/completion_queue.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/pollset.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/support/spinlock.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/event_string.h" + +grpc_tracer_flag grpc_trace_operation_failures = + GRPC_TRACER_INITIALIZER(false, "op_failure"); +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_pending_tags = + GRPC_TRACER_INITIALIZER(false, "pending_tags"); +grpc_tracer_flag grpc_trace_cq_refcount = + GRPC_TRACER_INITIALIZER(false, "cq_refcount"); +#endif + +typedef struct { + grpc_pollset_worker **worker; + void *tag; +} plucker; + +typedef struct { + bool can_get_pollset; + bool can_listen; + size_t (*size)(void); + void (*init)(grpc_pollset *pollset, gpr_mu **mu); + grpc_error *(*kick)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *specific_worker); + grpc_error *(*work)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker **worker, gpr_timespec now, + gpr_timespec deadline); + void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_closure *closure); + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_pollset *pollset); +} cq_poller_vtable; + +typedef struct non_polling_worker { + gpr_cv cv; + bool kicked; + struct non_polling_worker *next; + struct non_polling_worker *prev; +} non_polling_worker; + +typedef struct { + gpr_mu mu; + non_polling_worker *root; + grpc_closure *shutdown; +} non_polling_poller; + +static size_t non_polling_poller_size(void) { + return sizeof(non_polling_poller); +} + +static void non_polling_poller_init(grpc_pollset *pollset, gpr_mu **mu) { + non_polling_poller *npp = (non_polling_poller *)pollset; + gpr_mu_init(&npp->mu); + *mu = &npp->mu; +} + +static void non_polling_poller_destroy(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset) { + non_polling_poller *npp = (non_polling_poller *)pollset; + gpr_mu_destroy(&npp->mu); +} + +static grpc_error *non_polling_poller_work(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, + grpc_pollset_worker **worker, + gpr_timespec now, + gpr_timespec deadline) { + non_polling_poller *npp = (non_polling_poller *)pollset; + if (npp->shutdown) return GRPC_ERROR_NONE; + non_polling_worker w; + gpr_cv_init(&w.cv); + if (worker != NULL) *worker = (grpc_pollset_worker *)&w; + if (npp->root == NULL) { + npp->root = w.next = w.prev = &w; + } else { + w.next = npp->root; + w.prev = w.next->prev; + w.next->prev = w.prev->next = &w; + } + w.kicked = false; + while (!npp->shutdown && !w.kicked && !gpr_cv_wait(&w.cv, &npp->mu, deadline)) + ; + if (&w == npp->root) { + npp->root = w.next; + if (&w == npp->root) { + if (npp->shutdown) { + GRPC_CLOSURE_SCHED(exec_ctx, npp->shutdown, GRPC_ERROR_NONE); + } + npp->root = NULL; + } + } + w.next->prev = w.prev; + w.prev->next = w.next; + gpr_cv_destroy(&w.cv); + if (worker != NULL) *worker = NULL; + return GRPC_ERROR_NONE; +} + +static grpc_error *non_polling_poller_kick( + grpc_exec_ctx *exec_ctx, grpc_pollset *pollset, + grpc_pollset_worker *specific_worker) { + non_polling_poller *p = (non_polling_poller *)pollset; + if (specific_worker == NULL) specific_worker = (grpc_pollset_worker *)p->root; + if (specific_worker != NULL) { + non_polling_worker *w = (non_polling_worker *)specific_worker; + if (!w->kicked) { + w->kicked = true; + gpr_cv_signal(&w->cv); + } + } + return GRPC_ERROR_NONE; +} + +static void non_polling_poller_shutdown(grpc_exec_ctx *exec_ctx, + grpc_pollset *pollset, + grpc_closure *closure) { + non_polling_poller *p = (non_polling_poller *)pollset; + GPR_ASSERT(closure != NULL); + p->shutdown = closure; + if (p->root == NULL) { + GRPC_CLOSURE_SCHED(exec_ctx, closure, GRPC_ERROR_NONE); + } else { + non_polling_worker *w = p->root; + do { + gpr_cv_signal(&w->cv); + w = w->next; + } while (w != p->root); + } +} + +static const cq_poller_vtable g_poller_vtable_by_poller_type[] = { + /* GRPC_CQ_DEFAULT_POLLING */ + {.can_get_pollset = true, + .can_listen = true, + .size = grpc_pollset_size, + .init = grpc_pollset_init, + .kick = grpc_pollset_kick, + .work = grpc_pollset_work, + .shutdown = grpc_pollset_shutdown, + .destroy = grpc_pollset_destroy}, + /* GRPC_CQ_NON_LISTENING */ + {.can_get_pollset = true, + .can_listen = false, + .size = grpc_pollset_size, + .init = grpc_pollset_init, + .kick = grpc_pollset_kick, + .work = grpc_pollset_work, + .shutdown = grpc_pollset_shutdown, + .destroy = grpc_pollset_destroy}, + /* GRPC_CQ_NON_POLLING */ + {.can_get_pollset = false, + .can_listen = false, + .size = non_polling_poller_size, + .init = non_polling_poller_init, + .kick = non_polling_poller_kick, + .work = non_polling_poller_work, + .shutdown = non_polling_poller_shutdown, + .destroy = non_polling_poller_destroy}, +}; + +typedef struct cq_vtable { + grpc_cq_completion_type cq_completion_type; + size_t data_size; + void (*init)(void *data); + void (*shutdown)(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq); + void (*destroy)(void *data); + bool (*begin_op)(grpc_completion_queue *cq, void *tag); + void (*end_op)(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + grpc_event (*next)(grpc_completion_queue *cq, gpr_timespec deadline, + void *reserved); + grpc_event (*pluck)(grpc_completion_queue *cq, void *tag, + gpr_timespec deadline, void *reserved); +} cq_vtable; + +/* Queue that holds the cq_completion_events. Internally uses gpr_mpscq queue + * (a lockfree multiproducer single consumer queue). It uses a queue_lock + * to support multiple consumers. + * Only used in completion queues whose completion_type is GRPC_CQ_NEXT */ +typedef struct grpc_cq_event_queue { + /* Spinlock to serialize consumers i.e pop() operations */ + gpr_spinlock queue_lock; + + gpr_mpscq queue; + + /* A lazy counter of number of items in the queue. This is NOT atomically + incremented/decremented along with push/pop operations and hence is only + eventually consistent */ + gpr_atm num_queue_items; +} grpc_cq_event_queue; + +typedef struct cq_next_data { + /** Completed events for completion-queues of type GRPC_CQ_NEXT */ + grpc_cq_event_queue queue; + + /** Counter of how many things have ever been queued on this completion queue + useful for avoiding locks to check the queue */ + gpr_atm things_queued_ever; + + /* Number of outstanding events (+1 if not shut down) */ + gpr_atm pending_events; + + /** 0 initially. 1 once we initiated shutdown */ + bool shutdown_called; +} cq_next_data; + +typedef struct cq_pluck_data { + /** Completed events for completion-queues of type GRPC_CQ_PLUCK */ + grpc_cq_completion completed_head; + grpc_cq_completion *completed_tail; + + /** Number of pending events (+1 if we're not shutdown) */ + gpr_atm pending_events; + + /** Counter of how many things have ever been queued on this completion queue + useful for avoiding locks to check the queue */ + gpr_atm things_queued_ever; + + /** 0 initially. 1 once we completed shutting */ + /* TODO: (sreek) This is not needed since (shutdown == 1) if and only if + * (pending_events == 0). So consider removing this in future and use + * pending_events */ + gpr_atm shutdown; + + /** 0 initially. 1 once we initiated shutdown */ + bool shutdown_called; + + int num_pluckers; + plucker pluckers[GRPC_MAX_COMPLETION_QUEUE_PLUCKERS]; +} cq_pluck_data; + +/* Completion queue structure */ +struct grpc_completion_queue { + /** Once owning_refs drops to zero, we will destroy the cq */ + gpr_refcount owning_refs; + + gpr_mu *mu; + + const cq_vtable *vtable; + const cq_poller_vtable *poller_vtable; + +#ifndef NDEBUG + void **outstanding_tags; + size_t outstanding_tag_count; + size_t outstanding_tag_capacity; +#endif + + grpc_closure pollset_shutdown_done; + int num_polls; +}; + +/* Forward declarations */ +static void cq_finish_shutdown_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq); +static void cq_finish_shutdown_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq); +static void cq_shutdown_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq); +static void cq_shutdown_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq); + +static bool cq_begin_op_for_next(grpc_completion_queue *cq, void *tag); +static bool cq_begin_op_for_pluck(grpc_completion_queue *cq, void *tag); + +static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + +static void cq_end_op_for_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage); + +static grpc_event cq_next(grpc_completion_queue *cq, gpr_timespec deadline, + void *reserved); + +static grpc_event cq_pluck(grpc_completion_queue *cq, void *tag, + gpr_timespec deadline, void *reserved); + +static void cq_init_next(void *data); +static void cq_init_pluck(void *data); +static void cq_destroy_next(void *data); +static void cq_destroy_pluck(void *data); + +/* Completion queue vtables based on the completion-type */ +static const cq_vtable g_cq_vtable[] = { + /* GRPC_CQ_NEXT */ + {GRPC_CQ_NEXT, sizeof(cq_next_data), cq_init_next, cq_shutdown_next, + cq_destroy_next, cq_begin_op_for_next, cq_end_op_for_next, cq_next, NULL}, + /* GRPC_CQ_PLUCK */ + {GRPC_CQ_PLUCK, sizeof(cq_pluck_data), cq_init_pluck, cq_shutdown_pluck, + cq_destroy_pluck, cq_begin_op_for_pluck, cq_end_op_for_pluck, NULL, + cq_pluck}, +}; + +#define DATA_FROM_CQ(cq) ((void *)(cq + 1)) +#define POLLSET_FROM_CQ(cq) \ + ((grpc_pollset *)(cq->vtable->data_size + (char *)DATA_FROM_CQ(cq))) + +grpc_tracer_flag grpc_cq_pluck_trace = + GRPC_TRACER_INITIALIZER(true, "queue_pluck"); +grpc_tracer_flag grpc_cq_event_timeout_trace = + GRPC_TRACER_INITIALIZER(true, "queue_timeout"); + +#define GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, event) \ + if (GRPC_TRACER_ON(grpc_api_trace) && \ + (GRPC_TRACER_ON(grpc_cq_pluck_trace) || \ + (event)->type != GRPC_QUEUE_TIMEOUT)) { \ + char *_ev = grpc_event_string(event); \ + gpr_log(GPR_INFO, "RETURN_EVENT[%p]: %s", cq, _ev); \ + gpr_free(_ev); \ + } + +static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *cq, + grpc_error *error); + +static void cq_event_queue_init(grpc_cq_event_queue *q) { + gpr_mpscq_init(&q->queue); + q->queue_lock = GPR_SPINLOCK_INITIALIZER; + gpr_atm_no_barrier_store(&q->num_queue_items, 0); +} + +static void cq_event_queue_destroy(grpc_cq_event_queue *q) { + gpr_mpscq_destroy(&q->queue); +} + +static bool cq_event_queue_push(grpc_cq_event_queue *q, grpc_cq_completion *c) { + gpr_mpscq_push(&q->queue, (gpr_mpscq_node *)c); + return gpr_atm_no_barrier_fetch_add(&q->num_queue_items, 1) == 0; +} + +static grpc_cq_completion *cq_event_queue_pop(grpc_cq_event_queue *q) { + grpc_cq_completion *c = NULL; + if (gpr_spinlock_trylock(&q->queue_lock)) { + c = (grpc_cq_completion *)gpr_mpscq_pop(&q->queue); + gpr_spinlock_unlock(&q->queue_lock); + } + + if (c) { + gpr_atm_no_barrier_fetch_add(&q->num_queue_items, -1); + } + + return c; +} + +/* Note: The counter is not incremented/decremented atomically with push/pop. + * The count is only eventually consistent */ +static long cq_event_queue_num_items(grpc_cq_event_queue *q) { + return (long)gpr_atm_no_barrier_load(&q->num_queue_items); +} + +grpc_completion_queue *grpc_completion_queue_create_internal( + grpc_cq_completion_type completion_type, + grpc_cq_polling_type polling_type) { + grpc_completion_queue *cq; + + GPR_TIMER_BEGIN("grpc_completion_queue_create_internal", 0); + + GRPC_API_TRACE( + "grpc_completion_queue_create_internal(completion_type=%d, " + "polling_type=%d)", + 2, (completion_type, polling_type)); + + const cq_vtable *vtable = &g_cq_vtable[completion_type]; + const cq_poller_vtable *poller_vtable = + &g_poller_vtable_by_poller_type[polling_type]; + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_STATS_INC_CQS_CREATED(&exec_ctx); + grpc_exec_ctx_finish(&exec_ctx); + + cq = (grpc_completion_queue *)gpr_zalloc(sizeof(grpc_completion_queue) + + vtable->data_size + + poller_vtable->size()); + + cq->vtable = vtable; + cq->poller_vtable = poller_vtable; + + /* One for destroy(), one for pollset_shutdown */ + gpr_ref_init(&cq->owning_refs, 2); + + poller_vtable->init(POLLSET_FROM_CQ(cq), &cq->mu); + vtable->init(DATA_FROM_CQ(cq)); + + GRPC_CLOSURE_INIT(&cq->pollset_shutdown_done, on_pollset_shutdown_done, cq, + grpc_schedule_on_exec_ctx); + + GPR_TIMER_END("grpc_completion_queue_create_internal", 0); + + return cq; +} + +static void cq_init_next(void *ptr) { + cq_next_data *cqd = (cq_next_data *)ptr; + /* Initial count is dropped by grpc_completion_queue_shutdown */ + gpr_atm_no_barrier_store(&cqd->pending_events, 1); + cqd->shutdown_called = false; + gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); + cq_event_queue_init(&cqd->queue); +} + +static void cq_destroy_next(void *ptr) { + cq_next_data *cqd = (cq_next_data *)ptr; + GPR_ASSERT(cq_event_queue_num_items(&cqd->queue) == 0); + cq_event_queue_destroy(&cqd->queue); +} + +static void cq_init_pluck(void *ptr) { + cq_pluck_data *cqd = (cq_pluck_data *)ptr; + /* Initial count is dropped by grpc_completion_queue_shutdown */ + gpr_atm_no_barrier_store(&cqd->pending_events, 1); + cqd->completed_tail = &cqd->completed_head; + cqd->completed_head.next = (uintptr_t)cqd->completed_tail; + gpr_atm_no_barrier_store(&cqd->shutdown, 0); + cqd->shutdown_called = false; + cqd->num_pluckers = 0; + gpr_atm_no_barrier_store(&cqd->things_queued_ever, 0); +} + +static void cq_destroy_pluck(void *ptr) { + cq_pluck_data *cqd = (cq_pluck_data *)ptr; + GPR_ASSERT(cqd->completed_head.next == (uintptr_t)&cqd->completed_head); +} + +grpc_cq_completion_type grpc_get_cq_completion_type(grpc_completion_queue *cq) { + return cq->vtable->cq_completion_type; +} + +int grpc_get_cq_poll_num(grpc_completion_queue *cq) { + int cur_num_polls; + gpr_mu_lock(cq->mu); + cur_num_polls = cq->num_polls; + gpr_mu_unlock(cq->mu); + return cur_num_polls; +} + +#ifndef NDEBUG +void grpc_cq_internal_ref(grpc_completion_queue *cq, const char *reason, + const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CQ:%p ref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val + 1, + reason); + } +#else +void grpc_cq_internal_ref(grpc_completion_queue *cq) { +#endif + gpr_ref(&cq->owning_refs); +} + +static void on_pollset_shutdown_done(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_completion_queue *cq = (grpc_completion_queue *)arg; + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "pollset_destroy"); +} + +#ifndef NDEBUG +void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq, + const char *reason, const char *file, int line) { + if (GRPC_TRACER_ON(grpc_trace_cq_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&cq->owning_refs.count); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "CQ:%p unref %" PRIdPTR " -> %" PRIdPTR " %s", cq, val, val - 1, + reason); + } +#else +void grpc_cq_internal_unref(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq) { +#endif + if (gpr_unref(&cq->owning_refs)) { + cq->vtable->destroy(DATA_FROM_CQ(cq)); + cq->poller_vtable->destroy(exec_ctx, POLLSET_FROM_CQ(cq)); +#ifndef NDEBUG + gpr_free(cq->outstanding_tags); +#endif + gpr_free(cq); + } +} + +#ifndef NDEBUG +static void cq_check_tag(grpc_completion_queue *cq, void *tag, bool lock_cq) { + int found = 0; + if (lock_cq) { + gpr_mu_lock(cq->mu); + } + + for (int i = 0; i < (int)cq->outstanding_tag_count; i++) { + if (cq->outstanding_tags[i] == tag) { + cq->outstanding_tag_count--; + GPR_SWAP(void *, cq->outstanding_tags[i], + cq->outstanding_tags[cq->outstanding_tag_count]); + found = 1; + break; + } + } + + if (lock_cq) { + gpr_mu_unlock(cq->mu); + } + + GPR_ASSERT(found); +} +#else +static void cq_check_tag(grpc_completion_queue *cq, void *tag, bool lock_cq) {} +#endif + +/* Atomically increments a counter only if the counter is not zero. Returns + * true if the increment was successful; false if the counter is zero */ +static bool atm_inc_if_nonzero(gpr_atm *counter) { + while (true) { + gpr_atm count = gpr_atm_acq_load(counter); + /* If zero, we are done. If not, we must to a CAS (instead of an atomic + * increment) to maintain the contract: do not increment the counter if it + * is zero. */ + if (count == 0) { + return false; + } else if (gpr_atm_full_cas(counter, count, count + 1)) { + break; + } + } + + return true; +} + +static bool cq_begin_op_for_next(grpc_completion_queue *cq, void *tag) { + cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); + return atm_inc_if_nonzero(&cqd->pending_events); +} + +static bool cq_begin_op_for_pluck(grpc_completion_queue *cq, void *tag) { + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + return atm_inc_if_nonzero(&cqd->pending_events); +} + +bool grpc_cq_begin_op(grpc_completion_queue *cq, void *tag) { +#ifndef NDEBUG + gpr_mu_lock(cq->mu); + if (cq->outstanding_tag_count == cq->outstanding_tag_capacity) { + cq->outstanding_tag_capacity = GPR_MAX(4, 2 * cq->outstanding_tag_capacity); + cq->outstanding_tags = (void **)gpr_realloc( + cq->outstanding_tags, + sizeof(*cq->outstanding_tags) * cq->outstanding_tag_capacity); + } + cq->outstanding_tags[cq->outstanding_tag_count++] = tag; + gpr_mu_unlock(cq->mu); +#endif + return cq->vtable->begin_op(cq, tag); +} + +/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a + * completion + * type of GRPC_CQ_NEXT) */ +static void cq_end_op_for_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + GPR_TIMER_BEGIN("cq_end_op_for_next", 0); + + if (GRPC_TRACER_ON(grpc_api_trace) || + (GRPC_TRACER_ON(grpc_trace_operation_failures) && + error != GRPC_ERROR_NONE)) { + const char *errmsg = grpc_error_string(error); + GRPC_API_TRACE( + "cq_end_op_for_next(exec_ctx=%p, cq=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", + 7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage)); + if (GRPC_TRACER_ON(grpc_trace_operation_failures) && + error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); + } + } + + cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); + int is_success = (error == GRPC_ERROR_NONE); + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = (uintptr_t)(is_success); + + cq_check_tag(cq, tag, true); /* Used in debug builds only */ + + /* Add the completion to the queue */ + bool is_first = cq_event_queue_push(&cqd->queue, storage); + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + + /* Since we do not hold the cq lock here, it is important to do an 'acquire' + load here (instead of a 'no_barrier' load) to match with the release store + (done via gpr_atm_full_fetch_add(pending_events, -1)) in cq_shutdown_next + */ + bool will_definitely_shutdown = gpr_atm_acq_load(&cqd->pending_events) == 1; + + if (!will_definitely_shutdown) { + /* Only kick if this is the first item queued */ + if (is_first) { + gpr_mu_lock(cq->mu); + grpc_error *kick_error = + cq->poller_vtable->kick(exec_ctx, POLLSET_FROM_CQ(cq), NULL); + gpr_mu_unlock(cq->mu); + + if (kick_error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(kick_error); + gpr_log(GPR_ERROR, "Kick failed: %s", msg); + GRPC_ERROR_UNREF(kick_error); + } + } + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_mu_lock(cq->mu); + cq_finish_shutdown_next(exec_ctx, cq); + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); + } + } else { + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_atm_rel_store(&cqd->pending_events, 0); + gpr_mu_lock(cq->mu); + cq_finish_shutdown_next(exec_ctx, cq); + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); + } + + GPR_TIMER_END("cq_end_op_for_next", 0); + + GRPC_ERROR_UNREF(error); +} + +/* Queue a GRPC_OP_COMPLETED operation to a completion queue (with a + * completion + * type of GRPC_CQ_PLUCK) */ +static void cq_end_op_for_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq, void *tag, + grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, + void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + int is_success = (error == GRPC_ERROR_NONE); + + GPR_TIMER_BEGIN("cq_end_op_for_pluck", 0); + + if (GRPC_TRACER_ON(grpc_api_trace) || + (GRPC_TRACER_ON(grpc_trace_operation_failures) && + error != GRPC_ERROR_NONE)) { + const char *errmsg = grpc_error_string(error); + GRPC_API_TRACE( + "cq_end_op_for_pluck(exec_ctx=%p, cq=%p, tag=%p, error=%s, " + "done=%p, done_arg=%p, storage=%p)", + 7, (exec_ctx, cq, tag, errmsg, done, done_arg, storage)); + if (GRPC_TRACER_ON(grpc_trace_operation_failures) && + error != GRPC_ERROR_NONE) { + gpr_log(GPR_ERROR, "Operation failed: tag=%p, error=%s", tag, errmsg); + } + } + + storage->tag = tag; + storage->done = done; + storage->done_arg = done_arg; + storage->next = ((uintptr_t)&cqd->completed_head) | ((uintptr_t)(is_success)); + + gpr_mu_lock(cq->mu); + cq_check_tag(cq, tag, false); /* Used in debug builds only */ + + /* Add to the list of completions */ + gpr_atm_no_barrier_fetch_add(&cqd->things_queued_ever, 1); + cqd->completed_tail->next = + ((uintptr_t)storage) | (1u & (uintptr_t)cqd->completed_tail->next); + cqd->completed_tail = storage; + + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + cq_finish_shutdown_pluck(exec_ctx, cq); + gpr_mu_unlock(cq->mu); + } else { + grpc_pollset_worker *pluck_worker = NULL; + for (int i = 0; i < cqd->num_pluckers; i++) { + if (cqd->pluckers[i].tag == tag) { + pluck_worker = *cqd->pluckers[i].worker; + break; + } + } + + grpc_error *kick_error = + cq->poller_vtable->kick(exec_ctx, POLLSET_FROM_CQ(cq), pluck_worker); + + gpr_mu_unlock(cq->mu); + + if (kick_error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(kick_error); + gpr_log(GPR_ERROR, "Kick failed: %s", msg); + + GRPC_ERROR_UNREF(kick_error); + } + } + + GPR_TIMER_END("cq_end_op_for_pluck", 0); + + GRPC_ERROR_UNREF(error); +} + +void grpc_cq_end_op(grpc_exec_ctx *exec_ctx, grpc_completion_queue *cq, + void *tag, grpc_error *error, + void (*done)(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage), + void *done_arg, grpc_cq_completion *storage) { + cq->vtable->end_op(exec_ctx, cq, tag, error, done, done_arg, storage); +} + +typedef struct { + gpr_atm last_seen_things_queued_ever; + grpc_completion_queue *cq; + gpr_timespec deadline; + grpc_cq_completion *stolen_completion; + void *tag; /* for pluck */ + bool first_loop; +} cq_is_finished_arg; + +static bool cq_is_next_finished(grpc_exec_ctx *exec_ctx, void *arg) { + cq_is_finished_arg *a = (cq_is_finished_arg *)arg; + grpc_completion_queue *cq = a->cq; + cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); + GPR_ASSERT(a->stolen_completion == NULL); + + gpr_atm current_last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + + if (current_last_seen_things_queued_ever != a->last_seen_things_queued_ever) { + a->last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + + /* Pop a cq_completion from the queue. Returns NULL if the queue is empty + * might return NULL in some cases even if the queue is not empty; but + * that + * is ok and doesn't affect correctness. Might effect the tail latencies a + * bit) */ + a->stolen_completion = cq_event_queue_pop(&cqd->queue); + if (a->stolen_completion != NULL) { + return true; + } + } + return !a->first_loop && + gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0; +} + +#ifndef NDEBUG +static void dump_pending_tags(grpc_completion_queue *cq) { + if (!GRPC_TRACER_ON(grpc_trace_pending_tags)) return; + + gpr_strvec v; + gpr_strvec_init(&v); + gpr_strvec_add(&v, gpr_strdup("PENDING TAGS:")); + gpr_mu_lock(cq->mu); + for (size_t i = 0; i < cq->outstanding_tag_count; i++) { + char *s; + gpr_asprintf(&s, " %p", cq->outstanding_tags[i]); + gpr_strvec_add(&v, s); + } + gpr_mu_unlock(cq->mu); + char *out = gpr_strvec_flatten(&v, NULL); + gpr_strvec_destroy(&v); + gpr_log(GPR_DEBUG, "%s", out); + gpr_free(out); +} +#else +static void dump_pending_tags(grpc_completion_queue *cq) {} +#endif + +static grpc_event cq_next(grpc_completion_queue *cq, gpr_timespec deadline, + void *reserved) { + grpc_event ret; + gpr_timespec now; + cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); + + GPR_TIMER_BEGIN("grpc_completion_queue_next", 0); + + GRPC_API_TRACE( + "grpc_completion_queue_next(" + "cq=%p, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 5, (cq, deadline.tv_sec, deadline.tv_nsec, (int)deadline.clock_type, + reserved)); + GPR_ASSERT(!reserved); + + dump_pending_tags(cq); + + deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + + GRPC_CQ_INTERNAL_REF(cq, "next"); + + cq_is_finished_arg is_finished_arg = { + .last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever), + .cq = cq, + .deadline = deadline, + .stolen_completion = NULL, + .tag = NULL, + .first_loop = true}; + grpc_exec_ctx exec_ctx = + GRPC_EXEC_CTX_INITIALIZER(0, cq_is_next_finished, &is_finished_arg); + + for (;;) { + gpr_timespec iteration_deadline = deadline; + + if (is_finished_arg.stolen_completion != NULL) { + grpc_cq_completion *c = is_finished_arg.stolen_completion; + is_finished_arg.stolen_completion = NULL; + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(&exec_ctx, c->done_arg, c); + break; + } + + grpc_cq_completion *c = cq_event_queue_pop(&cqd->queue); + + if (c != NULL) { + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(&exec_ctx, c->done_arg, c); + break; + } else { + /* If c == NULL it means either the queue is empty OR in an transient + inconsistent state. If it is the latter, we shold do a 0-timeout poll + so that the thread comes back quickly from poll to make a second + attempt at popping. Not doing this can potentially deadlock this + thread forever (if the deadline is infinity) */ + if (cq_event_queue_num_items(&cqd->queue) > 0) { + iteration_deadline = gpr_time_0(GPR_CLOCK_MONOTONIC); + } + } + + if (gpr_atm_acq_load(&cqd->pending_events) == 0) { + /* Before returning, check if the queue has any items left over (since + gpr_mpscq_pop() can sometimes return NULL even if the queue is not + empty. If so, keep retrying but do not return GRPC_QUEUE_SHUTDOWN */ + if (cq_event_queue_num_items(&cqd->queue) > 0) { + /* Go to the beginning of the loop. No point doing a poll because + (cq->shutdown == true) is only possible when there is no pending + work (i.e cq->pending_events == 0) and any outstanding completion + events should have already been queued on this cq */ + continue; + } + + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; + break; + } + + now = gpr_now(GPR_CLOCK_MONOTONIC); + if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) { + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + + /* The main polling work happens in grpc_pollset_work */ + gpr_mu_lock(cq->mu); + cq->num_polls++; + grpc_error *err = cq->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cq), + NULL, now, iteration_deadline); + gpr_mu_unlock(cq->mu); + + if (err != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "Completion queue next failed: %s", msg); + + GRPC_ERROR_UNREF(err); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + is_finished_arg.first_loop = false; + } + + if (cq_event_queue_num_items(&cqd->queue) > 0 && + gpr_atm_acq_load(&cqd->pending_events) > 0) { + gpr_mu_lock(cq->mu); + cq->poller_vtable->kick(&exec_ctx, POLLSET_FROM_CQ(cq), NULL); + gpr_mu_unlock(cq->mu); + } + + GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret); + GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "next"); + grpc_exec_ctx_finish(&exec_ctx); + GPR_ASSERT(is_finished_arg.stolen_completion == NULL); + + GPR_TIMER_END("grpc_completion_queue_next", 0); + + return ret; +} + +/* Finishes the completion queue shutdown. This means that there are no more + completion events / tags expected from the completion queue + - Must be called under completion queue lock + - Must be called only once in completion queue's lifetime + - grpc_completion_queue_shutdown() MUST have been called before calling + this function */ +static void cq_finish_shutdown_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq) { + cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); + + GPR_ASSERT(cqd->shutdown_called); + GPR_ASSERT(gpr_atm_no_barrier_load(&cqd->pending_events) == 0); + + cq->poller_vtable->shutdown(exec_ctx, POLLSET_FROM_CQ(cq), + &cq->pollset_shutdown_done); +} + +static void cq_shutdown_next(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq) { + cq_next_data *cqd = (cq_next_data *)DATA_FROM_CQ(cq); + + /* Need an extra ref for cq here because: + * We call cq_finish_shutdown_next() below, that would call pollset shutdown. + * Pollset shutdown decrements the cq ref count which can potentially destroy + * the cq (if that happens to be the last ref). + * Creating an extra ref here prevents the cq from getting destroyed while + * this function is still active */ + GRPC_CQ_INTERNAL_REF(cq, "shutting_down"); + gpr_mu_lock(cq->mu); + if (cqd->shutdown_called) { + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); + return; + } + cqd->shutdown_called = true; + /* Doing a full_fetch_add (i.e acq/release) here to match with + * cq_begin_op_for_next and and cq_end_op_for_next functions which read/write + * on this counter without necessarily holding a lock on cq */ + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + cq_finish_shutdown_next(exec_ctx, cq); + } + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down"); +} + +grpc_event grpc_completion_queue_next(grpc_completion_queue *cq, + gpr_timespec deadline, void *reserved) { + return cq->vtable->next(cq, deadline, reserved); +} + +static int add_plucker(grpc_completion_queue *cq, void *tag, + grpc_pollset_worker **worker) { + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + if (cqd->num_pluckers == GRPC_MAX_COMPLETION_QUEUE_PLUCKERS) { + return 0; + } + cqd->pluckers[cqd->num_pluckers].tag = tag; + cqd->pluckers[cqd->num_pluckers].worker = worker; + cqd->num_pluckers++; + return 1; +} + +static void del_plucker(grpc_completion_queue *cq, void *tag, + grpc_pollset_worker **worker) { + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + for (int i = 0; i < cqd->num_pluckers; i++) { + if (cqd->pluckers[i].tag == tag && cqd->pluckers[i].worker == worker) { + cqd->num_pluckers--; + GPR_SWAP(plucker, cqd->pluckers[i], cqd->pluckers[cqd->num_pluckers]); + return; + } + } + GPR_UNREACHABLE_CODE(return ); +} + +static bool cq_is_pluck_finished(grpc_exec_ctx *exec_ctx, void *arg) { + cq_is_finished_arg *a = (cq_is_finished_arg *)arg; + grpc_completion_queue *cq = a->cq; + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + + GPR_ASSERT(a->stolen_completion == NULL); + gpr_atm current_last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + if (current_last_seen_things_queued_ever != a->last_seen_things_queued_ever) { + gpr_mu_lock(cq->mu); + a->last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever); + grpc_cq_completion *c; + grpc_cq_completion *prev = &cqd->completed_head; + while ((c = (grpc_cq_completion *)(prev->next & ~(uintptr_t)1)) != + &cqd->completed_head) { + if (c->tag == a->tag) { + prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)1); + if (c == cqd->completed_tail) { + cqd->completed_tail = prev; + } + gpr_mu_unlock(cq->mu); + a->stolen_completion = c; + return true; + } + prev = c; + } + gpr_mu_unlock(cq->mu); + } + return !a->first_loop && + gpr_time_cmp(a->deadline, gpr_now(a->deadline.clock_type)) < 0; +} + +static grpc_event cq_pluck(grpc_completion_queue *cq, void *tag, + gpr_timespec deadline, void *reserved) { + grpc_event ret; + grpc_cq_completion *c; + grpc_cq_completion *prev; + grpc_pollset_worker *worker = NULL; + gpr_timespec now; + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + + GPR_TIMER_BEGIN("grpc_completion_queue_pluck", 0); + + if (GRPC_TRACER_ON(grpc_cq_pluck_trace)) { + GRPC_API_TRACE( + "grpc_completion_queue_pluck(" + "cq=%p, tag=%p, " + "deadline=gpr_timespec { tv_sec: %" PRId64 + ", tv_nsec: %d, clock_type: %d }, " + "reserved=%p)", + 6, (cq, tag, deadline.tv_sec, deadline.tv_nsec, + (int)deadline.clock_type, reserved)); + } + GPR_ASSERT(!reserved); + + dump_pending_tags(cq); + + deadline = gpr_convert_clock_type(deadline, GPR_CLOCK_MONOTONIC); + + GRPC_CQ_INTERNAL_REF(cq, "pluck"); + gpr_mu_lock(cq->mu); + cq_is_finished_arg is_finished_arg = { + .last_seen_things_queued_ever = + gpr_atm_no_barrier_load(&cqd->things_queued_ever), + .cq = cq, + .deadline = deadline, + .stolen_completion = NULL, + .tag = tag, + .first_loop = true}; + grpc_exec_ctx exec_ctx = + GRPC_EXEC_CTX_INITIALIZER(0, cq_is_pluck_finished, &is_finished_arg); + for (;;) { + if (is_finished_arg.stolen_completion != NULL) { + gpr_mu_unlock(cq->mu); + c = is_finished_arg.stolen_completion; + is_finished_arg.stolen_completion = NULL; + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(&exec_ctx, c->done_arg, c); + break; + } + prev = &cqd->completed_head; + while ((c = (grpc_cq_completion *)(prev->next & ~(uintptr_t)1)) != + &cqd->completed_head) { + if (c->tag == tag) { + prev->next = (prev->next & (uintptr_t)1) | (c->next & ~(uintptr_t)1); + if (c == cqd->completed_tail) { + cqd->completed_tail = prev; + } + gpr_mu_unlock(cq->mu); + ret.type = GRPC_OP_COMPLETE; + ret.success = c->next & 1u; + ret.tag = c->tag; + c->done(&exec_ctx, c->done_arg, c); + goto done; + } + prev = c; + } + if (gpr_atm_no_barrier_load(&cqd->shutdown)) { + gpr_mu_unlock(cq->mu); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_SHUTDOWN; + break; + } + if (!add_plucker(cq, tag, &worker)) { + gpr_log(GPR_DEBUG, + "Too many outstanding grpc_completion_queue_pluck calls: maximum " + "is %d", + GRPC_MAX_COMPLETION_QUEUE_PLUCKERS); + gpr_mu_unlock(cq->mu); + memset(&ret, 0, sizeof(ret)); + /* TODO(ctiller): should we use a different result here */ + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + now = gpr_now(GPR_CLOCK_MONOTONIC); + if (!is_finished_arg.first_loop && gpr_time_cmp(now, deadline) >= 0) { + del_plucker(cq, tag, &worker); + gpr_mu_unlock(cq->mu); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + + cq->num_polls++; + grpc_error *err = cq->poller_vtable->work(&exec_ctx, POLLSET_FROM_CQ(cq), + &worker, now, deadline); + if (err != GRPC_ERROR_NONE) { + del_plucker(cq, tag, &worker); + gpr_mu_unlock(cq->mu); + const char *msg = grpc_error_string(err); + gpr_log(GPR_ERROR, "Completion queue pluck failed: %s", msg); + + GRPC_ERROR_UNREF(err); + memset(&ret, 0, sizeof(ret)); + ret.type = GRPC_QUEUE_TIMEOUT; + dump_pending_tags(cq); + break; + } + is_finished_arg.first_loop = false; + del_plucker(cq, tag, &worker); + } +done: + GRPC_SURFACE_TRACE_RETURNED_EVENT(cq, &ret); + GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "pluck"); + grpc_exec_ctx_finish(&exec_ctx); + GPR_ASSERT(is_finished_arg.stolen_completion == NULL); + + GPR_TIMER_END("grpc_completion_queue_pluck", 0); + + return ret; +} + +grpc_event grpc_completion_queue_pluck(grpc_completion_queue *cq, void *tag, + gpr_timespec deadline, void *reserved) { + return cq->vtable->pluck(cq, tag, deadline, reserved); +} + +static void cq_finish_shutdown_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq) { + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + + GPR_ASSERT(cqd->shutdown_called); + GPR_ASSERT(!gpr_atm_no_barrier_load(&cqd->shutdown)); + gpr_atm_no_barrier_store(&cqd->shutdown, 1); + + cq->poller_vtable->shutdown(exec_ctx, POLLSET_FROM_CQ(cq), + &cq->pollset_shutdown_done); +} + +/* NOTE: This function is almost exactly identical to cq_shutdown_next() but + * merging them is a bit tricky and probably not worth it */ +static void cq_shutdown_pluck(grpc_exec_ctx *exec_ctx, + grpc_completion_queue *cq) { + cq_pluck_data *cqd = (cq_pluck_data *)DATA_FROM_CQ(cq); + + /* Need an extra ref for cq here because: + * We call cq_finish_shutdown_pluck() below, that would call pollset shutdown. + * Pollset shutdown decrements the cq ref count which can potentially destroy + * the cq (if that happens to be the last ref). + * Creating an extra ref here prevents the cq from getting destroyed while + * this function is still active */ + GRPC_CQ_INTERNAL_REF(cq, "shutting_down (pluck cq)"); + gpr_mu_lock(cq->mu); + if (cqd->shutdown_called) { + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down (pluck cq)"); + return; + } + cqd->shutdown_called = true; + if (gpr_atm_full_fetch_add(&cqd->pending_events, -1) == 1) { + cq_finish_shutdown_pluck(exec_ctx, cq); + } + gpr_mu_unlock(cq->mu); + GRPC_CQ_INTERNAL_UNREF(exec_ctx, cq, "shutting_down (pluck cq)"); +} + +/* Shutdown simply drops a ref that we reserved at creation time; if we drop + to zero here, then enter shutdown mode and wake up any waiters */ +void grpc_completion_queue_shutdown(grpc_completion_queue *cq) { + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GPR_TIMER_BEGIN("grpc_completion_queue_shutdown", 0); + GRPC_API_TRACE("grpc_completion_queue_shutdown(cq=%p)", 1, (cq)); + cq->vtable->shutdown(&exec_ctx, cq); + grpc_exec_ctx_finish(&exec_ctx); + GPR_TIMER_END("grpc_completion_queue_shutdown", 0); +} + +void grpc_completion_queue_destroy(grpc_completion_queue *cq) { + GRPC_API_TRACE("grpc_completion_queue_destroy(cq=%p)", 1, (cq)); + GPR_TIMER_BEGIN("grpc_completion_queue_destroy", 0); + grpc_completion_queue_shutdown(cq); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + GRPC_CQ_INTERNAL_UNREF(&exec_ctx, cq, "destroy"); + grpc_exec_ctx_finish(&exec_ctx); + GPR_TIMER_END("grpc_completion_queue_destroy", 0); +} + +grpc_pollset *grpc_cq_pollset(grpc_completion_queue *cq) { + return cq->poller_vtable->can_get_pollset ? POLLSET_FROM_CQ(cq) : NULL; +} + +bool grpc_cq_can_listen(grpc_completion_queue *cq) { + return cq->poller_vtable->can_listen; +} diff --git a/src/core/lib/surface/completion_queue_factory.c b/src/core/lib/surface/completion_queue_factory.c deleted file mode 100644 index aeecff5306..0000000000 --- a/src/core/lib/surface/completion_queue_factory.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/completion_queue_factory.h" -#include "src/core/lib/surface/completion_queue.h" - -#include - -/* - * == Default completion queue factory implementation == - */ - -static grpc_completion_queue* default_create( - const grpc_completion_queue_factory* factory, - const grpc_completion_queue_attributes* attr) { - return grpc_completion_queue_create_internal(attr->cq_completion_type, - attr->cq_polling_type); -} - -static grpc_completion_queue_factory_vtable default_vtable = {default_create}; - -static const grpc_completion_queue_factory g_default_cq_factory = { - "Default Factory", NULL, &default_vtable}; - -/* - * == Completion queue factory APIs - */ - -const grpc_completion_queue_factory* grpc_completion_queue_factory_lookup( - const grpc_completion_queue_attributes* attributes) { - GPR_ASSERT(attributes->version >= 1 && - attributes->version <= GRPC_CQ_CURRENT_VERSION); - - /* The default factory can handle version 1 of the attributes structure. We - may have to change this as more fields are added to the structure */ - return &g_default_cq_factory; -} - -/* - * == Completion queue creation APIs == - */ - -grpc_completion_queue* grpc_completion_queue_create_for_next(void* reserved) { - GPR_ASSERT(!reserved); - grpc_completion_queue_attributes attr = {1, GRPC_CQ_NEXT, - GRPC_CQ_DEFAULT_POLLING}; - return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); -} - -grpc_completion_queue* grpc_completion_queue_create_for_pluck(void* reserved) { - GPR_ASSERT(!reserved); - grpc_completion_queue_attributes attr = {1, GRPC_CQ_PLUCK, - GRPC_CQ_DEFAULT_POLLING}; - return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); -} - -grpc_completion_queue* grpc_completion_queue_create( - const grpc_completion_queue_factory* factory, - const grpc_completion_queue_attributes* attr, void* reserved) { - GPR_ASSERT(!reserved); - return factory->vtable->create(factory, attr); -} diff --git a/src/core/lib/surface/completion_queue_factory.cc b/src/core/lib/surface/completion_queue_factory.cc new file mode 100644 index 0000000000..aeecff5306 --- /dev/null +++ b/src/core/lib/surface/completion_queue_factory.cc @@ -0,0 +1,77 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/completion_queue_factory.h" +#include "src/core/lib/surface/completion_queue.h" + +#include + +/* + * == Default completion queue factory implementation == + */ + +static grpc_completion_queue* default_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attr) { + return grpc_completion_queue_create_internal(attr->cq_completion_type, + attr->cq_polling_type); +} + +static grpc_completion_queue_factory_vtable default_vtable = {default_create}; + +static const grpc_completion_queue_factory g_default_cq_factory = { + "Default Factory", NULL, &default_vtable}; + +/* + * == Completion queue factory APIs + */ + +const grpc_completion_queue_factory* grpc_completion_queue_factory_lookup( + const grpc_completion_queue_attributes* attributes) { + GPR_ASSERT(attributes->version >= 1 && + attributes->version <= GRPC_CQ_CURRENT_VERSION); + + /* The default factory can handle version 1 of the attributes structure. We + may have to change this as more fields are added to the structure */ + return &g_default_cq_factory; +} + +/* + * == Completion queue creation APIs == + */ + +grpc_completion_queue* grpc_completion_queue_create_for_next(void* reserved) { + GPR_ASSERT(!reserved); + grpc_completion_queue_attributes attr = {1, GRPC_CQ_NEXT, + GRPC_CQ_DEFAULT_POLLING}; + return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); +} + +grpc_completion_queue* grpc_completion_queue_create_for_pluck(void* reserved) { + GPR_ASSERT(!reserved); + grpc_completion_queue_attributes attr = {1, GRPC_CQ_PLUCK, + GRPC_CQ_DEFAULT_POLLING}; + return g_default_cq_factory.vtable->create(&g_default_cq_factory, &attr); +} + +grpc_completion_queue* grpc_completion_queue_create( + const grpc_completion_queue_factory* factory, + const grpc_completion_queue_attributes* attr, void* reserved) { + GPR_ASSERT(!reserved); + return factory->vtable->create(factory, attr); +} diff --git a/src/core/lib/surface/event_string.c b/src/core/lib/surface/event_string.c deleted file mode 100644 index f236272e2a..0000000000 --- a/src/core/lib/surface/event_string.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/event_string.h" - -#include - -#include -#include -#include "src/core/lib/support/string.h" - -static void addhdr(gpr_strvec *buf, grpc_event *ev) { - char *tmp; - gpr_asprintf(&tmp, "tag:%p", ev->tag); - gpr_strvec_add(buf, tmp); -} - -static const char *errstr(int success) { return success ? "OK" : "ERROR"; } - -static void adderr(gpr_strvec *buf, int success) { - char *tmp; - gpr_asprintf(&tmp, " %s", errstr(success)); - gpr_strvec_add(buf, tmp); -} - -char *grpc_event_string(grpc_event *ev) { - char *out; - gpr_strvec buf; - - if (ev == NULL) return gpr_strdup("null"); - - gpr_strvec_init(&buf); - - switch (ev->type) { - case GRPC_QUEUE_TIMEOUT: - gpr_strvec_add(&buf, gpr_strdup("QUEUE_TIMEOUT")); - break; - case GRPC_QUEUE_SHUTDOWN: - gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN")); - break; - case GRPC_OP_COMPLETE: - gpr_strvec_add(&buf, gpr_strdup("OP_COMPLETE: ")); - addhdr(&buf, ev); - adderr(&buf, ev->success); - break; - } - - out = gpr_strvec_flatten(&buf, NULL); - gpr_strvec_destroy(&buf); - return out; -} diff --git a/src/core/lib/surface/event_string.cc b/src/core/lib/surface/event_string.cc new file mode 100644 index 0000000000..f236272e2a --- /dev/null +++ b/src/core/lib/surface/event_string.cc @@ -0,0 +1,66 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/event_string.h" + +#include + +#include +#include +#include "src/core/lib/support/string.h" + +static void addhdr(gpr_strvec *buf, grpc_event *ev) { + char *tmp; + gpr_asprintf(&tmp, "tag:%p", ev->tag); + gpr_strvec_add(buf, tmp); +} + +static const char *errstr(int success) { return success ? "OK" : "ERROR"; } + +static void adderr(gpr_strvec *buf, int success) { + char *tmp; + gpr_asprintf(&tmp, " %s", errstr(success)); + gpr_strvec_add(buf, tmp); +} + +char *grpc_event_string(grpc_event *ev) { + char *out; + gpr_strvec buf; + + if (ev == NULL) return gpr_strdup("null"); + + gpr_strvec_init(&buf); + + switch (ev->type) { + case GRPC_QUEUE_TIMEOUT: + gpr_strvec_add(&buf, gpr_strdup("QUEUE_TIMEOUT")); + break; + case GRPC_QUEUE_SHUTDOWN: + gpr_strvec_add(&buf, gpr_strdup("QUEUE_SHUTDOWN")); + break; + case GRPC_OP_COMPLETE: + gpr_strvec_add(&buf, gpr_strdup("OP_COMPLETE: ")); + addhdr(&buf, ev); + adderr(&buf, ev->success); + break; + } + + out = gpr_strvec_flatten(&buf, NULL); + gpr_strvec_destroy(&buf); + return out; +} diff --git a/src/core/lib/surface/init.c b/src/core/lib/surface/init.c deleted file mode 100644 index b089da2c54..0000000000 --- a/src/core/lib/surface/init.c +++ /dev/null @@ -1,209 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include "src/core/lib/channel/channel_stack.h" -#include "src/core/lib/channel/connected_channel.h" -#include "src/core/lib/channel/handshaker_registry.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/http/parser.h" -#include "src/core/lib/iomgr/call_combiner.h" -#include "src/core/lib/iomgr/combiner.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "src/core/lib/iomgr/resource_quota.h" -#include "src/core/lib/iomgr/timer_manager.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/surface/alarm_internal.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/lib/surface/completion_queue.h" -#include "src/core/lib/surface/init.h" -#include "src/core/lib/surface/lame_client.h" -#include "src/core/lib/surface/server.h" -#include "src/core/lib/transport/bdp_estimator.h" -#include "src/core/lib/transport/connectivity_state.h" -#include "src/core/lib/transport/transport_impl.h" - -/* (generated) built in registry of plugins */ -extern void grpc_register_built_in_plugins(void); - -#define MAX_PLUGINS 128 - -static gpr_once g_basic_init = GPR_ONCE_INIT; -static gpr_mu g_init_mu; -static int g_initializations; - -static void do_basic_init(void) { - gpr_log_verbosity_init(); - gpr_mu_init(&g_init_mu); - grpc_register_built_in_plugins(); - g_initializations = 0; -} - -static bool append_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, void *arg) { - return grpc_channel_stack_builder_append_filter( - builder, (const grpc_channel_filter *)arg, NULL, NULL); -} - -static bool prepend_filter(grpc_exec_ctx *exec_ctx, - grpc_channel_stack_builder *builder, void *arg) { - return grpc_channel_stack_builder_prepend_filter( - builder, (const grpc_channel_filter *)arg, NULL, NULL); -} - -static void register_builtin_channel_init() { - grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - grpc_add_connected_filter, NULL); - grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - grpc_add_connected_filter, NULL); - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - grpc_add_connected_filter, NULL); - grpc_channel_init_register_stage(GRPC_CLIENT_LAME_CHANNEL, - GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, - append_filter, (void *)&grpc_lame_filter); - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, prepend_filter, - (void *)&grpc_server_top_filter); -} - -typedef struct grpc_plugin { - void (*init)(); - void (*destroy)(); -} grpc_plugin; - -static grpc_plugin g_all_of_the_plugins[MAX_PLUGINS]; -static int g_number_of_plugins = 0; - -void grpc_register_plugin(void (*init)(void), void (*destroy)(void)) { - GRPC_API_TRACE("grpc_register_plugin(init=%p, destroy=%p)", 2, - ((void *)(intptr_t)init, (void *)(intptr_t)destroy)); - GPR_ASSERT(g_number_of_plugins != MAX_PLUGINS); - g_all_of_the_plugins[g_number_of_plugins].init = init; - g_all_of_the_plugins[g_number_of_plugins].destroy = destroy; - g_number_of_plugins++; -} - -void grpc_init(void) { - int i; - gpr_once_init(&g_basic_init, do_basic_init); - - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - gpr_mu_lock(&g_init_mu); - if (++g_initializations == 1) { - gpr_time_init(); - grpc_stats_init(); - grpc_slice_intern_init(); - grpc_mdctx_global_init(); - grpc_channel_init_init(); - grpc_register_tracer(&grpc_api_trace); - grpc_register_tracer(&grpc_trace_channel); - grpc_register_tracer(&grpc_connectivity_state_trace); - grpc_register_tracer(&grpc_trace_channel_stack_builder); - grpc_register_tracer(&grpc_http1_trace); - grpc_register_tracer(&grpc_cq_pluck_trace); // default on - grpc_register_tracer(&grpc_call_combiner_trace); - grpc_register_tracer(&grpc_combiner_trace); - grpc_register_tracer(&grpc_server_channel_trace); - grpc_register_tracer(&grpc_bdp_estimator_trace); - grpc_register_tracer(&grpc_cq_event_timeout_trace); // default on - grpc_register_tracer(&grpc_trace_operation_failures); - grpc_register_tracer(&grpc_resource_quota_trace); - grpc_register_tracer(&grpc_call_error_trace); -#ifndef NDEBUG - grpc_register_tracer(&grpc_trace_pending_tags); - grpc_register_tracer(&grpc_trace_alarm_refcount); - grpc_register_tracer(&grpc_trace_cq_refcount); - grpc_register_tracer(&grpc_trace_closure); - grpc_register_tracer(&grpc_trace_error_refcount); - grpc_register_tracer(&grpc_trace_stream_refcount); - grpc_register_tracer(&grpc_trace_fd_refcount); - grpc_register_tracer(&grpc_trace_metadata); -#endif - grpc_security_pre_init(); - grpc_iomgr_init(&exec_ctx); - gpr_timers_global_init(); - grpc_handshaker_factory_registry_init(); - grpc_security_init(); - for (i = 0; i < g_number_of_plugins; i++) { - if (g_all_of_the_plugins[i].init != NULL) { - g_all_of_the_plugins[i].init(); - } - } - /* register channel finalization AFTER all plugins, to ensure that it's run - * at the appropriate time */ - grpc_register_security_filters(); - register_builtin_channel_init(); - grpc_tracer_init("GRPC_TRACE"); - /* no more changes to channel init pipelines */ - grpc_channel_init_finalize(); - grpc_iomgr_start(&exec_ctx); - } - gpr_mu_unlock(&g_init_mu); - grpc_exec_ctx_finish(&exec_ctx); - GRPC_API_TRACE("grpc_init(void)", 0, ()); -} - -void grpc_shutdown(void) { - int i; - GRPC_API_TRACE("grpc_shutdown(void)", 0, ()); - grpc_exec_ctx exec_ctx = - GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL); - gpr_mu_lock(&g_init_mu); - if (--g_initializations == 0) { - grpc_executor_shutdown(&exec_ctx); - grpc_timer_manager_set_threading(false); // shutdown timer_manager thread - for (i = g_number_of_plugins; i >= 0; i--) { - if (g_all_of_the_plugins[i].destroy != NULL) { - g_all_of_the_plugins[i].destroy(); - } - } - grpc_iomgr_shutdown(&exec_ctx); - gpr_timers_global_destroy(); - grpc_tracer_shutdown(); - grpc_mdctx_global_shutdown(&exec_ctx); - grpc_handshaker_factory_registry_shutdown(&exec_ctx); - grpc_slice_intern_shutdown(); - grpc_stats_shutdown(); - } - gpr_mu_unlock(&g_init_mu); - grpc_exec_ctx_finish(&exec_ctx); -} - -int grpc_is_initialized(void) { - int r; - gpr_once_init(&g_basic_init, do_basic_init); - gpr_mu_lock(&g_init_mu); - r = g_initializations > 0; - gpr_mu_unlock(&g_init_mu); - return r; -} diff --git a/src/core/lib/surface/init.cc b/src/core/lib/surface/init.cc new file mode 100644 index 0000000000..b089da2c54 --- /dev/null +++ b/src/core/lib/surface/init.cc @@ -0,0 +1,209 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include "src/core/lib/channel/channel_stack.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/channel/handshaker_registry.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/http/parser.h" +#include "src/core/lib/iomgr/call_combiner.h" +#include "src/core/lib/iomgr/combiner.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/iomgr/resource_quota.h" +#include "src/core/lib/iomgr/timer_manager.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/surface/alarm_internal.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/init.h" +#include "src/core/lib/surface/lame_client.h" +#include "src/core/lib/surface/server.h" +#include "src/core/lib/transport/bdp_estimator.h" +#include "src/core/lib/transport/connectivity_state.h" +#include "src/core/lib/transport/transport_impl.h" + +/* (generated) built in registry of plugins */ +extern void grpc_register_built_in_plugins(void); + +#define MAX_PLUGINS 128 + +static gpr_once g_basic_init = GPR_ONCE_INIT; +static gpr_mu g_init_mu; +static int g_initializations; + +static void do_basic_init(void) { + gpr_log_verbosity_init(); + gpr_mu_init(&g_init_mu); + grpc_register_built_in_plugins(); + g_initializations = 0; +} + +static bool append_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, void *arg) { + return grpc_channel_stack_builder_append_filter( + builder, (const grpc_channel_filter *)arg, NULL, NULL); +} + +static bool prepend_filter(grpc_exec_ctx *exec_ctx, + grpc_channel_stack_builder *builder, void *arg) { + return grpc_channel_stack_builder_prepend_filter( + builder, (const grpc_channel_filter *)arg, NULL, NULL); +} + +static void register_builtin_channel_init() { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_add_connected_filter, NULL); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_add_connected_filter, NULL); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_add_connected_filter, NULL); + grpc_channel_init_register_stage(GRPC_CLIENT_LAME_CHANNEL, + GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + append_filter, (void *)&grpc_lame_filter); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, prepend_filter, + (void *)&grpc_server_top_filter); +} + +typedef struct grpc_plugin { + void (*init)(); + void (*destroy)(); +} grpc_plugin; + +static grpc_plugin g_all_of_the_plugins[MAX_PLUGINS]; +static int g_number_of_plugins = 0; + +void grpc_register_plugin(void (*init)(void), void (*destroy)(void)) { + GRPC_API_TRACE("grpc_register_plugin(init=%p, destroy=%p)", 2, + ((void *)(intptr_t)init, (void *)(intptr_t)destroy)); + GPR_ASSERT(g_number_of_plugins != MAX_PLUGINS); + g_all_of_the_plugins[g_number_of_plugins].init = init; + g_all_of_the_plugins[g_number_of_plugins].destroy = destroy; + g_number_of_plugins++; +} + +void grpc_init(void) { + int i; + gpr_once_init(&g_basic_init, do_basic_init); + + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + gpr_mu_lock(&g_init_mu); + if (++g_initializations == 1) { + gpr_time_init(); + grpc_stats_init(); + grpc_slice_intern_init(); + grpc_mdctx_global_init(); + grpc_channel_init_init(); + grpc_register_tracer(&grpc_api_trace); + grpc_register_tracer(&grpc_trace_channel); + grpc_register_tracer(&grpc_connectivity_state_trace); + grpc_register_tracer(&grpc_trace_channel_stack_builder); + grpc_register_tracer(&grpc_http1_trace); + grpc_register_tracer(&grpc_cq_pluck_trace); // default on + grpc_register_tracer(&grpc_call_combiner_trace); + grpc_register_tracer(&grpc_combiner_trace); + grpc_register_tracer(&grpc_server_channel_trace); + grpc_register_tracer(&grpc_bdp_estimator_trace); + grpc_register_tracer(&grpc_cq_event_timeout_trace); // default on + grpc_register_tracer(&grpc_trace_operation_failures); + grpc_register_tracer(&grpc_resource_quota_trace); + grpc_register_tracer(&grpc_call_error_trace); +#ifndef NDEBUG + grpc_register_tracer(&grpc_trace_pending_tags); + grpc_register_tracer(&grpc_trace_alarm_refcount); + grpc_register_tracer(&grpc_trace_cq_refcount); + grpc_register_tracer(&grpc_trace_closure); + grpc_register_tracer(&grpc_trace_error_refcount); + grpc_register_tracer(&grpc_trace_stream_refcount); + grpc_register_tracer(&grpc_trace_fd_refcount); + grpc_register_tracer(&grpc_trace_metadata); +#endif + grpc_security_pre_init(); + grpc_iomgr_init(&exec_ctx); + gpr_timers_global_init(); + grpc_handshaker_factory_registry_init(); + grpc_security_init(); + for (i = 0; i < g_number_of_plugins; i++) { + if (g_all_of_the_plugins[i].init != NULL) { + g_all_of_the_plugins[i].init(); + } + } + /* register channel finalization AFTER all plugins, to ensure that it's run + * at the appropriate time */ + grpc_register_security_filters(); + register_builtin_channel_init(); + grpc_tracer_init("GRPC_TRACE"); + /* no more changes to channel init pipelines */ + grpc_channel_init_finalize(); + grpc_iomgr_start(&exec_ctx); + } + gpr_mu_unlock(&g_init_mu); + grpc_exec_ctx_finish(&exec_ctx); + GRPC_API_TRACE("grpc_init(void)", 0, ()); +} + +void grpc_shutdown(void) { + int i; + GRPC_API_TRACE("grpc_shutdown(void)", 0, ()); + grpc_exec_ctx exec_ctx = + GRPC_EXEC_CTX_INITIALIZER(0, grpc_never_ready_to_finish, NULL); + gpr_mu_lock(&g_init_mu); + if (--g_initializations == 0) { + grpc_executor_shutdown(&exec_ctx); + grpc_timer_manager_set_threading(false); // shutdown timer_manager thread + for (i = g_number_of_plugins; i >= 0; i--) { + if (g_all_of_the_plugins[i].destroy != NULL) { + g_all_of_the_plugins[i].destroy(); + } + } + grpc_iomgr_shutdown(&exec_ctx); + gpr_timers_global_destroy(); + grpc_tracer_shutdown(); + grpc_mdctx_global_shutdown(&exec_ctx); + grpc_handshaker_factory_registry_shutdown(&exec_ctx); + grpc_slice_intern_shutdown(); + grpc_stats_shutdown(); + } + gpr_mu_unlock(&g_init_mu); + grpc_exec_ctx_finish(&exec_ctx); +} + +int grpc_is_initialized(void) { + int r; + gpr_once_init(&g_basic_init, do_basic_init); + gpr_mu_lock(&g_init_mu); + r = g_initializations > 0; + gpr_mu_unlock(&g_init_mu); + return r; +} diff --git a/src/core/lib/surface/init_secure.c b/src/core/lib/surface/init_secure.c deleted file mode 100644 index 8fbde3d1b4..0000000000 --- a/src/core/lib/surface/init_secure.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include "src/core/lib/surface/init.h" - -#include -#include - -#include "src/core/lib/debug/trace.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/security/credentials/plugin/plugin_credentials.h" -#include "src/core/lib/security/transport/auth_filters.h" -#include "src/core/lib/security/transport/secure_endpoint.h" -#include "src/core/lib/security/transport/security_connector.h" -#include "src/core/lib/security/transport/security_handshaker.h" -#include "src/core/lib/surface/channel_init.h" -#include "src/core/tsi/transport_security_interface.h" - -#ifndef NDEBUG -#include "src/core/lib/security/context/security_context.h" -#endif - -void grpc_security_pre_init(void) { - grpc_register_tracer(&grpc_trace_secure_endpoint); - grpc_register_tracer(&tsi_tracing_enabled); -#ifndef NDEBUG - grpc_register_tracer(&grpc_trace_auth_context_refcount); - grpc_register_tracer(&grpc_trace_security_connector_refcount); -#endif -} - -static bool maybe_prepend_client_auth_filter( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { - const grpc_channel_args *args = - grpc_channel_stack_builder_get_channel_arguments(builder); - if (args) { - for (size_t i = 0; i < args->num_args; i++) { - if (0 == strcmp(GRPC_ARG_SECURITY_CONNECTOR, args->args[i].key)) { - return grpc_channel_stack_builder_prepend_filter( - builder, &grpc_client_auth_filter, NULL, NULL); - } - } - } - return true; -} - -static bool maybe_prepend_server_auth_filter( - grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { - const grpc_channel_args *args = - grpc_channel_stack_builder_get_channel_arguments(builder); - if (args) { - for (size_t i = 0; i < args->num_args; i++) { - if (0 == strcmp(GRPC_SERVER_CREDENTIALS_ARG, args->args[i].key)) { - return grpc_channel_stack_builder_prepend_filter( - builder, &grpc_server_auth_filter, NULL, NULL); - } - } - } - return true; -} - -void grpc_register_security_filters(void) { - grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX, - maybe_prepend_client_auth_filter, NULL); - grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX, - maybe_prepend_client_auth_filter, NULL); - grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, - maybe_prepend_server_auth_filter, NULL); -} - -void grpc_security_init() { - grpc_security_register_handshaker_factories(); - grpc_register_tracer(&grpc_plugin_credentials_trace); -} diff --git a/src/core/lib/surface/init_secure.cc b/src/core/lib/surface/init_secure.cc new file mode 100644 index 0000000000..8fbde3d1b4 --- /dev/null +++ b/src/core/lib/surface/init_secure.cc @@ -0,0 +1,91 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include "src/core/lib/surface/init.h" + +#include +#include + +#include "src/core/lib/debug/trace.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/security/credentials/plugin/plugin_credentials.h" +#include "src/core/lib/security/transport/auth_filters.h" +#include "src/core/lib/security/transport/secure_endpoint.h" +#include "src/core/lib/security/transport/security_connector.h" +#include "src/core/lib/security/transport/security_handshaker.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/tsi/transport_security_interface.h" + +#ifndef NDEBUG +#include "src/core/lib/security/context/security_context.h" +#endif + +void grpc_security_pre_init(void) { + grpc_register_tracer(&grpc_trace_secure_endpoint); + grpc_register_tracer(&tsi_tracing_enabled); +#ifndef NDEBUG + grpc_register_tracer(&grpc_trace_auth_context_refcount); + grpc_register_tracer(&grpc_trace_security_connector_refcount); +#endif +} + +static bool maybe_prepend_client_auth_filter( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { + const grpc_channel_args *args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (args) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(GRPC_ARG_SECURITY_CONNECTOR, args->args[i].key)) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_client_auth_filter, NULL, NULL); + } + } + } + return true; +} + +static bool maybe_prepend_server_auth_filter( + grpc_exec_ctx *exec_ctx, grpc_channel_stack_builder *builder, void *arg) { + const grpc_channel_args *args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (args) { + for (size_t i = 0; i < args->num_args; i++) { + if (0 == strcmp(GRPC_SERVER_CREDENTIALS_ARG, args->args[i].key)) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_server_auth_filter, NULL, NULL); + } + } + } + return true; +} + +void grpc_register_security_filters(void) { + grpc_channel_init_register_stage(GRPC_CLIENT_SUBCHANNEL, INT_MAX, + maybe_prepend_client_auth_filter, NULL); + grpc_channel_init_register_stage(GRPC_CLIENT_DIRECT_CHANNEL, INT_MAX, + maybe_prepend_client_auth_filter, NULL); + grpc_channel_init_register_stage(GRPC_SERVER_CHANNEL, INT_MAX, + maybe_prepend_server_auth_filter, NULL); +} + +void grpc_security_init() { + grpc_security_register_handshaker_factories(); + grpc_register_tracer(&grpc_plugin_credentials_trace); +} diff --git a/src/core/lib/surface/init_unsecure.c b/src/core/lib/surface/init_unsecure.c deleted file mode 100644 index b852cab985..0000000000 --- a/src/core/lib/surface/init_unsecure.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/init.h" - -void grpc_security_pre_init(void) {} - -void grpc_register_security_filters(void) {} - -void grpc_security_init(void) {} diff --git a/src/core/lib/surface/init_unsecure.cc b/src/core/lib/surface/init_unsecure.cc new file mode 100644 index 0000000000..b852cab985 --- /dev/null +++ b/src/core/lib/surface/init_unsecure.cc @@ -0,0 +1,25 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/init.h" + +void grpc_security_pre_init(void) {} + +void grpc_register_security_filters(void) {} + +void grpc_security_init(void) {} diff --git a/src/core/lib/surface/metadata_array.c b/src/core/lib/surface/metadata_array.c deleted file mode 100644 index 0afb8b4b87..0000000000 --- a/src/core/lib/surface/metadata_array.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include - -#include "src/core/lib/surface/api_trace.h" - -void grpc_metadata_array_init(grpc_metadata_array* array) { - GRPC_API_TRACE("grpc_metadata_array_init(array=%p)", 1, (array)); - memset(array, 0, sizeof(*array)); -} - -void grpc_metadata_array_destroy(grpc_metadata_array* array) { - GRPC_API_TRACE("grpc_metadata_array_destroy(array=%p)", 1, (array)); - gpr_free(array->metadata); -} diff --git a/src/core/lib/surface/metadata_array.cc b/src/core/lib/surface/metadata_array.cc new file mode 100644 index 0000000000..0afb8b4b87 --- /dev/null +++ b/src/core/lib/surface/metadata_array.cc @@ -0,0 +1,34 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include + +#include "src/core/lib/surface/api_trace.h" + +void grpc_metadata_array_init(grpc_metadata_array* array) { + GRPC_API_TRACE("grpc_metadata_array_init(array=%p)", 1, (array)); + memset(array, 0, sizeof(*array)); +} + +void grpc_metadata_array_destroy(grpc_metadata_array* array) { + GRPC_API_TRACE("grpc_metadata_array_destroy(array=%p)", 1, (array)); + gpr_free(array->metadata); +} diff --git a/src/core/lib/surface/server.c b/src/core/lib/surface/server.c deleted file mode 100644 index 1d0fd472d0..0000000000 --- a/src/core/lib/surface/server.c +++ /dev/null @@ -1,1554 +0,0 @@ -/* - * - * Copyright 2015-2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/surface/server.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/channel/connected_channel.h" -#include "src/core/lib/debug/stats.h" -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/iomgr/iomgr.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/support/stack_lockfree.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/surface/api_trace.h" -#include "src/core/lib/surface/call.h" -#include "src/core/lib/surface/channel.h" -#include "src/core/lib/surface/completion_queue.h" -#include "src/core/lib/surface/init.h" -#include "src/core/lib/transport/metadata.h" -#include "src/core/lib/transport/static_metadata.h" - -typedef struct listener { - void *arg; - void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_pollset **pollsets, size_t pollset_count); - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_closure *closure); - struct listener *next; - grpc_closure destroy_done; -} listener; - -typedef struct call_data call_data; -typedef struct channel_data channel_data; -typedef struct registered_method registered_method; - -typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type; - -grpc_tracer_flag grpc_server_channel_trace = - GRPC_TRACER_INITIALIZER(false, "server_channel"); - -typedef struct requested_call { - requested_call_type type; - size_t cq_idx; - void *tag; - grpc_server *server; - grpc_completion_queue *cq_bound_to_call; - grpc_call **call; - grpc_cq_completion completion; - grpc_metadata_array *initial_metadata; - union { - struct { - grpc_call_details *details; - } batch; - struct { - registered_method *method; - gpr_timespec *deadline; - grpc_byte_buffer **optional_payload; - } registered; - } data; -} requested_call; - -typedef struct channel_registered_method { - registered_method *server_registered_method; - uint32_t flags; - bool has_host; - grpc_slice method; - grpc_slice host; -} channel_registered_method; - -struct channel_data { - grpc_server *server; - grpc_connectivity_state connectivity_state; - grpc_channel *channel; - size_t cq_idx; - /* linked list of all channels on a server */ - channel_data *next; - channel_data *prev; - channel_registered_method *registered_methods; - uint32_t registered_method_slots; - uint32_t registered_method_max_probes; - grpc_closure finish_destroy_channel_closure; - grpc_closure channel_connectivity_changed; -}; - -typedef struct shutdown_tag { - void *tag; - grpc_completion_queue *cq; - grpc_cq_completion completion; -} shutdown_tag; - -typedef enum { - /* waiting for metadata */ - NOT_STARTED, - /* inital metadata read, not flow controlled in yet */ - PENDING, - /* flow controlled in, on completion queue */ - ACTIVATED, - /* cancelled before being queued */ - ZOMBIED -} call_state; - -typedef struct request_matcher request_matcher; - -struct call_data { - grpc_call *call; - - /** protects state */ - gpr_mu mu_state; - /** the current state of a call - see call_state */ - call_state state; - - bool path_set; - bool host_set; - grpc_slice path; - grpc_slice host; - gpr_timespec deadline; - - grpc_completion_queue *cq_new; - - grpc_metadata_batch *recv_initial_metadata; - uint32_t recv_initial_metadata_flags; - grpc_metadata_array initial_metadata; - - request_matcher *matcher; - grpc_byte_buffer *payload; - - grpc_closure got_initial_metadata; - grpc_closure server_on_recv_initial_metadata; - grpc_closure kill_zombie_closure; - grpc_closure *on_done_recv_initial_metadata; - - grpc_closure publish; - - call_data *pending_next; -}; - -struct request_matcher { - grpc_server *server; - call_data *pending_head; - call_data *pending_tail; - gpr_stack_lockfree **requests_per_cq; -}; - -struct registered_method { - char *method; - char *host; - grpc_server_register_method_payload_handling payload_handling; - uint32_t flags; - /* one request matcher per method */ - request_matcher matcher; - registered_method *next; -}; - -typedef struct { - grpc_channel **channels; - size_t num_channels; -} channel_broadcaster; - -struct grpc_server { - grpc_channel_args *channel_args; - - grpc_completion_queue **cqs; - grpc_pollset **pollsets; - size_t cq_count; - size_t pollset_count; - bool started; - - /* The two following mutexes control access to server-state - mu_global controls access to non-call-related state (e.g., channel state) - mu_call controls access to call-related state (e.g., the call lists) - - If they are ever required to be nested, you must lock mu_global - before mu_call. This is currently used in shutdown processing - (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */ - gpr_mu mu_global; /* mutex for server and channel state */ - gpr_mu mu_call; /* mutex for call-specific state */ - - /* startup synchronization: flag is protected by mu_global, signals whether - we are doing the listener start routine or not */ - bool starting; - gpr_cv starting_cv; - - registered_method *registered_methods; - /** one request matcher for unregistered methods */ - request_matcher unregistered_request_matcher; - /** free list of available requested_calls_per_cq indices */ - gpr_stack_lockfree **request_freelist_per_cq; - /** requested call backing data */ - requested_call **requested_calls_per_cq; - int max_requested_calls_per_cq; - - gpr_atm shutdown_flag; - uint8_t shutdown_published; - size_t num_shutdown_tags; - shutdown_tag *shutdown_tags; - - channel_data root_channel_data; - - listener *listeners; - int listeners_destroyed; - gpr_refcount internal_refcount; - - /** when did we print the last shutdown progress message */ - gpr_timespec last_shutdown_message_time; -}; - -#define SERVER_FROM_CALL_ELEM(elem) \ - (((channel_data *)(elem)->channel_data)->server) - -static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *calld, - grpc_error *error); -static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, - size_t cq_idx, requested_call *rc, grpc_error *error); -/* Before calling maybe_finish_shutdown, we must hold mu_global and not - hold mu_call */ -static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_server *server); - -/* - * channel broadcaster - */ - -/* assumes server locked */ -static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { - channel_data *c; - size_t count = 0; - for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { - count++; - } - cb->num_channels = count; - cb->channels = - (grpc_channel **)gpr_malloc(sizeof(*cb->channels) * cb->num_channels); - count = 0; - for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { - cb->channels[count++] = c->channel; - GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast"); - } -} - -struct shutdown_cleanup_args { - grpc_closure closure; - grpc_slice slice; -}; - -static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - struct shutdown_cleanup_args *a = (struct shutdown_cleanup_args *)arg; - grpc_slice_unref_internal(exec_ctx, a->slice); - gpr_free(a); -} - -static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel, - bool send_goaway, grpc_error *send_disconnect) { - struct shutdown_cleanup_args *sc = - (struct shutdown_cleanup_args *)gpr_malloc(sizeof(*sc)); - GRPC_CLOSURE_INIT(&sc->closure, shutdown_cleanup, sc, - grpc_schedule_on_exec_ctx); - grpc_transport_op *op = grpc_make_transport_op(&sc->closure); - grpc_channel_element *elem; - - op->goaway_error = - send_goaway ? grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK) - : GRPC_ERROR_NONE; - op->set_accept_stream = true; - sc->slice = grpc_slice_from_copied_string("Server shutdown"); - op->disconnect_with_error = send_disconnect; - - elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); - elem->filter->start_transport_op(exec_ctx, elem, op); -} - -static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx, - channel_broadcaster *cb, - bool send_goaway, - grpc_error *force_disconnect) { - size_t i; - - for (i = 0; i < cb->num_channels; i++) { - send_shutdown(exec_ctx, cb->channels[i], send_goaway, - GRPC_ERROR_REF(force_disconnect)); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, cb->channels[i], "broadcast"); - } - gpr_free(cb->channels); - GRPC_ERROR_UNREF(force_disconnect); -} - -/* - * request_matcher - */ - -static void request_matcher_init(request_matcher *rm, size_t entries, - grpc_server *server) { - memset(rm, 0, sizeof(*rm)); - rm->server = server; - rm->requests_per_cq = (gpr_stack_lockfree **)gpr_malloc( - sizeof(*rm->requests_per_cq) * server->cq_count); - for (size_t i = 0; i < server->cq_count; i++) { - rm->requests_per_cq[i] = gpr_stack_lockfree_create(entries); - } -} - -static void request_matcher_destroy(request_matcher *rm) { - for (size_t i = 0; i < rm->server->cq_count; i++) { - GPR_ASSERT(gpr_stack_lockfree_pop(rm->requests_per_cq[i]) == -1); - gpr_stack_lockfree_destroy(rm->requests_per_cq[i]); - } - gpr_free(rm->requests_per_cq); -} - -static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem, - grpc_error *error) { - grpc_call_unref(grpc_call_from_top_element((grpc_call_element *)elem)); -} - -static void request_matcher_zombify_all_pending_calls(grpc_exec_ctx *exec_ctx, - request_matcher *rm) { - while (rm->pending_head) { - call_data *calld = rm->pending_head; - rm->pending_head = calld->pending_next; - gpr_mu_lock(&calld->mu_state); - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - GRPC_CLOSURE_INIT( - &calld->kill_zombie_closure, kill_zombie, - grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE); - } -} - -static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx, - grpc_server *server, - request_matcher *rm, - grpc_error *error) { - int request_id; - for (size_t i = 0; i < server->cq_count; i++) { - while ((request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[i])) != - -1) { - fail_call(exec_ctx, server, i, - &server->requested_calls_per_cq[i][request_id], - GRPC_ERROR_REF(error)); - } - } - GRPC_ERROR_UNREF(error); -} - -/* - * server proper - */ - -static void server_ref(grpc_server *server) { - gpr_ref(&server->internal_refcount); -} - -static void server_delete(grpc_exec_ctx *exec_ctx, grpc_server *server) { - registered_method *rm; - size_t i; - grpc_channel_args_destroy(exec_ctx, server->channel_args); - gpr_mu_destroy(&server->mu_global); - gpr_mu_destroy(&server->mu_call); - gpr_cv_destroy(&server->starting_cv); - while ((rm = server->registered_methods) != NULL) { - server->registered_methods = rm->next; - if (server->started) { - request_matcher_destroy(&rm->matcher); - } - gpr_free(rm->method); - gpr_free(rm->host); - gpr_free(rm); - } - if (server->started) { - request_matcher_destroy(&server->unregistered_request_matcher); - } - for (i = 0; i < server->cq_count; i++) { - GRPC_CQ_INTERNAL_UNREF(exec_ctx, server->cqs[i], "server"); - if (server->started) { - gpr_stack_lockfree_destroy(server->request_freelist_per_cq[i]); - gpr_free(server->requested_calls_per_cq[i]); - } - } - gpr_free(server->request_freelist_per_cq); - gpr_free(server->requested_calls_per_cq); - gpr_free(server->cqs); - gpr_free(server->pollsets); - gpr_free(server->shutdown_tags); - gpr_free(server); -} - -static void server_unref(grpc_exec_ctx *exec_ctx, grpc_server *server) { - if (gpr_unref(&server->internal_refcount)) { - server_delete(exec_ctx, server); - } -} - -static int is_channel_orphaned(channel_data *chand) { - return chand->next == chand; -} - -static void orphan_channel(channel_data *chand) { - chand->next->prev = chand->prev; - chand->prev->next = chand->next; - chand->next = chand->prev = chand; -} - -static void finish_destroy_channel(grpc_exec_ctx *exec_ctx, void *cd, - grpc_error *error) { - channel_data *chand = (channel_data *)cd; - grpc_server *server = chand->server; - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "server"); - server_unref(exec_ctx, server); -} - -static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand, - grpc_error *error) { - if (is_channel_orphaned(chand)) return; - GPR_ASSERT(chand->server != NULL); - orphan_channel(chand); - server_ref(chand->server); - maybe_finish_shutdown(exec_ctx, chand->server); - GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure, - finish_destroy_channel, chand, grpc_schedule_on_exec_ctx); - - if (GRPC_TRACER_ON(grpc_server_channel_trace) && error != GRPC_ERROR_NONE) { - const char *msg = grpc_error_string(error); - gpr_log(GPR_INFO, "Disconnected client: %s", msg); - } - GRPC_ERROR_UNREF(error); - - grpc_transport_op *op = - grpc_make_transport_op(&chand->finish_destroy_channel_closure); - op->set_accept_stream = true; - grpc_channel_next_op(exec_ctx, - grpc_channel_stack_element( - grpc_channel_get_channel_stack(chand->channel), 0), - op); -} - -static void done_request_event(grpc_exec_ctx *exec_ctx, void *req, - grpc_cq_completion *c) { - requested_call *rc = (requested_call *)req; - grpc_server *server = rc->server; - - if (rc >= server->requested_calls_per_cq[rc->cq_idx] && - rc < server->requested_calls_per_cq[rc->cq_idx] + - server->max_requested_calls_per_cq) { - GPR_ASSERT(rc - server->requested_calls_per_cq[rc->cq_idx] <= INT_MAX); - gpr_stack_lockfree_push( - server->request_freelist_per_cq[rc->cq_idx], - (int)(rc - server->requested_calls_per_cq[rc->cq_idx])); - } else { - gpr_free(req); - } - - server_unref(exec_ctx, server); -} - -static void publish_call(grpc_exec_ctx *exec_ctx, grpc_server *server, - call_data *calld, size_t cq_idx, requested_call *rc) { - grpc_call_set_completion_queue(exec_ctx, calld->call, rc->cq_bound_to_call); - grpc_call *call = calld->call; - *rc->call = call; - calld->cq_new = server->cqs[cq_idx]; - GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata); - switch (rc->type) { - case BATCH_CALL: - GPR_ASSERT(calld->host_set); - GPR_ASSERT(calld->path_set); - rc->data.batch.details->host = grpc_slice_ref_internal(calld->host); - rc->data.batch.details->method = grpc_slice_ref_internal(calld->path); - rc->data.batch.details->deadline = calld->deadline; - rc->data.batch.details->flags = calld->recv_initial_metadata_flags; - break; - case REGISTERED_CALL: - *rc->data.registered.deadline = calld->deadline; - if (rc->data.registered.optional_payload) { - *rc->data.registered.optional_payload = calld->payload; - calld->payload = NULL; - } - break; - default: - GPR_UNREACHABLE_CODE(return ); - } - - grpc_call_element *elem = - grpc_call_stack_element(grpc_call_get_call_stack(call), 0); - channel_data *chand = (channel_data *)elem->channel_data; - server_ref(chand->server); - grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, GRPC_ERROR_NONE, - done_request_event, rc, &rc->completion); -} - -static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - grpc_call_element *call_elem = (grpc_call_element *)arg; - call_data *calld = (call_data *)call_elem->call_data; - channel_data *chand = (channel_data *)call_elem->channel_data; - request_matcher *rm = calld->matcher; - grpc_server *server = rm->server; - - if (error != GRPC_ERROR_NONE || gpr_atm_acq_load(&server->shutdown_flag)) { - gpr_mu_lock(&calld->mu_state); - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - GRPC_CLOSURE_INIT( - &calld->kill_zombie_closure, kill_zombie, - grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, - GRPC_ERROR_REF(error)); - return; - } - - for (size_t i = 0; i < server->cq_count; i++) { - size_t cq_idx = (chand->cq_idx + i) % server->cq_count; - int request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[cq_idx]); - if (request_id == -1) { - continue; - } else { - GRPC_STATS_INC_SERVER_CQS_CHECKED(exec_ctx, i); - gpr_mu_lock(&calld->mu_state); - calld->state = ACTIVATED; - gpr_mu_unlock(&calld->mu_state); - publish_call(exec_ctx, server, calld, cq_idx, - &server->requested_calls_per_cq[cq_idx][request_id]); - return; /* early out */ - } - } - - /* no cq to take the request found: queue it on the slow list */ - GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED(exec_ctx); - gpr_mu_lock(&server->mu_call); - gpr_mu_lock(&calld->mu_state); - calld->state = PENDING; - gpr_mu_unlock(&calld->mu_state); - if (rm->pending_head == NULL) { - rm->pending_tail = rm->pending_head = calld; - } else { - rm->pending_tail->pending_next = calld; - rm->pending_tail = calld; - } - calld->pending_next = NULL; - gpr_mu_unlock(&server->mu_call); -} - -static void finish_start_new_rpc( - grpc_exec_ctx *exec_ctx, grpc_server *server, grpc_call_element *elem, - request_matcher *rm, - grpc_server_register_method_payload_handling payload_handling) { - call_data *calld = (call_data *)elem->call_data; - - if (gpr_atm_acq_load(&server->shutdown_flag)) { - gpr_mu_lock(&calld->mu_state); - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE); - return; - } - - calld->matcher = rm; - - switch (payload_handling) { - case GRPC_SRM_PAYLOAD_NONE: - publish_new_rpc(exec_ctx, elem, GRPC_ERROR_NONE); - break; - case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: { - grpc_op op; - memset(&op, 0, sizeof(op)); - op.op = GRPC_OP_RECV_MESSAGE; - op.data.recv_message.recv_message = &calld->payload; - GRPC_CLOSURE_INIT(&calld->publish, publish_new_rpc, elem, - grpc_schedule_on_exec_ctx); - grpc_call_start_batch_and_execute(exec_ctx, calld->call, &op, 1, - &calld->publish); - break; - } - } -} - -static void start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - grpc_server *server = chand->server; - uint32_t i; - uint32_t hash; - channel_registered_method *rm; - - if (chand->registered_methods && calld->path_set && calld->host_set) { - /* TODO(ctiller): unify these two searches */ - /* check for an exact match with host */ - hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(calld->host), - grpc_slice_hash(calld->path)); - for (i = 0; i <= chand->registered_method_max_probes; i++) { - rm = &chand->registered_methods[(hash + i) % - chand->registered_method_slots]; - if (!rm) break; - if (!rm->has_host) continue; - if (!grpc_slice_eq(rm->host, calld->host)) continue; - if (!grpc_slice_eq(rm->method, calld->path)) continue; - if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && - 0 == (calld->recv_initial_metadata_flags & - GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { - continue; - } - finish_start_new_rpc(exec_ctx, server, elem, - &rm->server_registered_method->matcher, - rm->server_registered_method->payload_handling); - return; - } - /* check for a wildcard method definition (no host set) */ - hash = GRPC_MDSTR_KV_HASH(0, grpc_slice_hash(calld->path)); - for (i = 0; i <= chand->registered_method_max_probes; i++) { - rm = &chand->registered_methods[(hash + i) % - chand->registered_method_slots]; - if (!rm) break; - if (rm->has_host) continue; - if (!grpc_slice_eq(rm->method, calld->path)) continue; - if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && - 0 == (calld->recv_initial_metadata_flags & - GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { - continue; - } - finish_start_new_rpc(exec_ctx, server, elem, - &rm->server_registered_method->matcher, - rm->server_registered_method->payload_handling); - return; - } - } - finish_start_new_rpc(exec_ctx, server, elem, - &server->unregistered_request_matcher, - GRPC_SRM_PAYLOAD_NONE); -} - -static int num_listeners(grpc_server *server) { - listener *l; - int n = 0; - for (l = server->listeners; l; l = l->next) { - n++; - } - return n; -} - -static void done_shutdown_event(grpc_exec_ctx *exec_ctx, void *server, - grpc_cq_completion *completion) { - server_unref(exec_ctx, (grpc_server *)server); -} - -static int num_channels(grpc_server *server) { - channel_data *chand; - int n = 0; - for (chand = server->root_channel_data.next; - chand != &server->root_channel_data; chand = chand->next) { - n++; - } - return n; -} - -static void kill_pending_work_locked(grpc_exec_ctx *exec_ctx, - grpc_server *server, grpc_error *error) { - if (server->started) { - request_matcher_kill_requests(exec_ctx, server, - &server->unregistered_request_matcher, - GRPC_ERROR_REF(error)); - request_matcher_zombify_all_pending_calls( - exec_ctx, &server->unregistered_request_matcher); - for (registered_method *rm = server->registered_methods; rm; - rm = rm->next) { - request_matcher_kill_requests(exec_ctx, server, &rm->matcher, - GRPC_ERROR_REF(error)); - request_matcher_zombify_all_pending_calls(exec_ctx, &rm->matcher); - } - } - GRPC_ERROR_UNREF(error); -} - -static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, - grpc_server *server) { - size_t i; - if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) { - return; - } - - kill_pending_work_locked( - exec_ctx, server, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); - - if (server->root_channel_data.next != &server->root_channel_data || - server->listeners_destroyed < num_listeners(server)) { - if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), - server->last_shutdown_message_time), - gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { - server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); - gpr_log(GPR_DEBUG, - "Waiting for %d channels and %d/%d listeners to be destroyed" - " before shutting down server", - num_channels(server), - num_listeners(server) - server->listeners_destroyed, - num_listeners(server)); - } - return; - } - server->shutdown_published = 1; - for (i = 0; i < server->num_shutdown_tags; i++) { - server_ref(server); - grpc_cq_end_op(exec_ctx, server->shutdown_tags[i].cq, - server->shutdown_tags[i].tag, GRPC_ERROR_NONE, - done_shutdown_event, server, - &server->shutdown_tags[i].completion); - } -} - -static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)ptr; - call_data *calld = (call_data *)elem->call_data; - gpr_timespec op_deadline; - - if (error == GRPC_ERROR_NONE) { - GPR_ASSERT(calld->recv_initial_metadata->idx.named.path != NULL); - GPR_ASSERT(calld->recv_initial_metadata->idx.named.authority != NULL); - calld->path = grpc_slice_ref_internal( - GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md)); - calld->host = grpc_slice_ref_internal( - GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.authority->md)); - calld->path_set = true; - calld->host_set = true; - grpc_metadata_batch_remove(exec_ctx, calld->recv_initial_metadata, - calld->recv_initial_metadata->idx.named.path); - grpc_metadata_batch_remove( - exec_ctx, calld->recv_initial_metadata, - calld->recv_initial_metadata->idx.named.authority); - } else { - GRPC_ERROR_REF(error); - } - op_deadline = calld->recv_initial_metadata->deadline; - if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) { - calld->deadline = op_deadline; - } - if (calld->host_set && calld->path_set) { - /* do nothing */ - } else { - grpc_error *src_error = error; - error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( - "Missing :authority or :path", &error, 1); - GRPC_ERROR_UNREF(src_error); - } - - GRPC_CLOSURE_RUN(exec_ctx, calld->on_done_recv_initial_metadata, error); -} - -static void server_mutate_op(grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - call_data *calld = (call_data *)elem->call_data; - - if (op->recv_initial_metadata) { - GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags == NULL); - calld->recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata; - calld->on_done_recv_initial_metadata = - op->payload->recv_initial_metadata.recv_initial_metadata_ready; - op->payload->recv_initial_metadata.recv_initial_metadata_ready = - &calld->server_on_recv_initial_metadata; - op->payload->recv_initial_metadata.recv_flags = - &calld->recv_initial_metadata_flags; - } -} - -static void server_start_transport_stream_op_batch( - grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - server_mutate_op(elem, op); - grpc_call_next_op(exec_ctx, elem, op); -} - -static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr, - grpc_error *error) { - grpc_call_element *elem = (grpc_call_element *)ptr; - call_data *calld = (call_data *)elem->call_data; - if (error == GRPC_ERROR_NONE) { - start_new_rpc(exec_ctx, elem); - } else { - gpr_mu_lock(&calld->mu_state); - if (calld->state == NOT_STARTED) { - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem, - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, - GRPC_ERROR_NONE); - } else if (calld->state == PENDING) { - calld->state = ZOMBIED; - gpr_mu_unlock(&calld->mu_state); - /* zombied call will be destroyed when it's removed from the pending - queue... later */ - } else { - gpr_mu_unlock(&calld->mu_state); - } - } -} - -static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd, - grpc_transport *transport, - const void *transport_server_data) { - channel_data *chand = (channel_data *)cd; - /* create a call */ - grpc_call_create_args args; - memset(&args, 0, sizeof(args)); - args.channel = chand->channel; - args.server_transport_data = transport_server_data; - args.send_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); - grpc_call *call; - grpc_error *error = grpc_call_create(exec_ctx, &args, &call); - grpc_call_element *elem = - grpc_call_stack_element(grpc_call_get_call_stack(call), 0); - if (error != GRPC_ERROR_NONE) { - got_initial_metadata(exec_ctx, elem, error); - GRPC_ERROR_UNREF(error); - return; - } - call_data *calld = (call_data *)elem->call_data; - grpc_op op; - memset(&op, 0, sizeof(op)); - op.op = GRPC_OP_RECV_INITIAL_METADATA; - op.data.recv_initial_metadata.recv_initial_metadata = - &calld->initial_metadata; - GRPC_CLOSURE_INIT(&calld->got_initial_metadata, got_initial_metadata, elem, - grpc_schedule_on_exec_ctx); - grpc_call_start_batch_and_execute(exec_ctx, call, &op, 1, - &calld->got_initial_metadata); -} - -static void channel_connectivity_changed(grpc_exec_ctx *exec_ctx, void *cd, - grpc_error *error) { - channel_data *chand = (channel_data *)cd; - grpc_server *server = chand->server; - if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { - grpc_transport_op *op = grpc_make_transport_op(NULL); - op->on_connectivity_state_change = &chand->channel_connectivity_changed, - op->connectivity_state = &chand->connectivity_state; - grpc_channel_next_op(exec_ctx, - grpc_channel_stack_element( - grpc_channel_get_channel_stack(chand->channel), 0), - op); - } else { - gpr_mu_lock(&server->mu_global); - destroy_channel(exec_ctx, chand, GRPC_ERROR_REF(error)); - gpr_mu_unlock(&server->mu_global); - GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "connectivity"); - } -} - -static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, - grpc_call_element *elem, - const grpc_call_element_args *args) { - call_data *calld = (call_data *)elem->call_data; - channel_data *chand = (channel_data *)elem->channel_data; - memset(calld, 0, sizeof(call_data)); - calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); - calld->call = grpc_call_from_top_element(elem); - gpr_mu_init(&calld->mu_state); - - GRPC_CLOSURE_INIT(&calld->server_on_recv_initial_metadata, - server_on_recv_initial_metadata, elem, - grpc_schedule_on_exec_ctx); - - server_ref(chand->server); - return GRPC_ERROR_NONE; -} - -static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, - const grpc_call_final_info *final_info, - grpc_closure *ignored) { - channel_data *chand = (channel_data *)elem->channel_data; - call_data *calld = (call_data *)elem->call_data; - - GPR_ASSERT(calld->state != PENDING); - - if (calld->host_set) { - grpc_slice_unref_internal(exec_ctx, calld->host); - } - if (calld->path_set) { - grpc_slice_unref_internal(exec_ctx, calld->path); - } - grpc_metadata_array_destroy(&calld->initial_metadata); - grpc_byte_buffer_destroy(calld->payload); - - gpr_mu_destroy(&calld->mu_state); - - server_unref(exec_ctx, chand->server); -} - -static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem, - grpc_channel_element_args *args) { - channel_data *chand = (channel_data *)elem->channel_data; - GPR_ASSERT(args->is_first); - GPR_ASSERT(!args->is_last); - chand->server = NULL; - chand->channel = NULL; - chand->next = chand->prev = chand; - chand->registered_methods = NULL; - chand->connectivity_state = GRPC_CHANNEL_IDLE; - GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed, - channel_connectivity_changed, chand, - grpc_schedule_on_exec_ctx); - return GRPC_ERROR_NONE; -} - -static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, - grpc_channel_element *elem) { - size_t i; - channel_data *chand = (channel_data *)elem->channel_data; - if (chand->registered_methods) { - for (i = 0; i < chand->registered_method_slots; i++) { - grpc_slice_unref_internal(exec_ctx, chand->registered_methods[i].method); - if (chand->registered_methods[i].has_host) { - grpc_slice_unref_internal(exec_ctx, chand->registered_methods[i].host); - } - } - gpr_free(chand->registered_methods); - } - if (chand->server) { - gpr_mu_lock(&chand->server->mu_global); - chand->next->prev = chand->prev; - chand->prev->next = chand->next; - chand->next = chand->prev = chand; - maybe_finish_shutdown(exec_ctx, chand->server); - gpr_mu_unlock(&chand->server->mu_global); - server_unref(exec_ctx, chand->server); - } -} - -const grpc_channel_filter grpc_server_top_filter = { - server_start_transport_stream_op_batch, - grpc_channel_next_op, - sizeof(call_data), - init_call_elem, - grpc_call_stack_ignore_set_pollset_or_pollset_set, - destroy_call_elem, - sizeof(channel_data), - init_channel_elem, - destroy_channel_elem, - grpc_channel_next_get_info, - "server", -}; - -static void register_completion_queue(grpc_server *server, - grpc_completion_queue *cq, - void *reserved) { - size_t i, n; - GPR_ASSERT(!reserved); - for (i = 0; i < server->cq_count; i++) { - if (server->cqs[i] == cq) return; - } - - GRPC_CQ_INTERNAL_REF(cq, "server"); - n = server->cq_count++; - server->cqs = (grpc_completion_queue **)gpr_realloc( - server->cqs, server->cq_count * sizeof(grpc_completion_queue *)); - server->cqs[n] = cq; -} - -void grpc_server_register_completion_queue(grpc_server *server, - grpc_completion_queue *cq, - void *reserved) { - GRPC_API_TRACE( - "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3, - (server, cq, reserved)); - - if (grpc_get_cq_completion_type(cq) != GRPC_CQ_NEXT) { - gpr_log(GPR_INFO, - "Completion queue which is not of type GRPC_CQ_NEXT is being " - "registered as a server-completion-queue"); - /* Ideally we should log an error and abort but ruby-wrapped-language API - calls grpc_completion_queue_pluck() on server completion queues */ - } - - register_completion_queue(server, cq, reserved); -} - -grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { - GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved)); - - grpc_server *server = (grpc_server *)gpr_zalloc(sizeof(grpc_server)); - - gpr_mu_init(&server->mu_global); - gpr_mu_init(&server->mu_call); - gpr_cv_init(&server->starting_cv); - - /* decremented by grpc_server_destroy */ - gpr_ref_init(&server->internal_refcount, 1); - server->root_channel_data.next = server->root_channel_data.prev = - &server->root_channel_data; - - /* TODO(ctiller): expose a channel_arg for this */ - server->max_requested_calls_per_cq = 32768; - server->channel_args = grpc_channel_args_copy(args); - - return server; -} - -static int streq(const char *a, const char *b) { - if (a == NULL && b == NULL) return 1; - if (a == NULL) return 0; - if (b == NULL) return 0; - return 0 == strcmp(a, b); -} - -void *grpc_server_register_method( - grpc_server *server, const char *method, const char *host, - grpc_server_register_method_payload_handling payload_handling, - uint32_t flags) { - registered_method *m; - GRPC_API_TRACE( - "grpc_server_register_method(server=%p, method=%s, host=%s, " - "flags=0x%08x)", - 4, (server, method, host, flags)); - if (!method) { - gpr_log(GPR_ERROR, - "grpc_server_register_method method string cannot be NULL"); - return NULL; - } - for (m = server->registered_methods; m; m = m->next) { - if (streq(m->method, method) && streq(m->host, host)) { - gpr_log(GPR_ERROR, "duplicate registration for %s@%s", method, - host ? host : "*"); - return NULL; - } - } - if ((flags & ~GRPC_INITIAL_METADATA_USED_MASK) != 0) { - gpr_log(GPR_ERROR, "grpc_server_register_method invalid flags 0x%08x", - flags); - return NULL; - } - m = (registered_method *)gpr_zalloc(sizeof(registered_method)); - m->method = gpr_strdup(method); - m->host = gpr_strdup(host); - m->next = server->registered_methods; - m->payload_handling = payload_handling; - m->flags = flags; - server->registered_methods = m; - return m; -} - -static void start_listeners(grpc_exec_ctx *exec_ctx, void *s, - grpc_error *error) { - grpc_server *server = (grpc_server *)s; - for (listener *l = server->listeners; l; l = l->next) { - l->start(exec_ctx, server, l->arg, server->pollsets, server->pollset_count); - } - - gpr_mu_lock(&server->mu_global); - server->starting = false; - gpr_cv_signal(&server->starting_cv); - gpr_mu_unlock(&server->mu_global); - - server_unref(exec_ctx, server); -} - -void grpc_server_start(grpc_server *server) { - size_t i; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server)); - - server->started = true; - server->pollset_count = 0; - server->pollsets = - (grpc_pollset **)gpr_malloc(sizeof(grpc_pollset *) * server->cq_count); - server->request_freelist_per_cq = (gpr_stack_lockfree **)gpr_malloc( - sizeof(*server->request_freelist_per_cq) * server->cq_count); - server->requested_calls_per_cq = (requested_call **)gpr_malloc( - sizeof(*server->requested_calls_per_cq) * server->cq_count); - for (i = 0; i < server->cq_count; i++) { - if (grpc_cq_can_listen(server->cqs[i])) { - server->pollsets[server->pollset_count++] = - grpc_cq_pollset(server->cqs[i]); - } - server->request_freelist_per_cq[i] = - gpr_stack_lockfree_create((size_t)server->max_requested_calls_per_cq); - for (int j = 0; j < server->max_requested_calls_per_cq; j++) { - gpr_stack_lockfree_push(server->request_freelist_per_cq[i], j); - } - server->requested_calls_per_cq[i] = (requested_call *)gpr_malloc( - (size_t)server->max_requested_calls_per_cq * - sizeof(*server->requested_calls_per_cq[i])); - } - request_matcher_init(&server->unregistered_request_matcher, - (size_t)server->max_requested_calls_per_cq, server); - for (registered_method *rm = server->registered_methods; rm; rm = rm->next) { - request_matcher_init(&rm->matcher, - (size_t)server->max_requested_calls_per_cq, server); - } - - server_ref(server); - server->starting = true; - GRPC_CLOSURE_SCHED( - &exec_ctx, - GRPC_CLOSURE_CREATE(start_listeners, server, - grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)), - GRPC_ERROR_NONE); - - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_get_pollsets(grpc_server *server, grpc_pollset ***pollsets, - size_t *pollset_count) { - *pollset_count = server->pollset_count; - *pollsets = server->pollsets; -} - -void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s, - grpc_transport *transport, - grpc_pollset *accepting_pollset, - const grpc_channel_args *args) { - size_t num_registered_methods; - size_t alloc; - registered_method *rm; - channel_registered_method *crm; - grpc_channel *channel; - channel_data *chand; - uint32_t hash; - size_t slots; - uint32_t probes; - uint32_t max_probes = 0; - grpc_transport_op *op = NULL; - - channel = - grpc_channel_create(exec_ctx, NULL, args, GRPC_SERVER_CHANNEL, transport); - chand = (channel_data *)grpc_channel_stack_element( - grpc_channel_get_channel_stack(channel), 0) - ->channel_data; - chand->server = s; - server_ref(s); - chand->channel = channel; - - size_t cq_idx; - for (cq_idx = 0; cq_idx < s->cq_count; cq_idx++) { - if (grpc_cq_pollset(s->cqs[cq_idx]) == accepting_pollset) break; - } - if (cq_idx == s->cq_count) { - /* completion queue not found: pick a random one to publish new calls to */ - cq_idx = (size_t)rand() % s->cq_count; - } - chand->cq_idx = cq_idx; - - num_registered_methods = 0; - for (rm = s->registered_methods; rm; rm = rm->next) { - num_registered_methods++; - } - /* build a lookup table phrased in terms of mdstr's in this channels context - to quickly find registered methods */ - if (num_registered_methods > 0) { - slots = 2 * num_registered_methods; - alloc = sizeof(channel_registered_method) * slots; - chand->registered_methods = (channel_registered_method *)gpr_zalloc(alloc); - for (rm = s->registered_methods; rm; rm = rm->next) { - grpc_slice host; - bool has_host; - grpc_slice method; - if (rm->host != NULL) { - host = grpc_slice_intern(grpc_slice_from_static_string(rm->host)); - has_host = true; - } else { - has_host = false; - } - method = grpc_slice_intern(grpc_slice_from_static_string(rm->method)); - hash = GRPC_MDSTR_KV_HASH(has_host ? grpc_slice_hash(host) : 0, - grpc_slice_hash(method)); - for (probes = 0; chand->registered_methods[(hash + probes) % slots] - .server_registered_method != NULL; - probes++) - ; - if (probes > max_probes) max_probes = probes; - crm = &chand->registered_methods[(hash + probes) % slots]; - crm->server_registered_method = rm; - crm->flags = rm->flags; - crm->has_host = has_host; - if (has_host) { - crm->host = host; - } - crm->method = method; - } - GPR_ASSERT(slots <= UINT32_MAX); - chand->registered_method_slots = (uint32_t)slots; - chand->registered_method_max_probes = max_probes; - } - - gpr_mu_lock(&s->mu_global); - chand->next = &s->root_channel_data; - chand->prev = chand->next->prev; - chand->next->prev = chand->prev->next = chand; - gpr_mu_unlock(&s->mu_global); - - GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity"); - op = grpc_make_transport_op(NULL); - op->set_accept_stream = true; - op->set_accept_stream_fn = accept_stream; - op->set_accept_stream_user_data = chand; - op->on_connectivity_state_change = &chand->channel_connectivity_changed; - op->connectivity_state = &chand->connectivity_state; - if (gpr_atm_acq_load(&s->shutdown_flag) != 0) { - op->disconnect_with_error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"); - } - grpc_transport_perform_op(exec_ctx, transport, op); -} - -void done_published_shutdown(grpc_exec_ctx *exec_ctx, void *done_arg, - grpc_cq_completion *storage) { - (void)done_arg; - gpr_free(storage); -} - -static void listener_destroy_done(grpc_exec_ctx *exec_ctx, void *s, - grpc_error *error) { - grpc_server *server = (grpc_server *)s; - gpr_mu_lock(&server->mu_global); - server->listeners_destroyed++; - maybe_finish_shutdown(exec_ctx, server); - gpr_mu_unlock(&server->mu_global); -} - -void grpc_server_shutdown_and_notify(grpc_server *server, - grpc_completion_queue *cq, void *tag) { - listener *l; - shutdown_tag *sdt; - channel_broadcaster broadcaster; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3, - (server, cq, tag)); - - /* wait for startup to be finished: locks mu_global */ - gpr_mu_lock(&server->mu_global); - while (server->starting) { - gpr_cv_wait(&server->starting_cv, &server->mu_global, - gpr_inf_future(GPR_CLOCK_REALTIME)); - } - - /* stay locked, and gather up some stuff to do */ - GPR_ASSERT(grpc_cq_begin_op(cq, tag)); - if (server->shutdown_published) { - grpc_cq_end_op( - &exec_ctx, cq, tag, GRPC_ERROR_NONE, done_published_shutdown, NULL, - (grpc_cq_completion *)gpr_malloc(sizeof(grpc_cq_completion))); - gpr_mu_unlock(&server->mu_global); - goto done; - } - server->shutdown_tags = (shutdown_tag *)gpr_realloc( - server->shutdown_tags, - sizeof(shutdown_tag) * (server->num_shutdown_tags + 1)); - sdt = &server->shutdown_tags[server->num_shutdown_tags++]; - sdt->tag = tag; - sdt->cq = cq; - if (gpr_atm_acq_load(&server->shutdown_flag)) { - gpr_mu_unlock(&server->mu_global); - goto done; - } - - server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); - - channel_broadcaster_init(server, &broadcaster); - - gpr_atm_rel_store(&server->shutdown_flag, 1); - - /* collect all unregistered then registered calls */ - gpr_mu_lock(&server->mu_call); - kill_pending_work_locked( - &exec_ctx, server, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); - gpr_mu_unlock(&server->mu_call); - - maybe_finish_shutdown(&exec_ctx, server); - gpr_mu_unlock(&server->mu_global); - - /* Shutdown listeners */ - for (l = server->listeners; l; l = l->next) { - GRPC_CLOSURE_INIT(&l->destroy_done, listener_destroy_done, server, - grpc_schedule_on_exec_ctx); - l->destroy(&exec_ctx, server, l->arg, &l->destroy_done); - } - - channel_broadcaster_shutdown(&exec_ctx, &broadcaster, true /* send_goaway */, - GRPC_ERROR_NONE); - -done: - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_cancel_all_calls(grpc_server *server) { - channel_broadcaster broadcaster; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server)); - - gpr_mu_lock(&server->mu_global); - channel_broadcaster_init(server, &broadcaster); - gpr_mu_unlock(&server->mu_global); - - channel_broadcaster_shutdown( - &exec_ctx, &broadcaster, false /* send_goaway */, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Cancelling all calls")); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_destroy(grpc_server *server) { - listener *l; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - - GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server)); - - gpr_mu_lock(&server->mu_global); - GPR_ASSERT(gpr_atm_acq_load(&server->shutdown_flag) || !server->listeners); - GPR_ASSERT(server->listeners_destroyed == num_listeners(server)); - - while (server->listeners) { - l = server->listeners; - server->listeners = l->next; - gpr_free(l); - } - - gpr_mu_unlock(&server->mu_global); - - server_unref(&exec_ctx, server); - grpc_exec_ctx_finish(&exec_ctx); -} - -void grpc_server_add_listener( - grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_pollset **pollsets, size_t pollset_count), - void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, - grpc_closure *on_done)) { - listener *l = (listener *)gpr_malloc(sizeof(listener)); - l->arg = arg; - l->start = start; - l->destroy = destroy; - l->next = server->listeners; - server->listeners = l; -} - -static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx, - grpc_server *server, size_t cq_idx, - requested_call *rc) { - call_data *calld = NULL; - request_matcher *rm = NULL; - int request_id; - if (gpr_atm_acq_load(&server->shutdown_flag)) { - fail_call(exec_ctx, server, cq_idx, rc, - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); - return GRPC_CALL_OK; - } - request_id = gpr_stack_lockfree_pop(server->request_freelist_per_cq[cq_idx]); - if (request_id == -1) { - /* out of request ids: just fail this one */ - fail_call(exec_ctx, server, cq_idx, rc, - grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Out of request ids"), - GRPC_ERROR_INT_LIMIT, server->max_requested_calls_per_cq)); - return GRPC_CALL_OK; - } - switch (rc->type) { - case BATCH_CALL: - rm = &server->unregistered_request_matcher; - break; - case REGISTERED_CALL: - rm = &rc->data.registered.method->matcher; - break; - } - server->requested_calls_per_cq[cq_idx][request_id] = *rc; - gpr_free(rc); - if (gpr_stack_lockfree_push(rm->requests_per_cq[cq_idx], request_id)) { - /* this was the first queued request: we need to lock and start - matching calls */ - gpr_mu_lock(&server->mu_call); - while ((calld = rm->pending_head) != NULL) { - request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[cq_idx]); - if (request_id == -1) break; - rm->pending_head = calld->pending_next; - gpr_mu_unlock(&server->mu_call); - gpr_mu_lock(&calld->mu_state); - if (calld->state == ZOMBIED) { - gpr_mu_unlock(&calld->mu_state); - GRPC_CLOSURE_INIT( - &calld->kill_zombie_closure, kill_zombie, - grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), - grpc_schedule_on_exec_ctx); - GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, - GRPC_ERROR_NONE); - } else { - GPR_ASSERT(calld->state == PENDING); - calld->state = ACTIVATED; - gpr_mu_unlock(&calld->mu_state); - publish_call(exec_ctx, server, calld, cq_idx, - &server->requested_calls_per_cq[cq_idx][request_id]); - } - gpr_mu_lock(&server->mu_call); - } - gpr_mu_unlock(&server->mu_call); - } - return GRPC_CALL_OK; -} - -grpc_call_error grpc_server_request_call( - grpc_server *server, grpc_call **call, grpc_call_details *details, - grpc_metadata_array *initial_metadata, - grpc_completion_queue *cq_bound_to_call, - grpc_completion_queue *cq_for_notification, void *tag) { - grpc_call_error error; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - requested_call *rc = (requested_call *)gpr_malloc(sizeof(*rc)); - GRPC_STATS_INC_SERVER_REQUESTED_CALLS(&exec_ctx); - GRPC_API_TRACE( - "grpc_server_request_call(" - "server=%p, call=%p, details=%p, initial_metadata=%p, " - "cq_bound_to_call=%p, cq_for_notification=%p, tag=%p)", - 7, (server, call, details, initial_metadata, cq_bound_to_call, - cq_for_notification, tag)); - size_t cq_idx; - for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) { - if (server->cqs[cq_idx] == cq_for_notification) { - break; - } - } - if (cq_idx == server->cq_count) { - gpr_free(rc); - error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; - goto done; - } - if (grpc_cq_begin_op(cq_for_notification, tag) == false) { - gpr_free(rc); - error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN; - goto done; - } - details->reserved = NULL; - rc->cq_idx = cq_idx; - rc->type = BATCH_CALL; - rc->server = server; - rc->tag = tag; - rc->cq_bound_to_call = cq_bound_to_call; - rc->call = call; - rc->data.batch.details = details; - rc->initial_metadata = initial_metadata; - error = queue_call_request(&exec_ctx, server, cq_idx, rc); -done: - grpc_exec_ctx_finish(&exec_ctx); - return error; -} - -grpc_call_error grpc_server_request_registered_call( - grpc_server *server, void *rmp, grpc_call **call, gpr_timespec *deadline, - grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload, - grpc_completion_queue *cq_bound_to_call, - grpc_completion_queue *cq_for_notification, void *tag) { - grpc_call_error error; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; - requested_call *rc = (requested_call *)gpr_malloc(sizeof(*rc)); - registered_method *rm = (registered_method *)rmp; - GRPC_STATS_INC_SERVER_REQUESTED_CALLS(&exec_ctx); - GRPC_API_TRACE( - "grpc_server_request_registered_call(" - "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, " - "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, " - "tag=%p)", - 9, (server, rmp, call, deadline, initial_metadata, optional_payload, - cq_bound_to_call, cq_for_notification, tag)); - - size_t cq_idx; - for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) { - if (server->cqs[cq_idx] == cq_for_notification) { - break; - } - } - if (cq_idx == server->cq_count) { - gpr_free(rc); - error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; - goto done; - } - if ((optional_payload == NULL) != - (rm->payload_handling == GRPC_SRM_PAYLOAD_NONE)) { - gpr_free(rc); - error = GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH; - goto done; - } - if (grpc_cq_begin_op(cq_for_notification, tag) == false) { - gpr_free(rc); - error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN; - goto done; - } - rc->cq_idx = cq_idx; - rc->type = REGISTERED_CALL; - rc->server = server; - rc->tag = tag; - rc->cq_bound_to_call = cq_bound_to_call; - rc->call = call; - rc->data.registered.method = rm; - rc->data.registered.deadline = deadline; - rc->initial_metadata = initial_metadata; - rc->data.registered.optional_payload = optional_payload; - error = queue_call_request(&exec_ctx, server, cq_idx, rc); -done: - grpc_exec_ctx_finish(&exec_ctx); - return error; -} - -static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, - size_t cq_idx, requested_call *rc, grpc_error *error) { - *rc->call = NULL; - rc->initial_metadata->count = 0; - GPR_ASSERT(error != GRPC_ERROR_NONE); - - server_ref(server); - grpc_cq_end_op(exec_ctx, server->cqs[cq_idx], rc->tag, error, - done_request_event, rc, &rc->completion); -} - -const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { - return server->channel_args; -} - -int grpc_server_has_open_connections(grpc_server *server) { - int r; - gpr_mu_lock(&server->mu_global); - r = server->root_channel_data.next != &server->root_channel_data; - gpr_mu_unlock(&server->mu_global); - return r; -} diff --git a/src/core/lib/surface/server.cc b/src/core/lib/surface/server.cc new file mode 100644 index 0000000000..1d0fd472d0 --- /dev/null +++ b/src/core/lib/surface/server.cc @@ -0,0 +1,1554 @@ +/* + * + * Copyright 2015-2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/surface/server.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/connected_channel.h" +#include "src/core/lib/debug/stats.h" +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/iomgr/iomgr.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/support/stack_lockfree.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/surface/api_trace.h" +#include "src/core/lib/surface/call.h" +#include "src/core/lib/surface/channel.h" +#include "src/core/lib/surface/completion_queue.h" +#include "src/core/lib/surface/init.h" +#include "src/core/lib/transport/metadata.h" +#include "src/core/lib/transport/static_metadata.h" + +typedef struct listener { + void *arg; + void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_pollset **pollsets, size_t pollset_count); + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_closure *closure); + struct listener *next; + grpc_closure destroy_done; +} listener; + +typedef struct call_data call_data; +typedef struct channel_data channel_data; +typedef struct registered_method registered_method; + +typedef enum { BATCH_CALL, REGISTERED_CALL } requested_call_type; + +grpc_tracer_flag grpc_server_channel_trace = + GRPC_TRACER_INITIALIZER(false, "server_channel"); + +typedef struct requested_call { + requested_call_type type; + size_t cq_idx; + void *tag; + grpc_server *server; + grpc_completion_queue *cq_bound_to_call; + grpc_call **call; + grpc_cq_completion completion; + grpc_metadata_array *initial_metadata; + union { + struct { + grpc_call_details *details; + } batch; + struct { + registered_method *method; + gpr_timespec *deadline; + grpc_byte_buffer **optional_payload; + } registered; + } data; +} requested_call; + +typedef struct channel_registered_method { + registered_method *server_registered_method; + uint32_t flags; + bool has_host; + grpc_slice method; + grpc_slice host; +} channel_registered_method; + +struct channel_data { + grpc_server *server; + grpc_connectivity_state connectivity_state; + grpc_channel *channel; + size_t cq_idx; + /* linked list of all channels on a server */ + channel_data *next; + channel_data *prev; + channel_registered_method *registered_methods; + uint32_t registered_method_slots; + uint32_t registered_method_max_probes; + grpc_closure finish_destroy_channel_closure; + grpc_closure channel_connectivity_changed; +}; + +typedef struct shutdown_tag { + void *tag; + grpc_completion_queue *cq; + grpc_cq_completion completion; +} shutdown_tag; + +typedef enum { + /* waiting for metadata */ + NOT_STARTED, + /* inital metadata read, not flow controlled in yet */ + PENDING, + /* flow controlled in, on completion queue */ + ACTIVATED, + /* cancelled before being queued */ + ZOMBIED +} call_state; + +typedef struct request_matcher request_matcher; + +struct call_data { + grpc_call *call; + + /** protects state */ + gpr_mu mu_state; + /** the current state of a call - see call_state */ + call_state state; + + bool path_set; + bool host_set; + grpc_slice path; + grpc_slice host; + gpr_timespec deadline; + + grpc_completion_queue *cq_new; + + grpc_metadata_batch *recv_initial_metadata; + uint32_t recv_initial_metadata_flags; + grpc_metadata_array initial_metadata; + + request_matcher *matcher; + grpc_byte_buffer *payload; + + grpc_closure got_initial_metadata; + grpc_closure server_on_recv_initial_metadata; + grpc_closure kill_zombie_closure; + grpc_closure *on_done_recv_initial_metadata; + + grpc_closure publish; + + call_data *pending_next; +}; + +struct request_matcher { + grpc_server *server; + call_data *pending_head; + call_data *pending_tail; + gpr_stack_lockfree **requests_per_cq; +}; + +struct registered_method { + char *method; + char *host; + grpc_server_register_method_payload_handling payload_handling; + uint32_t flags; + /* one request matcher per method */ + request_matcher matcher; + registered_method *next; +}; + +typedef struct { + grpc_channel **channels; + size_t num_channels; +} channel_broadcaster; + +struct grpc_server { + grpc_channel_args *channel_args; + + grpc_completion_queue **cqs; + grpc_pollset **pollsets; + size_t cq_count; + size_t pollset_count; + bool started; + + /* The two following mutexes control access to server-state + mu_global controls access to non-call-related state (e.g., channel state) + mu_call controls access to call-related state (e.g., the call lists) + + If they are ever required to be nested, you must lock mu_global + before mu_call. This is currently used in shutdown processing + (grpc_server_shutdown_and_notify and maybe_finish_shutdown) */ + gpr_mu mu_global; /* mutex for server and channel state */ + gpr_mu mu_call; /* mutex for call-specific state */ + + /* startup synchronization: flag is protected by mu_global, signals whether + we are doing the listener start routine or not */ + bool starting; + gpr_cv starting_cv; + + registered_method *registered_methods; + /** one request matcher for unregistered methods */ + request_matcher unregistered_request_matcher; + /** free list of available requested_calls_per_cq indices */ + gpr_stack_lockfree **request_freelist_per_cq; + /** requested call backing data */ + requested_call **requested_calls_per_cq; + int max_requested_calls_per_cq; + + gpr_atm shutdown_flag; + uint8_t shutdown_published; + size_t num_shutdown_tags; + shutdown_tag *shutdown_tags; + + channel_data root_channel_data; + + listener *listeners; + int listeners_destroyed; + gpr_refcount internal_refcount; + + /** when did we print the last shutdown progress message */ + gpr_timespec last_shutdown_message_time; +}; + +#define SERVER_FROM_CALL_ELEM(elem) \ + (((channel_data *)(elem)->channel_data)->server) + +static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *calld, + grpc_error *error); +static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + size_t cq_idx, requested_call *rc, grpc_error *error); +/* Before calling maybe_finish_shutdown, we must hold mu_global and not + hold mu_call */ +static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_server *server); + +/* + * channel broadcaster + */ + +/* assumes server locked */ +static void channel_broadcaster_init(grpc_server *s, channel_broadcaster *cb) { + channel_data *c; + size_t count = 0; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + count++; + } + cb->num_channels = count; + cb->channels = + (grpc_channel **)gpr_malloc(sizeof(*cb->channels) * cb->num_channels); + count = 0; + for (c = s->root_channel_data.next; c != &s->root_channel_data; c = c->next) { + cb->channels[count++] = c->channel; + GRPC_CHANNEL_INTERNAL_REF(c->channel, "broadcast"); + } +} + +struct shutdown_cleanup_args { + grpc_closure closure; + grpc_slice slice; +}; + +static void shutdown_cleanup(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + struct shutdown_cleanup_args *a = (struct shutdown_cleanup_args *)arg; + grpc_slice_unref_internal(exec_ctx, a->slice); + gpr_free(a); +} + +static void send_shutdown(grpc_exec_ctx *exec_ctx, grpc_channel *channel, + bool send_goaway, grpc_error *send_disconnect) { + struct shutdown_cleanup_args *sc = + (struct shutdown_cleanup_args *)gpr_malloc(sizeof(*sc)); + GRPC_CLOSURE_INIT(&sc->closure, shutdown_cleanup, sc, + grpc_schedule_on_exec_ctx); + grpc_transport_op *op = grpc_make_transport_op(&sc->closure); + grpc_channel_element *elem; + + op->goaway_error = + send_goaway ? grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"), + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_OK) + : GRPC_ERROR_NONE; + op->set_accept_stream = true; + sc->slice = grpc_slice_from_copied_string("Server shutdown"); + op->disconnect_with_error = send_disconnect; + + elem = grpc_channel_stack_element(grpc_channel_get_channel_stack(channel), 0); + elem->filter->start_transport_op(exec_ctx, elem, op); +} + +static void channel_broadcaster_shutdown(grpc_exec_ctx *exec_ctx, + channel_broadcaster *cb, + bool send_goaway, + grpc_error *force_disconnect) { + size_t i; + + for (i = 0; i < cb->num_channels; i++) { + send_shutdown(exec_ctx, cb->channels[i], send_goaway, + GRPC_ERROR_REF(force_disconnect)); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, cb->channels[i], "broadcast"); + } + gpr_free(cb->channels); + GRPC_ERROR_UNREF(force_disconnect); +} + +/* + * request_matcher + */ + +static void request_matcher_init(request_matcher *rm, size_t entries, + grpc_server *server) { + memset(rm, 0, sizeof(*rm)); + rm->server = server; + rm->requests_per_cq = (gpr_stack_lockfree **)gpr_malloc( + sizeof(*rm->requests_per_cq) * server->cq_count); + for (size_t i = 0; i < server->cq_count; i++) { + rm->requests_per_cq[i] = gpr_stack_lockfree_create(entries); + } +} + +static void request_matcher_destroy(request_matcher *rm) { + for (size_t i = 0; i < rm->server->cq_count; i++) { + GPR_ASSERT(gpr_stack_lockfree_pop(rm->requests_per_cq[i]) == -1); + gpr_stack_lockfree_destroy(rm->requests_per_cq[i]); + } + gpr_free(rm->requests_per_cq); +} + +static void kill_zombie(grpc_exec_ctx *exec_ctx, void *elem, + grpc_error *error) { + grpc_call_unref(grpc_call_from_top_element((grpc_call_element *)elem)); +} + +static void request_matcher_zombify_all_pending_calls(grpc_exec_ctx *exec_ctx, + request_matcher *rm) { + while (rm->pending_head) { + call_data *calld = rm->pending_head; + rm->pending_head = calld->pending_next; + gpr_mu_lock(&calld->mu_state); + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + GRPC_CLOSURE_INIT( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE); + } +} + +static void request_matcher_kill_requests(grpc_exec_ctx *exec_ctx, + grpc_server *server, + request_matcher *rm, + grpc_error *error) { + int request_id; + for (size_t i = 0; i < server->cq_count; i++) { + while ((request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[i])) != + -1) { + fail_call(exec_ctx, server, i, + &server->requested_calls_per_cq[i][request_id], + GRPC_ERROR_REF(error)); + } + } + GRPC_ERROR_UNREF(error); +} + +/* + * server proper + */ + +static void server_ref(grpc_server *server) { + gpr_ref(&server->internal_refcount); +} + +static void server_delete(grpc_exec_ctx *exec_ctx, grpc_server *server) { + registered_method *rm; + size_t i; + grpc_channel_args_destroy(exec_ctx, server->channel_args); + gpr_mu_destroy(&server->mu_global); + gpr_mu_destroy(&server->mu_call); + gpr_cv_destroy(&server->starting_cv); + while ((rm = server->registered_methods) != NULL) { + server->registered_methods = rm->next; + if (server->started) { + request_matcher_destroy(&rm->matcher); + } + gpr_free(rm->method); + gpr_free(rm->host); + gpr_free(rm); + } + if (server->started) { + request_matcher_destroy(&server->unregistered_request_matcher); + } + for (i = 0; i < server->cq_count; i++) { + GRPC_CQ_INTERNAL_UNREF(exec_ctx, server->cqs[i], "server"); + if (server->started) { + gpr_stack_lockfree_destroy(server->request_freelist_per_cq[i]); + gpr_free(server->requested_calls_per_cq[i]); + } + } + gpr_free(server->request_freelist_per_cq); + gpr_free(server->requested_calls_per_cq); + gpr_free(server->cqs); + gpr_free(server->pollsets); + gpr_free(server->shutdown_tags); + gpr_free(server); +} + +static void server_unref(grpc_exec_ctx *exec_ctx, grpc_server *server) { + if (gpr_unref(&server->internal_refcount)) { + server_delete(exec_ctx, server); + } +} + +static int is_channel_orphaned(channel_data *chand) { + return chand->next == chand; +} + +static void orphan_channel(channel_data *chand) { + chand->next->prev = chand->prev; + chand->prev->next = chand->next; + chand->next = chand->prev = chand; +} + +static void finish_destroy_channel(grpc_exec_ctx *exec_ctx, void *cd, + grpc_error *error) { + channel_data *chand = (channel_data *)cd; + grpc_server *server = chand->server; + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "server"); + server_unref(exec_ctx, server); +} + +static void destroy_channel(grpc_exec_ctx *exec_ctx, channel_data *chand, + grpc_error *error) { + if (is_channel_orphaned(chand)) return; + GPR_ASSERT(chand->server != NULL); + orphan_channel(chand); + server_ref(chand->server); + maybe_finish_shutdown(exec_ctx, chand->server); + GRPC_CLOSURE_INIT(&chand->finish_destroy_channel_closure, + finish_destroy_channel, chand, grpc_schedule_on_exec_ctx); + + if (GRPC_TRACER_ON(grpc_server_channel_trace) && error != GRPC_ERROR_NONE) { + const char *msg = grpc_error_string(error); + gpr_log(GPR_INFO, "Disconnected client: %s", msg); + } + GRPC_ERROR_UNREF(error); + + grpc_transport_op *op = + grpc_make_transport_op(&chand->finish_destroy_channel_closure); + op->set_accept_stream = true; + grpc_channel_next_op(exec_ctx, + grpc_channel_stack_element( + grpc_channel_get_channel_stack(chand->channel), 0), + op); +} + +static void done_request_event(grpc_exec_ctx *exec_ctx, void *req, + grpc_cq_completion *c) { + requested_call *rc = (requested_call *)req; + grpc_server *server = rc->server; + + if (rc >= server->requested_calls_per_cq[rc->cq_idx] && + rc < server->requested_calls_per_cq[rc->cq_idx] + + server->max_requested_calls_per_cq) { + GPR_ASSERT(rc - server->requested_calls_per_cq[rc->cq_idx] <= INT_MAX); + gpr_stack_lockfree_push( + server->request_freelist_per_cq[rc->cq_idx], + (int)(rc - server->requested_calls_per_cq[rc->cq_idx])); + } else { + gpr_free(req); + } + + server_unref(exec_ctx, server); +} + +static void publish_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + call_data *calld, size_t cq_idx, requested_call *rc) { + grpc_call_set_completion_queue(exec_ctx, calld->call, rc->cq_bound_to_call); + grpc_call *call = calld->call; + *rc->call = call; + calld->cq_new = server->cqs[cq_idx]; + GPR_SWAP(grpc_metadata_array, *rc->initial_metadata, calld->initial_metadata); + switch (rc->type) { + case BATCH_CALL: + GPR_ASSERT(calld->host_set); + GPR_ASSERT(calld->path_set); + rc->data.batch.details->host = grpc_slice_ref_internal(calld->host); + rc->data.batch.details->method = grpc_slice_ref_internal(calld->path); + rc->data.batch.details->deadline = calld->deadline; + rc->data.batch.details->flags = calld->recv_initial_metadata_flags; + break; + case REGISTERED_CALL: + *rc->data.registered.deadline = calld->deadline; + if (rc->data.registered.optional_payload) { + *rc->data.registered.optional_payload = calld->payload; + calld->payload = NULL; + } + break; + default: + GPR_UNREACHABLE_CODE(return ); + } + + grpc_call_element *elem = + grpc_call_stack_element(grpc_call_get_call_stack(call), 0); + channel_data *chand = (channel_data *)elem->channel_data; + server_ref(chand->server); + grpc_cq_end_op(exec_ctx, calld->cq_new, rc->tag, GRPC_ERROR_NONE, + done_request_event, rc, &rc->completion); +} + +static void publish_new_rpc(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *call_elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)call_elem->call_data; + channel_data *chand = (channel_data *)call_elem->channel_data; + request_matcher *rm = calld->matcher; + grpc_server *server = rm->server; + + if (error != GRPC_ERROR_NONE || gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_mu_lock(&calld->mu_state); + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + GRPC_CLOSURE_INIT( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, + GRPC_ERROR_REF(error)); + return; + } + + for (size_t i = 0; i < server->cq_count; i++) { + size_t cq_idx = (chand->cq_idx + i) % server->cq_count; + int request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[cq_idx]); + if (request_id == -1) { + continue; + } else { + GRPC_STATS_INC_SERVER_CQS_CHECKED(exec_ctx, i); + gpr_mu_lock(&calld->mu_state); + calld->state = ACTIVATED; + gpr_mu_unlock(&calld->mu_state); + publish_call(exec_ctx, server, calld, cq_idx, + &server->requested_calls_per_cq[cq_idx][request_id]); + return; /* early out */ + } + } + + /* no cq to take the request found: queue it on the slow list */ + GRPC_STATS_INC_SERVER_SLOWPATH_REQUESTS_QUEUED(exec_ctx); + gpr_mu_lock(&server->mu_call); + gpr_mu_lock(&calld->mu_state); + calld->state = PENDING; + gpr_mu_unlock(&calld->mu_state); + if (rm->pending_head == NULL) { + rm->pending_tail = rm->pending_head = calld; + } else { + rm->pending_tail->pending_next = calld; + rm->pending_tail = calld; + } + calld->pending_next = NULL; + gpr_mu_unlock(&server->mu_call); +} + +static void finish_start_new_rpc( + grpc_exec_ctx *exec_ctx, grpc_server *server, grpc_call_element *elem, + request_matcher *rm, + grpc_server_register_method_payload_handling payload_handling) { + call_data *calld = (call_data *)elem->call_data; + + if (gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_mu_lock(&calld->mu_state); + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, GRPC_ERROR_NONE); + return; + } + + calld->matcher = rm; + + switch (payload_handling) { + case GRPC_SRM_PAYLOAD_NONE: + publish_new_rpc(exec_ctx, elem, GRPC_ERROR_NONE); + break; + case GRPC_SRM_PAYLOAD_READ_INITIAL_BYTE_BUFFER: { + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_MESSAGE; + op.data.recv_message.recv_message = &calld->payload; + GRPC_CLOSURE_INIT(&calld->publish, publish_new_rpc, elem, + grpc_schedule_on_exec_ctx); + grpc_call_start_batch_and_execute(exec_ctx, calld->call, &op, 1, + &calld->publish); + break; + } + } +} + +static void start_new_rpc(grpc_exec_ctx *exec_ctx, grpc_call_element *elem) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + grpc_server *server = chand->server; + uint32_t i; + uint32_t hash; + channel_registered_method *rm; + + if (chand->registered_methods && calld->path_set && calld->host_set) { + /* TODO(ctiller): unify these two searches */ + /* check for an exact match with host */ + hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(calld->host), + grpc_slice_hash(calld->path)); + for (i = 0; i <= chand->registered_method_max_probes; i++) { + rm = &chand->registered_methods[(hash + i) % + chand->registered_method_slots]; + if (!rm) break; + if (!rm->has_host) continue; + if (!grpc_slice_eq(rm->host, calld->host)) continue; + if (!grpc_slice_eq(rm->method, calld->path)) continue; + if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && + 0 == (calld->recv_initial_metadata_flags & + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { + continue; + } + finish_start_new_rpc(exec_ctx, server, elem, + &rm->server_registered_method->matcher, + rm->server_registered_method->payload_handling); + return; + } + /* check for a wildcard method definition (no host set) */ + hash = GRPC_MDSTR_KV_HASH(0, grpc_slice_hash(calld->path)); + for (i = 0; i <= chand->registered_method_max_probes; i++) { + rm = &chand->registered_methods[(hash + i) % + chand->registered_method_slots]; + if (!rm) break; + if (rm->has_host) continue; + if (!grpc_slice_eq(rm->method, calld->path)) continue; + if ((rm->flags & GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST) && + 0 == (calld->recv_initial_metadata_flags & + GRPC_INITIAL_METADATA_IDEMPOTENT_REQUEST)) { + continue; + } + finish_start_new_rpc(exec_ctx, server, elem, + &rm->server_registered_method->matcher, + rm->server_registered_method->payload_handling); + return; + } + } + finish_start_new_rpc(exec_ctx, server, elem, + &server->unregistered_request_matcher, + GRPC_SRM_PAYLOAD_NONE); +} + +static int num_listeners(grpc_server *server) { + listener *l; + int n = 0; + for (l = server->listeners; l; l = l->next) { + n++; + } + return n; +} + +static void done_shutdown_event(grpc_exec_ctx *exec_ctx, void *server, + grpc_cq_completion *completion) { + server_unref(exec_ctx, (grpc_server *)server); +} + +static int num_channels(grpc_server *server) { + channel_data *chand; + int n = 0; + for (chand = server->root_channel_data.next; + chand != &server->root_channel_data; chand = chand->next) { + n++; + } + return n; +} + +static void kill_pending_work_locked(grpc_exec_ctx *exec_ctx, + grpc_server *server, grpc_error *error) { + if (server->started) { + request_matcher_kill_requests(exec_ctx, server, + &server->unregistered_request_matcher, + GRPC_ERROR_REF(error)); + request_matcher_zombify_all_pending_calls( + exec_ctx, &server->unregistered_request_matcher); + for (registered_method *rm = server->registered_methods; rm; + rm = rm->next) { + request_matcher_kill_requests(exec_ctx, server, &rm->matcher, + GRPC_ERROR_REF(error)); + request_matcher_zombify_all_pending_calls(exec_ctx, &rm->matcher); + } + } + GRPC_ERROR_UNREF(error); +} + +static void maybe_finish_shutdown(grpc_exec_ctx *exec_ctx, + grpc_server *server) { + size_t i; + if (!gpr_atm_acq_load(&server->shutdown_flag) || server->shutdown_published) { + return; + } + + kill_pending_work_locked( + exec_ctx, server, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); + + if (server->root_channel_data.next != &server->root_channel_data || + server->listeners_destroyed < num_listeners(server)) { + if (gpr_time_cmp(gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), + server->last_shutdown_message_time), + gpr_time_from_seconds(1, GPR_TIMESPAN)) >= 0) { + server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); + gpr_log(GPR_DEBUG, + "Waiting for %d channels and %d/%d listeners to be destroyed" + " before shutting down server", + num_channels(server), + num_listeners(server) - server->listeners_destroyed, + num_listeners(server)); + } + return; + } + server->shutdown_published = 1; + for (i = 0; i < server->num_shutdown_tags; i++) { + server_ref(server); + grpc_cq_end_op(exec_ctx, server->shutdown_tags[i].cq, + server->shutdown_tags[i].tag, GRPC_ERROR_NONE, + done_shutdown_event, server, + &server->shutdown_tags[i].completion); + } +} + +static void server_on_recv_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)ptr; + call_data *calld = (call_data *)elem->call_data; + gpr_timespec op_deadline; + + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->recv_initial_metadata->idx.named.path != NULL); + GPR_ASSERT(calld->recv_initial_metadata->idx.named.authority != NULL); + calld->path = grpc_slice_ref_internal( + GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.path->md)); + calld->host = grpc_slice_ref_internal( + GRPC_MDVALUE(calld->recv_initial_metadata->idx.named.authority->md)); + calld->path_set = true; + calld->host_set = true; + grpc_metadata_batch_remove(exec_ctx, calld->recv_initial_metadata, + calld->recv_initial_metadata->idx.named.path); + grpc_metadata_batch_remove( + exec_ctx, calld->recv_initial_metadata, + calld->recv_initial_metadata->idx.named.authority); + } else { + GRPC_ERROR_REF(error); + } + op_deadline = calld->recv_initial_metadata->deadline; + if (0 != gpr_time_cmp(op_deadline, gpr_inf_future(op_deadline.clock_type))) { + calld->deadline = op_deadline; + } + if (calld->host_set && calld->path_set) { + /* do nothing */ + } else { + grpc_error *src_error = error; + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Missing :authority or :path", &error, 1); + GRPC_ERROR_UNREF(src_error); + } + + GRPC_CLOSURE_RUN(exec_ctx, calld->on_done_recv_initial_metadata, error); +} + +static void server_mutate_op(grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + call_data *calld = (call_data *)elem->call_data; + + if (op->recv_initial_metadata) { + GPR_ASSERT(op->payload->recv_initial_metadata.recv_flags == NULL); + calld->recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata; + calld->on_done_recv_initial_metadata = + op->payload->recv_initial_metadata.recv_initial_metadata_ready; + op->payload->recv_initial_metadata.recv_initial_metadata_ready = + &calld->server_on_recv_initial_metadata; + op->payload->recv_initial_metadata.recv_flags = + &calld->recv_initial_metadata_flags; + } +} + +static void server_start_transport_stream_op_batch( + grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + server_mutate_op(elem, op); + grpc_call_next_op(exec_ctx, elem, op); +} + +static void got_initial_metadata(grpc_exec_ctx *exec_ctx, void *ptr, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)ptr; + call_data *calld = (call_data *)elem->call_data; + if (error == GRPC_ERROR_NONE) { + start_new_rpc(exec_ctx, elem); + } else { + gpr_mu_lock(&calld->mu_state); + if (calld->state == NOT_STARTED) { + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + GRPC_CLOSURE_INIT(&calld->kill_zombie_closure, kill_zombie, elem, + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, + GRPC_ERROR_NONE); + } else if (calld->state == PENDING) { + calld->state = ZOMBIED; + gpr_mu_unlock(&calld->mu_state); + /* zombied call will be destroyed when it's removed from the pending + queue... later */ + } else { + gpr_mu_unlock(&calld->mu_state); + } + } +} + +static void accept_stream(grpc_exec_ctx *exec_ctx, void *cd, + grpc_transport *transport, + const void *transport_server_data) { + channel_data *chand = (channel_data *)cd; + /* create a call */ + grpc_call_create_args args; + memset(&args, 0, sizeof(args)); + args.channel = chand->channel; + args.server_transport_data = transport_server_data; + args.send_deadline = gpr_inf_future(GPR_CLOCK_MONOTONIC); + grpc_call *call; + grpc_error *error = grpc_call_create(exec_ctx, &args, &call); + grpc_call_element *elem = + grpc_call_stack_element(grpc_call_get_call_stack(call), 0); + if (error != GRPC_ERROR_NONE) { + got_initial_metadata(exec_ctx, elem, error); + GRPC_ERROR_UNREF(error); + return; + } + call_data *calld = (call_data *)elem->call_data; + grpc_op op; + memset(&op, 0, sizeof(op)); + op.op = GRPC_OP_RECV_INITIAL_METADATA; + op.data.recv_initial_metadata.recv_initial_metadata = + &calld->initial_metadata; + GRPC_CLOSURE_INIT(&calld->got_initial_metadata, got_initial_metadata, elem, + grpc_schedule_on_exec_ctx); + grpc_call_start_batch_and_execute(exec_ctx, call, &op, 1, + &calld->got_initial_metadata); +} + +static void channel_connectivity_changed(grpc_exec_ctx *exec_ctx, void *cd, + grpc_error *error) { + channel_data *chand = (channel_data *)cd; + grpc_server *server = chand->server; + if (chand->connectivity_state != GRPC_CHANNEL_SHUTDOWN) { + grpc_transport_op *op = grpc_make_transport_op(NULL); + op->on_connectivity_state_change = &chand->channel_connectivity_changed, + op->connectivity_state = &chand->connectivity_state; + grpc_channel_next_op(exec_ctx, + grpc_channel_stack_element( + grpc_channel_get_channel_stack(chand->channel), 0), + op); + } else { + gpr_mu_lock(&server->mu_global); + destroy_channel(exec_ctx, chand, GRPC_ERROR_REF(error)); + gpr_mu_unlock(&server->mu_global); + GRPC_CHANNEL_INTERNAL_UNREF(exec_ctx, chand->channel, "connectivity"); + } +} + +static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_call_element_args *args) { + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + memset(calld, 0, sizeof(call_data)); + calld->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); + calld->call = grpc_call_from_top_element(elem); + gpr_mu_init(&calld->mu_state); + + GRPC_CLOSURE_INIT(&calld->server_on_recv_initial_metadata, + server_on_recv_initial_metadata, elem, + grpc_schedule_on_exec_ctx); + + server_ref(chand->server); + return GRPC_ERROR_NONE; +} + +static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, + const grpc_call_final_info *final_info, + grpc_closure *ignored) { + channel_data *chand = (channel_data *)elem->channel_data; + call_data *calld = (call_data *)elem->call_data; + + GPR_ASSERT(calld->state != PENDING); + + if (calld->host_set) { + grpc_slice_unref_internal(exec_ctx, calld->host); + } + if (calld->path_set) { + grpc_slice_unref_internal(exec_ctx, calld->path); + } + grpc_metadata_array_destroy(&calld->initial_metadata); + grpc_byte_buffer_destroy(calld->payload); + + gpr_mu_destroy(&calld->mu_state); + + server_unref(exec_ctx, chand->server); +} + +static grpc_error *init_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem, + grpc_channel_element_args *args) { + channel_data *chand = (channel_data *)elem->channel_data; + GPR_ASSERT(args->is_first); + GPR_ASSERT(!args->is_last); + chand->server = NULL; + chand->channel = NULL; + chand->next = chand->prev = chand; + chand->registered_methods = NULL; + chand->connectivity_state = GRPC_CHANNEL_IDLE; + GRPC_CLOSURE_INIT(&chand->channel_connectivity_changed, + channel_connectivity_changed, chand, + grpc_schedule_on_exec_ctx); + return GRPC_ERROR_NONE; +} + +static void destroy_channel_elem(grpc_exec_ctx *exec_ctx, + grpc_channel_element *elem) { + size_t i; + channel_data *chand = (channel_data *)elem->channel_data; + if (chand->registered_methods) { + for (i = 0; i < chand->registered_method_slots; i++) { + grpc_slice_unref_internal(exec_ctx, chand->registered_methods[i].method); + if (chand->registered_methods[i].has_host) { + grpc_slice_unref_internal(exec_ctx, chand->registered_methods[i].host); + } + } + gpr_free(chand->registered_methods); + } + if (chand->server) { + gpr_mu_lock(&chand->server->mu_global); + chand->next->prev = chand->prev; + chand->prev->next = chand->next; + chand->next = chand->prev = chand; + maybe_finish_shutdown(exec_ctx, chand->server); + gpr_mu_unlock(&chand->server->mu_global); + server_unref(exec_ctx, chand->server); + } +} + +const grpc_channel_filter grpc_server_top_filter = { + server_start_transport_stream_op_batch, + grpc_channel_next_op, + sizeof(call_data), + init_call_elem, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + destroy_call_elem, + sizeof(channel_data), + init_channel_elem, + destroy_channel_elem, + grpc_channel_next_get_info, + "server", +}; + +static void register_completion_queue(grpc_server *server, + grpc_completion_queue *cq, + void *reserved) { + size_t i, n; + GPR_ASSERT(!reserved); + for (i = 0; i < server->cq_count; i++) { + if (server->cqs[i] == cq) return; + } + + GRPC_CQ_INTERNAL_REF(cq, "server"); + n = server->cq_count++; + server->cqs = (grpc_completion_queue **)gpr_realloc( + server->cqs, server->cq_count * sizeof(grpc_completion_queue *)); + server->cqs[n] = cq; +} + +void grpc_server_register_completion_queue(grpc_server *server, + grpc_completion_queue *cq, + void *reserved) { + GRPC_API_TRACE( + "grpc_server_register_completion_queue(server=%p, cq=%p, reserved=%p)", 3, + (server, cq, reserved)); + + if (grpc_get_cq_completion_type(cq) != GRPC_CQ_NEXT) { + gpr_log(GPR_INFO, + "Completion queue which is not of type GRPC_CQ_NEXT is being " + "registered as a server-completion-queue"); + /* Ideally we should log an error and abort but ruby-wrapped-language API + calls grpc_completion_queue_pluck() on server completion queues */ + } + + register_completion_queue(server, cq, reserved); +} + +grpc_server *grpc_server_create(const grpc_channel_args *args, void *reserved) { + GRPC_API_TRACE("grpc_server_create(%p, %p)", 2, (args, reserved)); + + grpc_server *server = (grpc_server *)gpr_zalloc(sizeof(grpc_server)); + + gpr_mu_init(&server->mu_global); + gpr_mu_init(&server->mu_call); + gpr_cv_init(&server->starting_cv); + + /* decremented by grpc_server_destroy */ + gpr_ref_init(&server->internal_refcount, 1); + server->root_channel_data.next = server->root_channel_data.prev = + &server->root_channel_data; + + /* TODO(ctiller): expose a channel_arg for this */ + server->max_requested_calls_per_cq = 32768; + server->channel_args = grpc_channel_args_copy(args); + + return server; +} + +static int streq(const char *a, const char *b) { + if (a == NULL && b == NULL) return 1; + if (a == NULL) return 0; + if (b == NULL) return 0; + return 0 == strcmp(a, b); +} + +void *grpc_server_register_method( + grpc_server *server, const char *method, const char *host, + grpc_server_register_method_payload_handling payload_handling, + uint32_t flags) { + registered_method *m; + GRPC_API_TRACE( + "grpc_server_register_method(server=%p, method=%s, host=%s, " + "flags=0x%08x)", + 4, (server, method, host, flags)); + if (!method) { + gpr_log(GPR_ERROR, + "grpc_server_register_method method string cannot be NULL"); + return NULL; + } + for (m = server->registered_methods; m; m = m->next) { + if (streq(m->method, method) && streq(m->host, host)) { + gpr_log(GPR_ERROR, "duplicate registration for %s@%s", method, + host ? host : "*"); + return NULL; + } + } + if ((flags & ~GRPC_INITIAL_METADATA_USED_MASK) != 0) { + gpr_log(GPR_ERROR, "grpc_server_register_method invalid flags 0x%08x", + flags); + return NULL; + } + m = (registered_method *)gpr_zalloc(sizeof(registered_method)); + m->method = gpr_strdup(method); + m->host = gpr_strdup(host); + m->next = server->registered_methods; + m->payload_handling = payload_handling; + m->flags = flags; + server->registered_methods = m; + return m; +} + +static void start_listeners(grpc_exec_ctx *exec_ctx, void *s, + grpc_error *error) { + grpc_server *server = (grpc_server *)s; + for (listener *l = server->listeners; l; l = l->next) { + l->start(exec_ctx, server, l->arg, server->pollsets, server->pollset_count); + } + + gpr_mu_lock(&server->mu_global); + server->starting = false; + gpr_cv_signal(&server->starting_cv); + gpr_mu_unlock(&server->mu_global); + + server_unref(exec_ctx, server); +} + +void grpc_server_start(grpc_server *server) { + size_t i; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_start(server=%p)", 1, (server)); + + server->started = true; + server->pollset_count = 0; + server->pollsets = + (grpc_pollset **)gpr_malloc(sizeof(grpc_pollset *) * server->cq_count); + server->request_freelist_per_cq = (gpr_stack_lockfree **)gpr_malloc( + sizeof(*server->request_freelist_per_cq) * server->cq_count); + server->requested_calls_per_cq = (requested_call **)gpr_malloc( + sizeof(*server->requested_calls_per_cq) * server->cq_count); + for (i = 0; i < server->cq_count; i++) { + if (grpc_cq_can_listen(server->cqs[i])) { + server->pollsets[server->pollset_count++] = + grpc_cq_pollset(server->cqs[i]); + } + server->request_freelist_per_cq[i] = + gpr_stack_lockfree_create((size_t)server->max_requested_calls_per_cq); + for (int j = 0; j < server->max_requested_calls_per_cq; j++) { + gpr_stack_lockfree_push(server->request_freelist_per_cq[i], j); + } + server->requested_calls_per_cq[i] = (requested_call *)gpr_malloc( + (size_t)server->max_requested_calls_per_cq * + sizeof(*server->requested_calls_per_cq[i])); + } + request_matcher_init(&server->unregistered_request_matcher, + (size_t)server->max_requested_calls_per_cq, server); + for (registered_method *rm = server->registered_methods; rm; rm = rm->next) { + request_matcher_init(&rm->matcher, + (size_t)server->max_requested_calls_per_cq, server); + } + + server_ref(server); + server->starting = true; + GRPC_CLOSURE_SCHED( + &exec_ctx, + GRPC_CLOSURE_CREATE(start_listeners, server, + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)), + GRPC_ERROR_NONE); + + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_server_get_pollsets(grpc_server *server, grpc_pollset ***pollsets, + size_t *pollset_count) { + *pollset_count = server->pollset_count; + *pollsets = server->pollsets; +} + +void grpc_server_setup_transport(grpc_exec_ctx *exec_ctx, grpc_server *s, + grpc_transport *transport, + grpc_pollset *accepting_pollset, + const grpc_channel_args *args) { + size_t num_registered_methods; + size_t alloc; + registered_method *rm; + channel_registered_method *crm; + grpc_channel *channel; + channel_data *chand; + uint32_t hash; + size_t slots; + uint32_t probes; + uint32_t max_probes = 0; + grpc_transport_op *op = NULL; + + channel = + grpc_channel_create(exec_ctx, NULL, args, GRPC_SERVER_CHANNEL, transport); + chand = (channel_data *)grpc_channel_stack_element( + grpc_channel_get_channel_stack(channel), 0) + ->channel_data; + chand->server = s; + server_ref(s); + chand->channel = channel; + + size_t cq_idx; + for (cq_idx = 0; cq_idx < s->cq_count; cq_idx++) { + if (grpc_cq_pollset(s->cqs[cq_idx]) == accepting_pollset) break; + } + if (cq_idx == s->cq_count) { + /* completion queue not found: pick a random one to publish new calls to */ + cq_idx = (size_t)rand() % s->cq_count; + } + chand->cq_idx = cq_idx; + + num_registered_methods = 0; + for (rm = s->registered_methods; rm; rm = rm->next) { + num_registered_methods++; + } + /* build a lookup table phrased in terms of mdstr's in this channels context + to quickly find registered methods */ + if (num_registered_methods > 0) { + slots = 2 * num_registered_methods; + alloc = sizeof(channel_registered_method) * slots; + chand->registered_methods = (channel_registered_method *)gpr_zalloc(alloc); + for (rm = s->registered_methods; rm; rm = rm->next) { + grpc_slice host; + bool has_host; + grpc_slice method; + if (rm->host != NULL) { + host = grpc_slice_intern(grpc_slice_from_static_string(rm->host)); + has_host = true; + } else { + has_host = false; + } + method = grpc_slice_intern(grpc_slice_from_static_string(rm->method)); + hash = GRPC_MDSTR_KV_HASH(has_host ? grpc_slice_hash(host) : 0, + grpc_slice_hash(method)); + for (probes = 0; chand->registered_methods[(hash + probes) % slots] + .server_registered_method != NULL; + probes++) + ; + if (probes > max_probes) max_probes = probes; + crm = &chand->registered_methods[(hash + probes) % slots]; + crm->server_registered_method = rm; + crm->flags = rm->flags; + crm->has_host = has_host; + if (has_host) { + crm->host = host; + } + crm->method = method; + } + GPR_ASSERT(slots <= UINT32_MAX); + chand->registered_method_slots = (uint32_t)slots; + chand->registered_method_max_probes = max_probes; + } + + gpr_mu_lock(&s->mu_global); + chand->next = &s->root_channel_data; + chand->prev = chand->next->prev; + chand->next->prev = chand->prev->next = chand; + gpr_mu_unlock(&s->mu_global); + + GRPC_CHANNEL_INTERNAL_REF(channel, "connectivity"); + op = grpc_make_transport_op(NULL); + op->set_accept_stream = true; + op->set_accept_stream_fn = accept_stream; + op->set_accept_stream_user_data = chand; + op->on_connectivity_state_change = &chand->channel_connectivity_changed; + op->connectivity_state = &chand->connectivity_state; + if (gpr_atm_acq_load(&s->shutdown_flag) != 0) { + op->disconnect_with_error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server shutdown"); + } + grpc_transport_perform_op(exec_ctx, transport, op); +} + +void done_published_shutdown(grpc_exec_ctx *exec_ctx, void *done_arg, + grpc_cq_completion *storage) { + (void)done_arg; + gpr_free(storage); +} + +static void listener_destroy_done(grpc_exec_ctx *exec_ctx, void *s, + grpc_error *error) { + grpc_server *server = (grpc_server *)s; + gpr_mu_lock(&server->mu_global); + server->listeners_destroyed++; + maybe_finish_shutdown(exec_ctx, server); + gpr_mu_unlock(&server->mu_global); +} + +void grpc_server_shutdown_and_notify(grpc_server *server, + grpc_completion_queue *cq, void *tag) { + listener *l; + shutdown_tag *sdt; + channel_broadcaster broadcaster; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_shutdown_and_notify(server=%p, cq=%p, tag=%p)", 3, + (server, cq, tag)); + + /* wait for startup to be finished: locks mu_global */ + gpr_mu_lock(&server->mu_global); + while (server->starting) { + gpr_cv_wait(&server->starting_cv, &server->mu_global, + gpr_inf_future(GPR_CLOCK_REALTIME)); + } + + /* stay locked, and gather up some stuff to do */ + GPR_ASSERT(grpc_cq_begin_op(cq, tag)); + if (server->shutdown_published) { + grpc_cq_end_op( + &exec_ctx, cq, tag, GRPC_ERROR_NONE, done_published_shutdown, NULL, + (grpc_cq_completion *)gpr_malloc(sizeof(grpc_cq_completion))); + gpr_mu_unlock(&server->mu_global); + goto done; + } + server->shutdown_tags = (shutdown_tag *)gpr_realloc( + server->shutdown_tags, + sizeof(shutdown_tag) * (server->num_shutdown_tags + 1)); + sdt = &server->shutdown_tags[server->num_shutdown_tags++]; + sdt->tag = tag; + sdt->cq = cq; + if (gpr_atm_acq_load(&server->shutdown_flag)) { + gpr_mu_unlock(&server->mu_global); + goto done; + } + + server->last_shutdown_message_time = gpr_now(GPR_CLOCK_REALTIME); + + channel_broadcaster_init(server, &broadcaster); + + gpr_atm_rel_store(&server->shutdown_flag, 1); + + /* collect all unregistered then registered calls */ + gpr_mu_lock(&server->mu_call); + kill_pending_work_locked( + &exec_ctx, server, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); + gpr_mu_unlock(&server->mu_call); + + maybe_finish_shutdown(&exec_ctx, server); + gpr_mu_unlock(&server->mu_global); + + /* Shutdown listeners */ + for (l = server->listeners; l; l = l->next) { + GRPC_CLOSURE_INIT(&l->destroy_done, listener_destroy_done, server, + grpc_schedule_on_exec_ctx); + l->destroy(&exec_ctx, server, l->arg, &l->destroy_done); + } + + channel_broadcaster_shutdown(&exec_ctx, &broadcaster, true /* send_goaway */, + GRPC_ERROR_NONE); + +done: + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_server_cancel_all_calls(grpc_server *server) { + channel_broadcaster broadcaster; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_cancel_all_calls(server=%p)", 1, (server)); + + gpr_mu_lock(&server->mu_global); + channel_broadcaster_init(server, &broadcaster); + gpr_mu_unlock(&server->mu_global); + + channel_broadcaster_shutdown( + &exec_ctx, &broadcaster, false /* send_goaway */, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Cancelling all calls")); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_server_destroy(grpc_server *server) { + listener *l; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + + GRPC_API_TRACE("grpc_server_destroy(server=%p)", 1, (server)); + + gpr_mu_lock(&server->mu_global); + GPR_ASSERT(gpr_atm_acq_load(&server->shutdown_flag) || !server->listeners); + GPR_ASSERT(server->listeners_destroyed == num_listeners(server)); + + while (server->listeners) { + l = server->listeners; + server->listeners = l->next; + gpr_free(l); + } + + gpr_mu_unlock(&server->mu_global); + + server_unref(&exec_ctx, server); + grpc_exec_ctx_finish(&exec_ctx); +} + +void grpc_server_add_listener( + grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + void (*start)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_pollset **pollsets, size_t pollset_count), + void (*destroy)(grpc_exec_ctx *exec_ctx, grpc_server *server, void *arg, + grpc_closure *on_done)) { + listener *l = (listener *)gpr_malloc(sizeof(listener)); + l->arg = arg; + l->start = start; + l->destroy = destroy; + l->next = server->listeners; + server->listeners = l; +} + +static grpc_call_error queue_call_request(grpc_exec_ctx *exec_ctx, + grpc_server *server, size_t cq_idx, + requested_call *rc) { + call_data *calld = NULL; + request_matcher *rm = NULL; + int request_id; + if (gpr_atm_acq_load(&server->shutdown_flag)) { + fail_call(exec_ctx, server, cq_idx, rc, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Server Shutdown")); + return GRPC_CALL_OK; + } + request_id = gpr_stack_lockfree_pop(server->request_freelist_per_cq[cq_idx]); + if (request_id == -1) { + /* out of request ids: just fail this one */ + fail_call(exec_ctx, server, cq_idx, rc, + grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Out of request ids"), + GRPC_ERROR_INT_LIMIT, server->max_requested_calls_per_cq)); + return GRPC_CALL_OK; + } + switch (rc->type) { + case BATCH_CALL: + rm = &server->unregistered_request_matcher; + break; + case REGISTERED_CALL: + rm = &rc->data.registered.method->matcher; + break; + } + server->requested_calls_per_cq[cq_idx][request_id] = *rc; + gpr_free(rc); + if (gpr_stack_lockfree_push(rm->requests_per_cq[cq_idx], request_id)) { + /* this was the first queued request: we need to lock and start + matching calls */ + gpr_mu_lock(&server->mu_call); + while ((calld = rm->pending_head) != NULL) { + request_id = gpr_stack_lockfree_pop(rm->requests_per_cq[cq_idx]); + if (request_id == -1) break; + rm->pending_head = calld->pending_next; + gpr_mu_unlock(&server->mu_call); + gpr_mu_lock(&calld->mu_state); + if (calld->state == ZOMBIED) { + gpr_mu_unlock(&calld->mu_state); + GRPC_CLOSURE_INIT( + &calld->kill_zombie_closure, kill_zombie, + grpc_call_stack_element(grpc_call_get_call_stack(calld->call), 0), + grpc_schedule_on_exec_ctx); + GRPC_CLOSURE_SCHED(exec_ctx, &calld->kill_zombie_closure, + GRPC_ERROR_NONE); + } else { + GPR_ASSERT(calld->state == PENDING); + calld->state = ACTIVATED; + gpr_mu_unlock(&calld->mu_state); + publish_call(exec_ctx, server, calld, cq_idx, + &server->requested_calls_per_cq[cq_idx][request_id]); + } + gpr_mu_lock(&server->mu_call); + } + gpr_mu_unlock(&server->mu_call); + } + return GRPC_CALL_OK; +} + +grpc_call_error grpc_server_request_call( + grpc_server *server, grpc_call **call, grpc_call_details *details, + grpc_metadata_array *initial_metadata, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, void *tag) { + grpc_call_error error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + requested_call *rc = (requested_call *)gpr_malloc(sizeof(*rc)); + GRPC_STATS_INC_SERVER_REQUESTED_CALLS(&exec_ctx); + GRPC_API_TRACE( + "grpc_server_request_call(" + "server=%p, call=%p, details=%p, initial_metadata=%p, " + "cq_bound_to_call=%p, cq_for_notification=%p, tag=%p)", + 7, (server, call, details, initial_metadata, cq_bound_to_call, + cq_for_notification, tag)); + size_t cq_idx; + for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) { + if (server->cqs[cq_idx] == cq_for_notification) { + break; + } + } + if (cq_idx == server->cq_count) { + gpr_free(rc); + error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + goto done; + } + if (grpc_cq_begin_op(cq_for_notification, tag) == false) { + gpr_free(rc); + error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN; + goto done; + } + details->reserved = NULL; + rc->cq_idx = cq_idx; + rc->type = BATCH_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->call = call; + rc->data.batch.details = details; + rc->initial_metadata = initial_metadata; + error = queue_call_request(&exec_ctx, server, cq_idx, rc); +done: + grpc_exec_ctx_finish(&exec_ctx); + return error; +} + +grpc_call_error grpc_server_request_registered_call( + grpc_server *server, void *rmp, grpc_call **call, gpr_timespec *deadline, + grpc_metadata_array *initial_metadata, grpc_byte_buffer **optional_payload, + grpc_completion_queue *cq_bound_to_call, + grpc_completion_queue *cq_for_notification, void *tag) { + grpc_call_error error; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + requested_call *rc = (requested_call *)gpr_malloc(sizeof(*rc)); + registered_method *rm = (registered_method *)rmp; + GRPC_STATS_INC_SERVER_REQUESTED_CALLS(&exec_ctx); + GRPC_API_TRACE( + "grpc_server_request_registered_call(" + "server=%p, rmp=%p, call=%p, deadline=%p, initial_metadata=%p, " + "optional_payload=%p, cq_bound_to_call=%p, cq_for_notification=%p, " + "tag=%p)", + 9, (server, rmp, call, deadline, initial_metadata, optional_payload, + cq_bound_to_call, cq_for_notification, tag)); + + size_t cq_idx; + for (cq_idx = 0; cq_idx < server->cq_count; cq_idx++) { + if (server->cqs[cq_idx] == cq_for_notification) { + break; + } + } + if (cq_idx == server->cq_count) { + gpr_free(rc); + error = GRPC_CALL_ERROR_NOT_SERVER_COMPLETION_QUEUE; + goto done; + } + if ((optional_payload == NULL) != + (rm->payload_handling == GRPC_SRM_PAYLOAD_NONE)) { + gpr_free(rc); + error = GRPC_CALL_ERROR_PAYLOAD_TYPE_MISMATCH; + goto done; + } + if (grpc_cq_begin_op(cq_for_notification, tag) == false) { + gpr_free(rc); + error = GRPC_CALL_ERROR_COMPLETION_QUEUE_SHUTDOWN; + goto done; + } + rc->cq_idx = cq_idx; + rc->type = REGISTERED_CALL; + rc->server = server; + rc->tag = tag; + rc->cq_bound_to_call = cq_bound_to_call; + rc->call = call; + rc->data.registered.method = rm; + rc->data.registered.deadline = deadline; + rc->initial_metadata = initial_metadata; + rc->data.registered.optional_payload = optional_payload; + error = queue_call_request(&exec_ctx, server, cq_idx, rc); +done: + grpc_exec_ctx_finish(&exec_ctx); + return error; +} + +static void fail_call(grpc_exec_ctx *exec_ctx, grpc_server *server, + size_t cq_idx, requested_call *rc, grpc_error *error) { + *rc->call = NULL; + rc->initial_metadata->count = 0; + GPR_ASSERT(error != GRPC_ERROR_NONE); + + server_ref(server); + grpc_cq_end_op(exec_ctx, server->cqs[cq_idx], rc->tag, error, + done_request_event, rc, &rc->completion); +} + +const grpc_channel_args *grpc_server_get_channel_args(grpc_server *server) { + return server->channel_args; +} + +int grpc_server_has_open_connections(grpc_server *server) { + int r; + gpr_mu_lock(&server->mu_global); + r = server->root_channel_data.next != &server->root_channel_data; + gpr_mu_unlock(&server->mu_global); + return r; +} diff --git a/src/core/lib/surface/validate_metadata.c b/src/core/lib/surface/validate_metadata.c deleted file mode 100644 index 81d07fae44..0000000000 --- a/src/core/lib/surface/validate_metadata.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include -#include - -#include -#include -#include - -#include "src/core/lib/iomgr/error.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/surface/validate_metadata.h" - -static grpc_error *conforms_to(grpc_slice slice, const uint8_t *legal_bits, - const char *err_desc) { - const uint8_t *p = GRPC_SLICE_START_PTR(slice); - const uint8_t *e = GRPC_SLICE_END_PTR(slice); - for (; p != e; p++) { - int idx = *p; - int byte = idx / 8; - int bit = idx % 8; - if ((legal_bits[byte] & (1 << bit)) == 0) { - char *dump = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); - grpc_error *error = grpc_error_set_str( - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_desc), - GRPC_ERROR_INT_OFFSET, - p - GRPC_SLICE_START_PTR(slice)), - GRPC_ERROR_STR_RAW_BYTES, grpc_slice_from_copied_string(dump)); - gpr_free(dump); - return error; - } - } - return GRPC_ERROR_NONE; -} - -static int error2int(grpc_error *error) { - int r = (error == GRPC_ERROR_NONE); - GRPC_ERROR_UNREF(error); - return r; -} - -grpc_error *grpc_validate_header_key_is_legal(grpc_slice slice) { - static const uint8_t legal_header_bits[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0x00, 0x00, 0x00, - 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - if (GRPC_SLICE_LENGTH(slice) == 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Metadata keys cannot be zero length"); - } - if (GRPC_SLICE_START_PTR(slice)[0] == ':') { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Metadata keys cannot start with :"); - } - return conforms_to(slice, legal_header_bits, "Illegal header key"); -} - -int grpc_header_key_is_legal(grpc_slice slice) { - return error2int(grpc_validate_header_key_is_legal(slice)); -} - -grpc_error *grpc_validate_header_nonbin_value_is_legal(grpc_slice slice) { - static const uint8_t legal_header_bits[256 / 8] = { - 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - return conforms_to(slice, legal_header_bits, "Illegal header value"); -} - -int grpc_header_nonbin_value_is_legal(grpc_slice slice) { - return error2int(grpc_validate_header_nonbin_value_is_legal(slice)); -} - -int grpc_is_binary_header(grpc_slice slice) { - if (GRPC_SLICE_LENGTH(slice) < 5) return 0; - return 0 == memcmp(GRPC_SLICE_END_PTR(slice) - 4, "-bin", 4); -} diff --git a/src/core/lib/surface/validate_metadata.cc b/src/core/lib/surface/validate_metadata.cc new file mode 100644 index 0000000000..81d07fae44 --- /dev/null +++ b/src/core/lib/surface/validate_metadata.cc @@ -0,0 +1,94 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include +#include + +#include +#include +#include + +#include "src/core/lib/iomgr/error.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/surface/validate_metadata.h" + +static grpc_error *conforms_to(grpc_slice slice, const uint8_t *legal_bits, + const char *err_desc) { + const uint8_t *p = GRPC_SLICE_START_PTR(slice); + const uint8_t *e = GRPC_SLICE_END_PTR(slice); + for (; p != e; p++) { + int idx = *p; + int byte = idx / 8; + int bit = idx % 8; + if ((legal_bits[byte] & (1 << bit)) == 0) { + char *dump = grpc_dump_slice(slice, GPR_DUMP_HEX | GPR_DUMP_ASCII); + grpc_error *error = grpc_error_set_str( + grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(err_desc), + GRPC_ERROR_INT_OFFSET, + p - GRPC_SLICE_START_PTR(slice)), + GRPC_ERROR_STR_RAW_BYTES, grpc_slice_from_copied_string(dump)); + gpr_free(dump); + return error; + } + } + return GRPC_ERROR_NONE; +} + +static int error2int(grpc_error *error) { + int r = (error == GRPC_ERROR_NONE); + GRPC_ERROR_UNREF(error); + return r; +} + +grpc_error *grpc_validate_header_key_is_legal(grpc_slice slice) { + static const uint8_t legal_header_bits[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xff, 0x03, 0x00, 0x00, 0x00, + 0x80, 0xfe, 0xff, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (GRPC_SLICE_LENGTH(slice) == 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Metadata keys cannot be zero length"); + } + if (GRPC_SLICE_START_PTR(slice)[0] == ':') { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Metadata keys cannot start with :"); + } + return conforms_to(slice, legal_header_bits, "Illegal header key"); +} + +int grpc_header_key_is_legal(grpc_slice slice) { + return error2int(grpc_validate_header_key_is_legal(slice)); +} + +grpc_error *grpc_validate_header_nonbin_value_is_legal(grpc_slice slice) { + static const uint8_t legal_header_bits[256 / 8] = { + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + return conforms_to(slice, legal_header_bits, "Illegal header value"); +} + +int grpc_header_nonbin_value_is_legal(grpc_slice slice) { + return error2int(grpc_validate_header_nonbin_value_is_legal(slice)); +} + +int grpc_is_binary_header(grpc_slice slice) { + if (GRPC_SLICE_LENGTH(slice) < 5) return 0; + return 0 == memcmp(GRPC_SLICE_END_PTR(slice) - 4, "-bin", 4); +} diff --git a/src/core/lib/transport/bdp_estimator.c b/src/core/lib/transport/bdp_estimator.c deleted file mode 100644 index 8b57693413..0000000000 --- a/src/core/lib/transport/bdp_estimator.c +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/bdp_estimator.h" - -#include - -#include -#include - -grpc_tracer_flag grpc_bdp_estimator_trace = - GRPC_TRACER_INITIALIZER(false, "bdp_estimator"); - -void grpc_bdp_estimator_init(grpc_bdp_estimator *estimator, const char *name) { - estimator->estimate = 65536; - estimator->ping_state = GRPC_BDP_PING_UNSCHEDULED; - estimator->name = name; - estimator->bw_est = 0; -} - -bool grpc_bdp_estimator_get_estimate(const grpc_bdp_estimator *estimator, - int64_t *estimate) { - *estimate = estimator->estimate; - return true; -} - -bool grpc_bdp_estimator_get_bw(const grpc_bdp_estimator *estimator, - double *bw) { - *bw = estimator->bw_est; - return true; -} - -void grpc_bdp_estimator_add_incoming_bytes(grpc_bdp_estimator *estimator, - int64_t num_bytes) { - estimator->accumulator += num_bytes; -} - -bool grpc_bdp_estimator_need_ping(const grpc_bdp_estimator *estimator) { - switch (estimator->ping_state) { - case GRPC_BDP_PING_UNSCHEDULED: - return true; - case GRPC_BDP_PING_SCHEDULED: - return false; - case GRPC_BDP_PING_STARTED: - return false; - } - GPR_UNREACHABLE_CODE(return false); -} - -void grpc_bdp_estimator_schedule_ping(grpc_bdp_estimator *estimator) { - if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, - estimator->name, estimator->accumulator, estimator->estimate); - } - GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_UNSCHEDULED); - estimator->ping_state = GRPC_BDP_PING_SCHEDULED; - estimator->accumulator = 0; -} - -void grpc_bdp_estimator_start_ping(grpc_bdp_estimator *estimator) { - if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, - estimator->name, estimator->accumulator, estimator->estimate); - } - GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_SCHEDULED); - estimator->ping_state = GRPC_BDP_PING_STARTED; - estimator->accumulator = 0; - estimator->ping_start_time = gpr_now(GPR_CLOCK_MONOTONIC); -} - -void grpc_bdp_estimator_complete_ping(grpc_bdp_estimator *estimator) { - gpr_timespec dt_ts = - gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), estimator->ping_start_time); - double dt = (double)dt_ts.tv_sec + 1e-9 * (double)dt_ts.tv_nsec; - double bw = dt > 0 ? ((double)estimator->accumulator / dt) : 0; - if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64 - " dt=%lf bw=%lfMbs bw_est=%lfMbs", - estimator->name, estimator->accumulator, estimator->estimate, dt, - bw / 125000.0, estimator->bw_est / 125000.0); - } - GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_STARTED); - if (estimator->accumulator > 2 * estimator->estimate / 3 && - bw > estimator->bw_est) { - estimator->estimate = - GPR_MAX(estimator->accumulator, estimator->estimate * 2); - estimator->bw_est = bw; - if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { - gpr_log(GPR_DEBUG, "bdp[%s]: estimate increased to %" PRId64, - estimator->name, estimator->estimate); - } - } - estimator->ping_state = GRPC_BDP_PING_UNSCHEDULED; - estimator->accumulator = 0; -} diff --git a/src/core/lib/transport/bdp_estimator.cc b/src/core/lib/transport/bdp_estimator.cc new file mode 100644 index 0000000000..8b57693413 --- /dev/null +++ b/src/core/lib/transport/bdp_estimator.cc @@ -0,0 +1,110 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/bdp_estimator.h" + +#include + +#include +#include + +grpc_tracer_flag grpc_bdp_estimator_trace = + GRPC_TRACER_INITIALIZER(false, "bdp_estimator"); + +void grpc_bdp_estimator_init(grpc_bdp_estimator *estimator, const char *name) { + estimator->estimate = 65536; + estimator->ping_state = GRPC_BDP_PING_UNSCHEDULED; + estimator->name = name; + estimator->bw_est = 0; +} + +bool grpc_bdp_estimator_get_estimate(const grpc_bdp_estimator *estimator, + int64_t *estimate) { + *estimate = estimator->estimate; + return true; +} + +bool grpc_bdp_estimator_get_bw(const grpc_bdp_estimator *estimator, + double *bw) { + *bw = estimator->bw_est; + return true; +} + +void grpc_bdp_estimator_add_incoming_bytes(grpc_bdp_estimator *estimator, + int64_t num_bytes) { + estimator->accumulator += num_bytes; +} + +bool grpc_bdp_estimator_need_ping(const grpc_bdp_estimator *estimator) { + switch (estimator->ping_state) { + case GRPC_BDP_PING_UNSCHEDULED: + return true; + case GRPC_BDP_PING_SCHEDULED: + return false; + case GRPC_BDP_PING_STARTED: + return false; + } + GPR_UNREACHABLE_CODE(return false); +} + +void grpc_bdp_estimator_schedule_ping(grpc_bdp_estimator *estimator) { + if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "bdp[%s]:sched acc=%" PRId64 " est=%" PRId64, + estimator->name, estimator->accumulator, estimator->estimate); + } + GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_UNSCHEDULED); + estimator->ping_state = GRPC_BDP_PING_SCHEDULED; + estimator->accumulator = 0; +} + +void grpc_bdp_estimator_start_ping(grpc_bdp_estimator *estimator) { + if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "bdp[%s]:start acc=%" PRId64 " est=%" PRId64, + estimator->name, estimator->accumulator, estimator->estimate); + } + GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_SCHEDULED); + estimator->ping_state = GRPC_BDP_PING_STARTED; + estimator->accumulator = 0; + estimator->ping_start_time = gpr_now(GPR_CLOCK_MONOTONIC); +} + +void grpc_bdp_estimator_complete_ping(grpc_bdp_estimator *estimator) { + gpr_timespec dt_ts = + gpr_time_sub(gpr_now(GPR_CLOCK_MONOTONIC), estimator->ping_start_time); + double dt = (double)dt_ts.tv_sec + 1e-9 * (double)dt_ts.tv_nsec; + double bw = dt > 0 ? ((double)estimator->accumulator / dt) : 0; + if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "bdp[%s]:complete acc=%" PRId64 " est=%" PRId64 + " dt=%lf bw=%lfMbs bw_est=%lfMbs", + estimator->name, estimator->accumulator, estimator->estimate, dt, + bw / 125000.0, estimator->bw_est / 125000.0); + } + GPR_ASSERT(estimator->ping_state == GRPC_BDP_PING_STARTED); + if (estimator->accumulator > 2 * estimator->estimate / 3 && + bw > estimator->bw_est) { + estimator->estimate = + GPR_MAX(estimator->accumulator, estimator->estimate * 2); + estimator->bw_est = bw; + if (GRPC_TRACER_ON(grpc_bdp_estimator_trace)) { + gpr_log(GPR_DEBUG, "bdp[%s]: estimate increased to %" PRId64, + estimator->name, estimator->estimate); + } + } + estimator->ping_state = GRPC_BDP_PING_UNSCHEDULED; + estimator->accumulator = 0; +} diff --git a/src/core/lib/transport/byte_stream.c b/src/core/lib/transport/byte_stream.c deleted file mode 100644 index 08f61629a9..0000000000 --- a/src/core/lib/transport/byte_stream.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/byte_stream.h" - -#include -#include - -#include - -#include "src/core/lib/slice/slice_internal.h" - -bool grpc_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, size_t max_size_hint, - grpc_closure *on_complete) { - return byte_stream->vtable->next(exec_ctx, byte_stream, max_size_hint, - on_complete); -} - -grpc_error *grpc_byte_stream_pull(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_slice *slice) { - return byte_stream->vtable->pull(exec_ctx, byte_stream, slice); -} - -void grpc_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_error *error) { - byte_stream->vtable->shutdown(exec_ctx, byte_stream, error); -} - -void grpc_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) { - byte_stream->vtable->destroy(exec_ctx, byte_stream); -} - -// grpc_slice_buffer_stream - -static bool slice_buffer_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - size_t max_size_hint, - grpc_closure *on_complete) { - grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; - GPR_ASSERT(stream->cursor < stream->backing_buffer->count); - return true; -} - -static grpc_error *slice_buffer_stream_pull(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_slice *slice) { - grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); - } - GPR_ASSERT(stream->cursor < stream->backing_buffer->count); - *slice = - grpc_slice_ref_internal(stream->backing_buffer->slices[stream->cursor]); - stream->cursor++; - return GRPC_ERROR_NONE; -} - -static void slice_buffer_stream_shutdown(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_error *error) { - grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = error; -} - -static void slice_buffer_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) { - grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, stream->backing_buffer); - GRPC_ERROR_UNREF(stream->shutdown_error); -} - -static const grpc_byte_stream_vtable slice_buffer_stream_vtable = { - slice_buffer_stream_next, slice_buffer_stream_pull, - slice_buffer_stream_shutdown, slice_buffer_stream_destroy}; - -void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream *stream, - grpc_slice_buffer *slice_buffer, - uint32_t flags) { - GPR_ASSERT(slice_buffer->length <= UINT32_MAX); - stream->base.length = (uint32_t)slice_buffer->length; - stream->base.flags = flags; - stream->base.vtable = &slice_buffer_stream_vtable; - stream->backing_buffer = slice_buffer; - stream->cursor = 0; - stream->shutdown_error = GRPC_ERROR_NONE; -} - -// grpc_caching_byte_stream - -void grpc_byte_stream_cache_init(grpc_byte_stream_cache *cache, - grpc_byte_stream *underlying_stream) { - cache->underlying_stream = underlying_stream; - grpc_slice_buffer_init(&cache->cache_buffer); -} - -void grpc_byte_stream_cache_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream_cache *cache) { - grpc_byte_stream_destroy(exec_ctx, cache->underlying_stream); - grpc_slice_buffer_destroy_internal(exec_ctx, &cache->cache_buffer); -} - -static bool caching_byte_stream_next(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - size_t max_size_hint, - grpc_closure *on_complete) { - grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; - if (stream->shutdown_error != GRPC_ERROR_NONE) return true; - if (stream->cursor < stream->cache->cache_buffer.count) return true; - return grpc_byte_stream_next(exec_ctx, stream->cache->underlying_stream, - max_size_hint, on_complete); -} - -static grpc_error *caching_byte_stream_pull(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_slice *slice) { - grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; - if (stream->shutdown_error != GRPC_ERROR_NONE) { - return GRPC_ERROR_REF(stream->shutdown_error); - } - if (stream->cursor < stream->cache->cache_buffer.count) { - *slice = grpc_slice_ref_internal( - stream->cache->cache_buffer.slices[stream->cursor]); - ++stream->cursor; - return GRPC_ERROR_NONE; - } - grpc_error *error = - grpc_byte_stream_pull(exec_ctx, stream->cache->underlying_stream, slice); - if (error == GRPC_ERROR_NONE) { - ++stream->cursor; - grpc_slice_buffer_add(&stream->cache->cache_buffer, - grpc_slice_ref_internal(*slice)); - } - return error; -} - -static void caching_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream, - grpc_error *error) { - grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; - GRPC_ERROR_UNREF(stream->shutdown_error); - stream->shutdown_error = GRPC_ERROR_REF(error); - grpc_byte_stream_shutdown(exec_ctx, stream->cache->underlying_stream, error); -} - -static void caching_byte_stream_destroy(grpc_exec_ctx *exec_ctx, - grpc_byte_stream *byte_stream) { - grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; - GRPC_ERROR_UNREF(stream->shutdown_error); -} - -static const grpc_byte_stream_vtable caching_byte_stream_vtable = { - caching_byte_stream_next, caching_byte_stream_pull, - caching_byte_stream_shutdown, caching_byte_stream_destroy}; - -void grpc_caching_byte_stream_init(grpc_caching_byte_stream *stream, - grpc_byte_stream_cache *cache) { - memset(stream, 0, sizeof(*stream)); - stream->base.length = cache->underlying_stream->length; - stream->base.flags = cache->underlying_stream->flags; - stream->base.vtable = &caching_byte_stream_vtable; - stream->cache = cache; - stream->shutdown_error = GRPC_ERROR_NONE; -} - -void grpc_caching_byte_stream_reset(grpc_caching_byte_stream *stream) { - stream->cursor = 0; -} diff --git a/src/core/lib/transport/byte_stream.cc b/src/core/lib/transport/byte_stream.cc new file mode 100644 index 0000000000..08f61629a9 --- /dev/null +++ b/src/core/lib/transport/byte_stream.cc @@ -0,0 +1,187 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/byte_stream.h" + +#include +#include + +#include + +#include "src/core/lib/slice/slice_internal.h" + +bool grpc_byte_stream_next(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, size_t max_size_hint, + grpc_closure *on_complete) { + return byte_stream->vtable->next(exec_ctx, byte_stream, max_size_hint, + on_complete); +} + +grpc_error *grpc_byte_stream_pull(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_slice *slice) { + return byte_stream->vtable->pull(exec_ctx, byte_stream, slice); +} + +void grpc_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_error *error) { + byte_stream->vtable->shutdown(exec_ctx, byte_stream, error); +} + +void grpc_byte_stream_destroy(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream) { + byte_stream->vtable->destroy(exec_ctx, byte_stream); +} + +// grpc_slice_buffer_stream + +static bool slice_buffer_stream_next(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + size_t max_size_hint, + grpc_closure *on_complete) { + grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; + GPR_ASSERT(stream->cursor < stream->backing_buffer->count); + return true; +} + +static grpc_error *slice_buffer_stream_pull(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_slice *slice) { + grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; + if (stream->shutdown_error != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(stream->shutdown_error); + } + GPR_ASSERT(stream->cursor < stream->backing_buffer->count); + *slice = + grpc_slice_ref_internal(stream->backing_buffer->slices[stream->cursor]); + stream->cursor++; + return GRPC_ERROR_NONE; +} + +static void slice_buffer_stream_shutdown(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_error *error) { + grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; + GRPC_ERROR_UNREF(stream->shutdown_error); + stream->shutdown_error = error; +} + +static void slice_buffer_stream_destroy(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream) { + grpc_slice_buffer_stream *stream = (grpc_slice_buffer_stream *)byte_stream; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, stream->backing_buffer); + GRPC_ERROR_UNREF(stream->shutdown_error); +} + +static const grpc_byte_stream_vtable slice_buffer_stream_vtable = { + slice_buffer_stream_next, slice_buffer_stream_pull, + slice_buffer_stream_shutdown, slice_buffer_stream_destroy}; + +void grpc_slice_buffer_stream_init(grpc_slice_buffer_stream *stream, + grpc_slice_buffer *slice_buffer, + uint32_t flags) { + GPR_ASSERT(slice_buffer->length <= UINT32_MAX); + stream->base.length = (uint32_t)slice_buffer->length; + stream->base.flags = flags; + stream->base.vtable = &slice_buffer_stream_vtable; + stream->backing_buffer = slice_buffer; + stream->cursor = 0; + stream->shutdown_error = GRPC_ERROR_NONE; +} + +// grpc_caching_byte_stream + +void grpc_byte_stream_cache_init(grpc_byte_stream_cache *cache, + grpc_byte_stream *underlying_stream) { + cache->underlying_stream = underlying_stream; + grpc_slice_buffer_init(&cache->cache_buffer); +} + +void grpc_byte_stream_cache_destroy(grpc_exec_ctx *exec_ctx, + grpc_byte_stream_cache *cache) { + grpc_byte_stream_destroy(exec_ctx, cache->underlying_stream); + grpc_slice_buffer_destroy_internal(exec_ctx, &cache->cache_buffer); +} + +static bool caching_byte_stream_next(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + size_t max_size_hint, + grpc_closure *on_complete) { + grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; + if (stream->shutdown_error != GRPC_ERROR_NONE) return true; + if (stream->cursor < stream->cache->cache_buffer.count) return true; + return grpc_byte_stream_next(exec_ctx, stream->cache->underlying_stream, + max_size_hint, on_complete); +} + +static grpc_error *caching_byte_stream_pull(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_slice *slice) { + grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; + if (stream->shutdown_error != GRPC_ERROR_NONE) { + return GRPC_ERROR_REF(stream->shutdown_error); + } + if (stream->cursor < stream->cache->cache_buffer.count) { + *slice = grpc_slice_ref_internal( + stream->cache->cache_buffer.slices[stream->cursor]); + ++stream->cursor; + return GRPC_ERROR_NONE; + } + grpc_error *error = + grpc_byte_stream_pull(exec_ctx, stream->cache->underlying_stream, slice); + if (error == GRPC_ERROR_NONE) { + ++stream->cursor; + grpc_slice_buffer_add(&stream->cache->cache_buffer, + grpc_slice_ref_internal(*slice)); + } + return error; +} + +static void caching_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream, + grpc_error *error) { + grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; + GRPC_ERROR_UNREF(stream->shutdown_error); + stream->shutdown_error = GRPC_ERROR_REF(error); + grpc_byte_stream_shutdown(exec_ctx, stream->cache->underlying_stream, error); +} + +static void caching_byte_stream_destroy(grpc_exec_ctx *exec_ctx, + grpc_byte_stream *byte_stream) { + grpc_caching_byte_stream *stream = (grpc_caching_byte_stream *)byte_stream; + GRPC_ERROR_UNREF(stream->shutdown_error); +} + +static const grpc_byte_stream_vtable caching_byte_stream_vtable = { + caching_byte_stream_next, caching_byte_stream_pull, + caching_byte_stream_shutdown, caching_byte_stream_destroy}; + +void grpc_caching_byte_stream_init(grpc_caching_byte_stream *stream, + grpc_byte_stream_cache *cache) { + memset(stream, 0, sizeof(*stream)); + stream->base.length = cache->underlying_stream->length; + stream->base.flags = cache->underlying_stream->flags; + stream->base.vtable = &caching_byte_stream_vtable; + stream->cache = cache; + stream->shutdown_error = GRPC_ERROR_NONE; +} + +void grpc_caching_byte_stream_reset(grpc_caching_byte_stream *stream) { + stream->cursor = 0; +} diff --git a/src/core/lib/transport/connectivity_state.c b/src/core/lib/transport/connectivity_state.c deleted file mode 100644 index f328a6cdbb..0000000000 --- a/src/core/lib/transport/connectivity_state.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/connectivity_state.h" - -#include - -#include -#include -#include - -grpc_tracer_flag grpc_connectivity_state_trace = - GRPC_TRACER_INITIALIZER(false, "connectivity_state"); - -const char *grpc_connectivity_state_name(grpc_connectivity_state state) { - switch (state) { - case GRPC_CHANNEL_INIT: - return "INIT"; - case GRPC_CHANNEL_IDLE: - return "IDLE"; - case GRPC_CHANNEL_CONNECTING: - return "CONNECTING"; - case GRPC_CHANNEL_READY: - return "READY"; - case GRPC_CHANNEL_TRANSIENT_FAILURE: - return "TRANSIENT_FAILURE"; - case GRPC_CHANNEL_SHUTDOWN: - return "SHUTDOWN"; - } - GPR_UNREACHABLE_CODE(return "UNKNOWN"); -} - -void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state init_state, - const char *name) { - gpr_atm_no_barrier_store(&tracker->current_state_atm, init_state); - tracker->current_error = GRPC_ERROR_NONE; - tracker->watchers = NULL; - tracker->name = gpr_strdup(name); -} - -void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx, - grpc_connectivity_state_tracker *tracker) { - grpc_error *error; - grpc_connectivity_state_watcher *w; - while ((w = tracker->watchers)) { - tracker->watchers = w->next; - - if (GRPC_CHANNEL_SHUTDOWN != *w->current) { - *w->current = GRPC_CHANNEL_SHUTDOWN; - error = GRPC_ERROR_NONE; - } else { - error = - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutdown connectivity owner"); - } - GRPC_CLOSURE_SCHED(exec_ctx, w->notify, error); - gpr_free(w); - } - GRPC_ERROR_UNREF(tracker->current_error); - gpr_free(tracker->name); -} - -grpc_connectivity_state grpc_connectivity_state_check( - grpc_connectivity_state_tracker *tracker) { - grpc_connectivity_state cur = - (grpc_connectivity_state)gpr_atm_no_barrier_load( - &tracker->current_state_atm); - if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name, - grpc_connectivity_state_name(cur)); - } - return cur; -} - -grpc_connectivity_state grpc_connectivity_state_get( - grpc_connectivity_state_tracker *tracker, grpc_error **error) { - grpc_connectivity_state cur = - (grpc_connectivity_state)gpr_atm_no_barrier_load( - &tracker->current_state_atm); - if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name, - grpc_connectivity_state_name(cur)); - } - if (error != NULL) { - *error = GRPC_ERROR_REF(tracker->current_error); - } - return cur; -} - -bool grpc_connectivity_state_has_watchers( - grpc_connectivity_state_tracker *connectivity_state) { - return connectivity_state->watchers != NULL; -} - -bool grpc_connectivity_state_notify_on_state_change( - grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state *current, grpc_closure *notify) { - grpc_connectivity_state cur = - (grpc_connectivity_state)gpr_atm_no_barrier_load( - &tracker->current_state_atm); - if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { - if (current == NULL) { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: unsubscribe notify=%p", tracker, - tracker->name, notify); - } else { - gpr_log(GPR_DEBUG, "CONWATCH: %p %s: from %s [cur=%s] notify=%p", tracker, - tracker->name, grpc_connectivity_state_name(*current), - grpc_connectivity_state_name(cur), notify); - } - } - if (current == NULL) { - grpc_connectivity_state_watcher *w = tracker->watchers; - if (w != NULL && w->notify == notify) { - GRPC_CLOSURE_SCHED(exec_ctx, notify, GRPC_ERROR_CANCELLED); - tracker->watchers = w->next; - gpr_free(w); - return false; - } - while (w != NULL) { - grpc_connectivity_state_watcher *rm_candidate = w->next; - if (rm_candidate != NULL && rm_candidate->notify == notify) { - GRPC_CLOSURE_SCHED(exec_ctx, notify, GRPC_ERROR_CANCELLED); - w->next = w->next->next; - gpr_free(rm_candidate); - return false; - } - w = w->next; - } - return false; - } else { - if (cur != *current) { - *current = cur; - GRPC_CLOSURE_SCHED(exec_ctx, notify, - GRPC_ERROR_REF(tracker->current_error)); - } else { - grpc_connectivity_state_watcher *w = - (grpc_connectivity_state_watcher *)gpr_malloc(sizeof(*w)); - w->current = current; - w->notify = notify; - w->next = tracker->watchers; - tracker->watchers = w; - } - return cur == GRPC_CHANNEL_IDLE; - } -} - -void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, - grpc_connectivity_state_tracker *tracker, - grpc_connectivity_state state, - grpc_error *error, const char *reason) { - grpc_connectivity_state cur = - (grpc_connectivity_state)gpr_atm_no_barrier_load( - &tracker->current_state_atm); - grpc_connectivity_state_watcher *w; - if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { - const char *error_string = grpc_error_string(error); - gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker, - tracker->name, grpc_connectivity_state_name(cur), - grpc_connectivity_state_name(state), reason, error, error_string); - } - switch (state) { - case GRPC_CHANNEL_INIT: - case GRPC_CHANNEL_CONNECTING: - case GRPC_CHANNEL_IDLE: - case GRPC_CHANNEL_READY: - GPR_ASSERT(error == GRPC_ERROR_NONE); - break; - case GRPC_CHANNEL_SHUTDOWN: - case GRPC_CHANNEL_TRANSIENT_FAILURE: - GPR_ASSERT(error != GRPC_ERROR_NONE); - break; - } - GRPC_ERROR_UNREF(tracker->current_error); - tracker->current_error = error; - if (cur == state) { - return; - } - GPR_ASSERT(cur != GRPC_CHANNEL_SHUTDOWN); - gpr_atm_no_barrier_store(&tracker->current_state_atm, state); - while ((w = tracker->watchers) != NULL) { - *w->current = state; - tracker->watchers = w->next; - if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { - gpr_log(GPR_DEBUG, "NOTIFY: %p %s: %p", tracker, tracker->name, - w->notify); - } - GRPC_CLOSURE_SCHED(exec_ctx, w->notify, - GRPC_ERROR_REF(tracker->current_error)); - gpr_free(w); - } -} diff --git a/src/core/lib/transport/connectivity_state.cc b/src/core/lib/transport/connectivity_state.cc new file mode 100644 index 0000000000..f328a6cdbb --- /dev/null +++ b/src/core/lib/transport/connectivity_state.cc @@ -0,0 +1,206 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/connectivity_state.h" + +#include + +#include +#include +#include + +grpc_tracer_flag grpc_connectivity_state_trace = + GRPC_TRACER_INITIALIZER(false, "connectivity_state"); + +const char *grpc_connectivity_state_name(grpc_connectivity_state state) { + switch (state) { + case GRPC_CHANNEL_INIT: + return "INIT"; + case GRPC_CHANNEL_IDLE: + return "IDLE"; + case GRPC_CHANNEL_CONNECTING: + return "CONNECTING"; + case GRPC_CHANNEL_READY: + return "READY"; + case GRPC_CHANNEL_TRANSIENT_FAILURE: + return "TRANSIENT_FAILURE"; + case GRPC_CHANNEL_SHUTDOWN: + return "SHUTDOWN"; + } + GPR_UNREACHABLE_CODE(return "UNKNOWN"); +} + +void grpc_connectivity_state_init(grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state init_state, + const char *name) { + gpr_atm_no_barrier_store(&tracker->current_state_atm, init_state); + tracker->current_error = GRPC_ERROR_NONE; + tracker->watchers = NULL; + tracker->name = gpr_strdup(name); +} + +void grpc_connectivity_state_destroy(grpc_exec_ctx *exec_ctx, + grpc_connectivity_state_tracker *tracker) { + grpc_error *error; + grpc_connectivity_state_watcher *w; + while ((w = tracker->watchers)) { + tracker->watchers = w->next; + + if (GRPC_CHANNEL_SHUTDOWN != *w->current) { + *w->current = GRPC_CHANNEL_SHUTDOWN; + error = GRPC_ERROR_NONE; + } else { + error = + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Shutdown connectivity owner"); + } + GRPC_CLOSURE_SCHED(exec_ctx, w->notify, error); + gpr_free(w); + } + GRPC_ERROR_UNREF(tracker->current_error); + gpr_free(tracker->name); +} + +grpc_connectivity_state grpc_connectivity_state_check( + grpc_connectivity_state_tracker *tracker) { + grpc_connectivity_state cur = + (grpc_connectivity_state)gpr_atm_no_barrier_load( + &tracker->current_state_atm); + if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name, + grpc_connectivity_state_name(cur)); + } + return cur; +} + +grpc_connectivity_state grpc_connectivity_state_get( + grpc_connectivity_state_tracker *tracker, grpc_error **error) { + grpc_connectivity_state cur = + (grpc_connectivity_state)gpr_atm_no_barrier_load( + &tracker->current_state_atm); + if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: get %s", tracker, tracker->name, + grpc_connectivity_state_name(cur)); + } + if (error != NULL) { + *error = GRPC_ERROR_REF(tracker->current_error); + } + return cur; +} + +bool grpc_connectivity_state_has_watchers( + grpc_connectivity_state_tracker *connectivity_state) { + return connectivity_state->watchers != NULL; +} + +bool grpc_connectivity_state_notify_on_state_change( + grpc_exec_ctx *exec_ctx, grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state *current, grpc_closure *notify) { + grpc_connectivity_state cur = + (grpc_connectivity_state)gpr_atm_no_barrier_load( + &tracker->current_state_atm); + if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { + if (current == NULL) { + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: unsubscribe notify=%p", tracker, + tracker->name, notify); + } else { + gpr_log(GPR_DEBUG, "CONWATCH: %p %s: from %s [cur=%s] notify=%p", tracker, + tracker->name, grpc_connectivity_state_name(*current), + grpc_connectivity_state_name(cur), notify); + } + } + if (current == NULL) { + grpc_connectivity_state_watcher *w = tracker->watchers; + if (w != NULL && w->notify == notify) { + GRPC_CLOSURE_SCHED(exec_ctx, notify, GRPC_ERROR_CANCELLED); + tracker->watchers = w->next; + gpr_free(w); + return false; + } + while (w != NULL) { + grpc_connectivity_state_watcher *rm_candidate = w->next; + if (rm_candidate != NULL && rm_candidate->notify == notify) { + GRPC_CLOSURE_SCHED(exec_ctx, notify, GRPC_ERROR_CANCELLED); + w->next = w->next->next; + gpr_free(rm_candidate); + return false; + } + w = w->next; + } + return false; + } else { + if (cur != *current) { + *current = cur; + GRPC_CLOSURE_SCHED(exec_ctx, notify, + GRPC_ERROR_REF(tracker->current_error)); + } else { + grpc_connectivity_state_watcher *w = + (grpc_connectivity_state_watcher *)gpr_malloc(sizeof(*w)); + w->current = current; + w->notify = notify; + w->next = tracker->watchers; + tracker->watchers = w; + } + return cur == GRPC_CHANNEL_IDLE; + } +} + +void grpc_connectivity_state_set(grpc_exec_ctx *exec_ctx, + grpc_connectivity_state_tracker *tracker, + grpc_connectivity_state state, + grpc_error *error, const char *reason) { + grpc_connectivity_state cur = + (grpc_connectivity_state)gpr_atm_no_barrier_load( + &tracker->current_state_atm); + grpc_connectivity_state_watcher *w; + if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { + const char *error_string = grpc_error_string(error); + gpr_log(GPR_DEBUG, "SET: %p %s: %s --> %s [%s] error=%p %s", tracker, + tracker->name, grpc_connectivity_state_name(cur), + grpc_connectivity_state_name(state), reason, error, error_string); + } + switch (state) { + case GRPC_CHANNEL_INIT: + case GRPC_CHANNEL_CONNECTING: + case GRPC_CHANNEL_IDLE: + case GRPC_CHANNEL_READY: + GPR_ASSERT(error == GRPC_ERROR_NONE); + break; + case GRPC_CHANNEL_SHUTDOWN: + case GRPC_CHANNEL_TRANSIENT_FAILURE: + GPR_ASSERT(error != GRPC_ERROR_NONE); + break; + } + GRPC_ERROR_UNREF(tracker->current_error); + tracker->current_error = error; + if (cur == state) { + return; + } + GPR_ASSERT(cur != GRPC_CHANNEL_SHUTDOWN); + gpr_atm_no_barrier_store(&tracker->current_state_atm, state); + while ((w = tracker->watchers) != NULL) { + *w->current = state; + tracker->watchers = w->next; + if (GRPC_TRACER_ON(grpc_connectivity_state_trace)) { + gpr_log(GPR_DEBUG, "NOTIFY: %p %s: %p", tracker, tracker->name, + w->notify); + } + GRPC_CLOSURE_SCHED(exec_ctx, w->notify, + GRPC_ERROR_REF(tracker->current_error)); + gpr_free(w); + } +} diff --git a/src/core/lib/transport/error_utils.c b/src/core/lib/transport/error_utils.c deleted file mode 100644 index 5e3920b627..0000000000 --- a/src/core/lib/transport/error_utils.c +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/error_utils.h" - -#include "src/core/lib/iomgr/error_internal.h" -#include "src/core/lib/transport/status_conversion.h" - -static grpc_error *recursively_find_error_with_field(grpc_error *error, - grpc_error_ints which) { - // If the error itself has a status code, return it. - if (grpc_error_get_int(error, which, NULL)) { - return error; - } - if (grpc_error_is_special(error)) return NULL; - // Otherwise, search through its children. - uint8_t slot = error->first_err; - while (slot != UINT8_MAX) { - grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot); - grpc_error *result = recursively_find_error_with_field(lerr->err, which); - if (result) return result; - slot = lerr->next; - } - return NULL; -} - -void grpc_error_get_status(grpc_error *error, gpr_timespec deadline, - grpc_status_code *code, grpc_slice *slice, - grpc_http2_error_code *http_error) { - // Start with the parent error and recurse through the tree of children - // until we find the first one that has a status code. - grpc_error *found_error = - recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS); - if (found_error == NULL) { - /// If no grpc-status exists, retry through the tree to find a http2 error - /// code - found_error = - recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR); - } - - // If we found an error with a status code above, use that; otherwise, - // fall back to using the parent error. - if (found_error == NULL) found_error = error; - - grpc_status_code status = GRPC_STATUS_UNKNOWN; - intptr_t integer; - if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) { - status = (grpc_status_code)integer; - } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, - &integer)) { - status = grpc_http2_error_to_grpc_status((grpc_http2_error_code)integer, - deadline); - } - if (code != NULL) *code = status; - - if (http_error != NULL) { - if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) { - *http_error = (grpc_http2_error_code)integer; - } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, - &integer)) { - *http_error = grpc_status_to_http2_error((grpc_status_code)integer); - } else { - *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR - : GRPC_HTTP2_INTERNAL_ERROR; - } - } - - // If the error has a status message, use it. Otherwise, fall back to - // the error description. - if (slice != NULL) { - if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) { - if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) { - *slice = grpc_slice_from_static_string("unknown error"); - } - } - } - - if (found_error == NULL) found_error = error; -} - -bool grpc_error_has_clear_grpc_status(grpc_error *error) { - if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) { - return true; - } - uint8_t slot = error->first_err; - while (slot != UINT8_MAX) { - grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot); - if (grpc_error_has_clear_grpc_status(lerr->err)) { - return true; - } - slot = lerr->next; - } - return false; -} diff --git a/src/core/lib/transport/error_utils.cc b/src/core/lib/transport/error_utils.cc new file mode 100644 index 0000000000..5e3920b627 --- /dev/null +++ b/src/core/lib/transport/error_utils.cc @@ -0,0 +1,109 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/error_utils.h" + +#include "src/core/lib/iomgr/error_internal.h" +#include "src/core/lib/transport/status_conversion.h" + +static grpc_error *recursively_find_error_with_field(grpc_error *error, + grpc_error_ints which) { + // If the error itself has a status code, return it. + if (grpc_error_get_int(error, which, NULL)) { + return error; + } + if (grpc_error_is_special(error)) return NULL; + // Otherwise, search through its children. + uint8_t slot = error->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot); + grpc_error *result = recursively_find_error_with_field(lerr->err, which); + if (result) return result; + slot = lerr->next; + } + return NULL; +} + +void grpc_error_get_status(grpc_error *error, gpr_timespec deadline, + grpc_status_code *code, grpc_slice *slice, + grpc_http2_error_code *http_error) { + // Start with the parent error and recurse through the tree of children + // until we find the first one that has a status code. + grpc_error *found_error = + recursively_find_error_with_field(error, GRPC_ERROR_INT_GRPC_STATUS); + if (found_error == NULL) { + /// If no grpc-status exists, retry through the tree to find a http2 error + /// code + found_error = + recursively_find_error_with_field(error, GRPC_ERROR_INT_HTTP2_ERROR); + } + + // If we found an error with a status code above, use that; otherwise, + // fall back to using the parent error. + if (found_error == NULL) found_error = error; + + grpc_status_code status = GRPC_STATUS_UNKNOWN; + intptr_t integer; + if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, &integer)) { + status = (grpc_status_code)integer; + } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, + &integer)) { + status = grpc_http2_error_to_grpc_status((grpc_http2_error_code)integer, + deadline); + } + if (code != NULL) *code = status; + + if (http_error != NULL) { + if (grpc_error_get_int(found_error, GRPC_ERROR_INT_HTTP2_ERROR, &integer)) { + *http_error = (grpc_http2_error_code)integer; + } else if (grpc_error_get_int(found_error, GRPC_ERROR_INT_GRPC_STATUS, + &integer)) { + *http_error = grpc_status_to_http2_error((grpc_status_code)integer); + } else { + *http_error = found_error == GRPC_ERROR_NONE ? GRPC_HTTP2_NO_ERROR + : GRPC_HTTP2_INTERNAL_ERROR; + } + } + + // If the error has a status message, use it. Otherwise, fall back to + // the error description. + if (slice != NULL) { + if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_GRPC_MESSAGE, slice)) { + if (!grpc_error_get_str(found_error, GRPC_ERROR_STR_DESCRIPTION, slice)) { + *slice = grpc_slice_from_static_string("unknown error"); + } + } + } + + if (found_error == NULL) found_error = error; +} + +bool grpc_error_has_clear_grpc_status(grpc_error *error) { + if (grpc_error_get_int(error, GRPC_ERROR_INT_GRPC_STATUS, NULL)) { + return true; + } + uint8_t slot = error->first_err; + while (slot != UINT8_MAX) { + grpc_linked_error *lerr = (grpc_linked_error *)(error->arena + slot); + if (grpc_error_has_clear_grpc_status(lerr->err)) { + return true; + } + slot = lerr->next; + } + return false; +} diff --git a/src/core/lib/transport/metadata.c b/src/core/lib/transport/metadata.c deleted file mode 100644 index 188b485625..0000000000 --- a/src/core/lib/transport/metadata.c +++ /dev/null @@ -1,534 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/metadata.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "src/core/lib/iomgr/iomgr_internal.h" -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/murmur_hash.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/static_metadata.h" - -/* There are two kinds of mdelem and mdstr instances. - * Static instances are declared in static_metadata.{h,c} and - * are initialized by grpc_mdctx_global_init(). - * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed - * by internal_string and internal_element structures. - * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are - * used to determine which kind of element a pointer refers to. - */ - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_metadata = - GRPC_TRACER_INITIALIZER(false, "metadata"); -#define DEBUG_ARGS , const char *file, int line -#define FWD_DEBUG_ARGS , file, line -#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__) -#else -#define DEBUG_ARGS -#define FWD_DEBUG_ARGS -#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s)) -#endif - -#define INITIAL_SHARD_CAPACITY 8 -#define LOG2_SHARD_COUNT 4 -#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT)) - -#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity)) -#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1)) - -typedef void (*destroy_user_data_func)(void *user_data); - -/* Shadow structure for grpc_mdelem_data for interned elements */ -typedef struct interned_metadata { - /* must be byte compatible with grpc_mdelem_data */ - grpc_slice key; - grpc_slice value; - - /* private only data */ - gpr_atm refcnt; - - gpr_mu mu_user_data; - gpr_atm destroy_user_data; - gpr_atm user_data; - - struct interned_metadata *bucket_next; -} interned_metadata; - -/* Shadow structure for grpc_mdelem_data for allocated elements */ -typedef struct allocated_metadata { - /* must be byte compatible with grpc_mdelem_data */ - grpc_slice key; - grpc_slice value; - - /* private only data */ - gpr_atm refcnt; -} allocated_metadata; - -typedef struct mdtab_shard { - gpr_mu mu; - interned_metadata **elems; - size_t count; - size_t capacity; - /** Estimate of the number of unreferenced mdelems in the hash table. - This will eventually converge to the exact number, but it's instantaneous - accuracy is not guaranteed */ - gpr_atm free_estimate; -} mdtab_shard; - -static mdtab_shard g_shards[SHARD_COUNT]; - -static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard); - -void grpc_mdctx_global_init(void) { - /* initialize shards */ - for (size_t i = 0; i < SHARD_COUNT; i++) { - mdtab_shard *shard = &g_shards[i]; - gpr_mu_init(&shard->mu); - shard->count = 0; - gpr_atm_no_barrier_store(&shard->free_estimate, 0); - shard->capacity = INITIAL_SHARD_CAPACITY; - shard->elems = (interned_metadata **)gpr_zalloc(sizeof(*shard->elems) * - shard->capacity); - } -} - -void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) { - for (size_t i = 0; i < SHARD_COUNT; i++) { - mdtab_shard *shard = &g_shards[i]; - gpr_mu_destroy(&shard->mu); - gc_mdtab(exec_ctx, shard); - /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ - if (shard->count != 0) { - gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked", - shard->count); - if (grpc_iomgr_abort_on_leaks()) { - abort(); - } - } - gpr_free(shard->elems); - } -} - -static int is_mdelem_static(grpc_mdelem e) { - return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] && - GRPC_MDELEM_DATA(e) < - &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; -} - -static void ref_md_locked(mdtab_shard *shard, - interned_metadata *md DEBUG_ARGS) { -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(md->key); - char *value_str = grpc_slice_to_c_string(md->value); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void *)md, - gpr_atm_no_barrier_load(&md->refcnt), - gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) { - gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1); - } -} - -static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) { - size_t i; - interned_metadata **prev_next; - interned_metadata *md, *next; - gpr_atm num_freed = 0; - - GPR_TIMER_BEGIN("gc_mdtab", 0); - for (i = 0; i < shard->capacity; i++) { - prev_next = &shard->elems[i]; - for (md = shard->elems[i]; md; md = next) { - void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data); - next = md->bucket_next; - if (gpr_atm_acq_load(&md->refcnt) == 0) { - grpc_slice_unref_internal(exec_ctx, md->key); - grpc_slice_unref_internal(exec_ctx, md->value); - if (md->user_data) { - ((destroy_user_data_func)gpr_atm_no_barrier_load( - &md->destroy_user_data))(user_data); - } - gpr_free(md); - *prev_next = next; - num_freed++; - shard->count--; - } else { - prev_next = &md->bucket_next; - } - } - } - gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed); - GPR_TIMER_END("gc_mdtab", 0); -} - -static void grow_mdtab(mdtab_shard *shard) { - size_t capacity = shard->capacity * 2; - size_t i; - interned_metadata **mdtab; - interned_metadata *md, *next; - uint32_t hash; - - GPR_TIMER_BEGIN("grow_mdtab", 0); - - mdtab = - (interned_metadata **)gpr_zalloc(sizeof(interned_metadata *) * capacity); - - for (i = 0; i < shard->capacity; i++) { - for (md = shard->elems[i]; md; md = next) { - size_t idx; - hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key), - grpc_slice_hash(md->value)); - next = md->bucket_next; - idx = TABLE_IDX(hash, capacity); - md->bucket_next = mdtab[idx]; - mdtab[idx] = md; - } - } - - gpr_free(shard->elems); - shard->elems = mdtab; - shard->capacity = capacity; - - GPR_TIMER_END("grow_mdtab", 0); -} - -static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) { - if (gpr_atm_no_barrier_load(&shard->free_estimate) > - (gpr_atm)(shard->capacity / 4)) { - gc_mdtab(exec_ctx, shard); - } else { - grow_mdtab(shard); - } -} - -grpc_mdelem grpc_mdelem_create( - grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value, - grpc_mdelem_data *compatible_external_backing_store) { - if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) { - if (compatible_external_backing_store != NULL) { - return GRPC_MAKE_MDELEM(compatible_external_backing_store, - GRPC_MDELEM_STORAGE_EXTERNAL); - } - - allocated_metadata *allocated = - (allocated_metadata *)gpr_malloc(sizeof(*allocated)); - allocated->key = grpc_slice_ref_internal(key); - allocated->value = grpc_slice_ref_internal(value); - gpr_atm_rel_store(&allocated->refcnt, 1); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(allocated->key); - char *value_str = grpc_slice_to_c_string(allocated->value); - gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'", - (void *)allocated, gpr_atm_no_barrier_load(&allocated->refcnt), - key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED); - } - - if (GRPC_IS_STATIC_METADATA_STRING(key) && - GRPC_IS_STATIC_METADATA_STRING(value)) { - grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings( - GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value)); - if (!GRPC_MDISNULL(static_elem)) { - return static_elem; - } - } - - uint32_t hash = - GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value)); - interned_metadata *md; - mdtab_shard *shard = &g_shards[SHARD_IDX(hash)]; - size_t idx; - - GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0); - - gpr_mu_lock(&shard->mu); - - idx = TABLE_IDX(hash, shard->capacity); - /* search for an existing pair */ - for (md = shard->elems[idx]; md; md = md->bucket_next) { - if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) { - REF_MD_LOCKED(shard, md); - gpr_mu_unlock(&shard->mu); - GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); - return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED); - } - } - - /* not found: create a new pair */ - md = (interned_metadata *)gpr_malloc(sizeof(interned_metadata)); - gpr_atm_rel_store(&md->refcnt, 1); - md->key = grpc_slice_ref_internal(key); - md->value = grpc_slice_ref_internal(value); - md->user_data = 0; - md->destroy_user_data = 0; - md->bucket_next = shard->elems[idx]; - shard->elems[idx] = md; - gpr_mu_init(&md->mu_user_data); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(md->key); - char *value_str = grpc_slice_to_c_string(md->value); - gpr_log(GPR_DEBUG, "ELM NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void *)md, - gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - shard->count++; - - if (shard->count > shard->capacity * 2) { - rehash_mdtab(exec_ctx, shard); - } - - gpr_mu_unlock(&shard->mu); - - GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); - - return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED); -} - -grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key, - grpc_slice value) { - grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL); - grpc_slice_unref_internal(exec_ctx, key); - grpc_slice_unref_internal(exec_ctx, value); - return out; -} - -grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx, - grpc_metadata *metadata) { - bool changed = false; - grpc_slice key_slice = - grpc_slice_maybe_static_intern(metadata->key, &changed); - grpc_slice value_slice = - grpc_slice_maybe_static_intern(metadata->value, &changed); - return grpc_mdelem_create(exec_ctx, key_slice, value_slice, - changed ? NULL : (grpc_mdelem_data *)metadata); -} - -static size_t get_base64_encoded_size(size_t raw_length) { - static const uint8_t tail_xtra[3] = {0, 2, 3}; - return raw_length / 3 * 4 + tail_xtra[raw_length % 3]; -} - -size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) { - size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); - size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem)); - if (grpc_is_binary_header(GRPC_MDKEY(elem))) { - return overhead_and_key + get_base64_encoded_size(value_len); - } else { - return overhead_and_key + value_len; - } -} - -grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) { - switch (GRPC_MDELEM_STORAGE(gmd)) { - case GRPC_MDELEM_STORAGE_EXTERNAL: - case GRPC_MDELEM_STORAGE_STATIC: - break; - case GRPC_MDELEM_STORAGE_INTERNED: { - interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(md->key); - char *value_str = grpc_slice_to_c_string(md->value); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", - (void *)md, gpr_atm_no_barrier_load(&md->refcnt), - gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - /* we can assume the ref count is >= 1 as the application is calling - this function - meaning that no adjustment to mdtab_free is necessary, - simplifying the logic here to be just an atomic increment */ - /* use C assert to have this removed in opt builds */ - GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1); - gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); - break; - } - case GRPC_MDELEM_STORAGE_ALLOCATED: { - allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(md->key); - char *value_str = grpc_slice_to_c_string(md->value); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", - (void *)md, gpr_atm_no_barrier_load(&md->refcnt), - gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - /* we can assume the ref count is >= 1 as the application is calling - this function - meaning that no adjustment to mdtab_free is necessary, - simplifying the logic here to be just an atomic increment */ - /* use C assert to have this removed in opt builds */ - gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); - break; - } - } - return gmd; -} - -void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem gmd DEBUG_ARGS) { - switch (GRPC_MDELEM_STORAGE(gmd)) { - case GRPC_MDELEM_STORAGE_EXTERNAL: - case GRPC_MDELEM_STORAGE_STATIC: - break; - case GRPC_MDELEM_STORAGE_INTERNED: { - interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(md->key); - char *value_str = grpc_slice_to_c_string(md->value); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", - (void *)md, gpr_atm_no_barrier_load(&md->refcnt), - gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key), - grpc_slice_hash(md->value)); - const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1); - GPR_ASSERT(prev_refcount >= 1); - if (1 == prev_refcount) { - /* once the refcount hits zero, some other thread can come along and - free md at any time: it's unsafe from this point on to access it */ - mdtab_shard *shard = &g_shards[SHARD_IDX(hash)]; - gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1); - } - break; - } - case GRPC_MDELEM_STORAGE_ALLOCATED: { - allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd); -#ifndef NDEBUG - if (GRPC_TRACER_ON(grpc_trace_metadata)) { - char *key_str = grpc_slice_to_c_string(md->key); - char *value_str = grpc_slice_to_c_string(md->value); - gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, - "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", - (void *)md, gpr_atm_no_barrier_load(&md->refcnt), - gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str); - gpr_free(key_str); - gpr_free(value_str); - } -#endif - const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1); - GPR_ASSERT(prev_refcount >= 1); - if (1 == prev_refcount) { - grpc_slice_unref_internal(exec_ctx, md->key); - grpc_slice_unref_internal(exec_ctx, md->value); - gpr_free(md); - } - break; - } - } -} - -void *grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void *)) { - switch (GRPC_MDELEM_STORAGE(md)) { - case GRPC_MDELEM_STORAGE_EXTERNAL: - case GRPC_MDELEM_STORAGE_ALLOCATED: - return NULL; - case GRPC_MDELEM_STORAGE_STATIC: - return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) - - grpc_static_mdelem_table]; - case GRPC_MDELEM_STORAGE_INTERNED: { - interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md); - void *result; - if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) { - return (void *)gpr_atm_no_barrier_load(&im->user_data); - } else { - return NULL; - } - return result; - } - } - GPR_UNREACHABLE_CODE(return NULL); -} - -void *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *), - void *user_data) { - switch (GRPC_MDELEM_STORAGE(md)) { - case GRPC_MDELEM_STORAGE_EXTERNAL: - case GRPC_MDELEM_STORAGE_ALLOCATED: - destroy_func(user_data); - return NULL; - case GRPC_MDELEM_STORAGE_STATIC: - destroy_func(user_data); - return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) - - grpc_static_mdelem_table]; - case GRPC_MDELEM_STORAGE_INTERNED: { - interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md); - GPR_ASSERT(!is_mdelem_static(md)); - GPR_ASSERT((user_data == NULL) == (destroy_func == NULL)); - gpr_mu_lock(&im->mu_user_data); - if (gpr_atm_no_barrier_load(&im->destroy_user_data)) { - /* user data can only be set once */ - gpr_mu_unlock(&im->mu_user_data); - if (destroy_func != NULL) { - destroy_func(user_data); - } - return (void *)gpr_atm_no_barrier_load(&im->user_data); - } - gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data); - gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func); - gpr_mu_unlock(&im->mu_user_data); - return user_data; - } - } - GPR_UNREACHABLE_CODE(return NULL); -} - -bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) { - if (a.payload == b.payload) return true; - if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false; - if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false; - return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) && - grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b)); -} diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc new file mode 100644 index 0000000000..188b485625 --- /dev/null +++ b/src/core/lib/transport/metadata.cc @@ -0,0 +1,534 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/metadata.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/iomgr/iomgr_internal.h" +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/murmur_hash.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/static_metadata.h" + +/* There are two kinds of mdelem and mdstr instances. + * Static instances are declared in static_metadata.{h,c} and + * are initialized by grpc_mdctx_global_init(). + * Dynamic instances are stored in hash tables on grpc_mdctx, and are backed + * by internal_string and internal_element structures. + * Internal helper functions here-in (is_mdstr_static, is_mdelem_static) are + * used to determine which kind of element a pointer refers to. + */ + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_metadata = + GRPC_TRACER_INITIALIZER(false, "metadata"); +#define DEBUG_ARGS , const char *file, int line +#define FWD_DEBUG_ARGS , file, line +#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s), __FILE__, __LINE__) +#else +#define DEBUG_ARGS +#define FWD_DEBUG_ARGS +#define REF_MD_LOCKED(shard, s) ref_md_locked((shard), (s)) +#endif + +#define INITIAL_SHARD_CAPACITY 8 +#define LOG2_SHARD_COUNT 4 +#define SHARD_COUNT ((size_t)(1 << LOG2_SHARD_COUNT)) + +#define TABLE_IDX(hash, capacity) (((hash) >> (LOG2_SHARD_COUNT)) % (capacity)) +#define SHARD_IDX(hash) ((hash) & ((1 << (LOG2_SHARD_COUNT)) - 1)) + +typedef void (*destroy_user_data_func)(void *user_data); + +/* Shadow structure for grpc_mdelem_data for interned elements */ +typedef struct interned_metadata { + /* must be byte compatible with grpc_mdelem_data */ + grpc_slice key; + grpc_slice value; + + /* private only data */ + gpr_atm refcnt; + + gpr_mu mu_user_data; + gpr_atm destroy_user_data; + gpr_atm user_data; + + struct interned_metadata *bucket_next; +} interned_metadata; + +/* Shadow structure for grpc_mdelem_data for allocated elements */ +typedef struct allocated_metadata { + /* must be byte compatible with grpc_mdelem_data */ + grpc_slice key; + grpc_slice value; + + /* private only data */ + gpr_atm refcnt; +} allocated_metadata; + +typedef struct mdtab_shard { + gpr_mu mu; + interned_metadata **elems; + size_t count; + size_t capacity; + /** Estimate of the number of unreferenced mdelems in the hash table. + This will eventually converge to the exact number, but it's instantaneous + accuracy is not guaranteed */ + gpr_atm free_estimate; +} mdtab_shard; + +static mdtab_shard g_shards[SHARD_COUNT]; + +static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard); + +void grpc_mdctx_global_init(void) { + /* initialize shards */ + for (size_t i = 0; i < SHARD_COUNT; i++) { + mdtab_shard *shard = &g_shards[i]; + gpr_mu_init(&shard->mu); + shard->count = 0; + gpr_atm_no_barrier_store(&shard->free_estimate, 0); + shard->capacity = INITIAL_SHARD_CAPACITY; + shard->elems = (interned_metadata **)gpr_zalloc(sizeof(*shard->elems) * + shard->capacity); + } +} + +void grpc_mdctx_global_shutdown(grpc_exec_ctx *exec_ctx) { + for (size_t i = 0; i < SHARD_COUNT; i++) { + mdtab_shard *shard = &g_shards[i]; + gpr_mu_destroy(&shard->mu); + gc_mdtab(exec_ctx, shard); + /* TODO(ctiller): GPR_ASSERT(shard->count == 0); */ + if (shard->count != 0) { + gpr_log(GPR_DEBUG, "WARNING: %" PRIuPTR " metadata elements were leaked", + shard->count); + if (grpc_iomgr_abort_on_leaks()) { + abort(); + } + } + gpr_free(shard->elems); + } +} + +static int is_mdelem_static(grpc_mdelem e) { + return GRPC_MDELEM_DATA(e) >= &grpc_static_mdelem_table[0] && + GRPC_MDELEM_DATA(e) < + &grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT]; +} + +static void ref_md_locked(mdtab_shard *shard, + interned_metadata *md DEBUG_ARGS) { +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(md->key); + char *value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", (void *)md, + gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + if (0 == gpr_atm_no_barrier_fetch_add(&md->refcnt, 1)) { + gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -1); + } +} + +static void gc_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) { + size_t i; + interned_metadata **prev_next; + interned_metadata *md, *next; + gpr_atm num_freed = 0; + + GPR_TIMER_BEGIN("gc_mdtab", 0); + for (i = 0; i < shard->capacity; i++) { + prev_next = &shard->elems[i]; + for (md = shard->elems[i]; md; md = next) { + void *user_data = (void *)gpr_atm_no_barrier_load(&md->user_data); + next = md->bucket_next; + if (gpr_atm_acq_load(&md->refcnt) == 0) { + grpc_slice_unref_internal(exec_ctx, md->key); + grpc_slice_unref_internal(exec_ctx, md->value); + if (md->user_data) { + ((destroy_user_data_func)gpr_atm_no_barrier_load( + &md->destroy_user_data))(user_data); + } + gpr_free(md); + *prev_next = next; + num_freed++; + shard->count--; + } else { + prev_next = &md->bucket_next; + } + } + } + gpr_atm_no_barrier_fetch_add(&shard->free_estimate, -num_freed); + GPR_TIMER_END("gc_mdtab", 0); +} + +static void grow_mdtab(mdtab_shard *shard) { + size_t capacity = shard->capacity * 2; + size_t i; + interned_metadata **mdtab; + interned_metadata *md, *next; + uint32_t hash; + + GPR_TIMER_BEGIN("grow_mdtab", 0); + + mdtab = + (interned_metadata **)gpr_zalloc(sizeof(interned_metadata *) * capacity); + + for (i = 0; i < shard->capacity; i++) { + for (md = shard->elems[i]; md; md = next) { + size_t idx; + hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key), + grpc_slice_hash(md->value)); + next = md->bucket_next; + idx = TABLE_IDX(hash, capacity); + md->bucket_next = mdtab[idx]; + mdtab[idx] = md; + } + } + + gpr_free(shard->elems); + shard->elems = mdtab; + shard->capacity = capacity; + + GPR_TIMER_END("grow_mdtab", 0); +} + +static void rehash_mdtab(grpc_exec_ctx *exec_ctx, mdtab_shard *shard) { + if (gpr_atm_no_barrier_load(&shard->free_estimate) > + (gpr_atm)(shard->capacity / 4)) { + gc_mdtab(exec_ctx, shard); + } else { + grow_mdtab(shard); + } +} + +grpc_mdelem grpc_mdelem_create( + grpc_exec_ctx *exec_ctx, grpc_slice key, grpc_slice value, + grpc_mdelem_data *compatible_external_backing_store) { + if (!grpc_slice_is_interned(key) || !grpc_slice_is_interned(value)) { + if (compatible_external_backing_store != NULL) { + return GRPC_MAKE_MDELEM(compatible_external_backing_store, + GRPC_MDELEM_STORAGE_EXTERNAL); + } + + allocated_metadata *allocated = + (allocated_metadata *)gpr_malloc(sizeof(*allocated)); + allocated->key = grpc_slice_ref_internal(key); + allocated->value = grpc_slice_ref_internal(value); + gpr_atm_rel_store(&allocated->refcnt, 1); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(allocated->key); + char *value_str = grpc_slice_to_c_string(allocated->value); + gpr_log(GPR_DEBUG, "ELM ALLOC:%p:%" PRIdPTR ": '%s' = '%s'", + (void *)allocated, gpr_atm_no_barrier_load(&allocated->refcnt), + key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + return GRPC_MAKE_MDELEM(allocated, GRPC_MDELEM_STORAGE_ALLOCATED); + } + + if (GRPC_IS_STATIC_METADATA_STRING(key) && + GRPC_IS_STATIC_METADATA_STRING(value)) { + grpc_mdelem static_elem = grpc_static_mdelem_for_static_strings( + GRPC_STATIC_METADATA_INDEX(key), GRPC_STATIC_METADATA_INDEX(value)); + if (!GRPC_MDISNULL(static_elem)) { + return static_elem; + } + } + + uint32_t hash = + GRPC_MDSTR_KV_HASH(grpc_slice_hash(key), grpc_slice_hash(value)); + interned_metadata *md; + mdtab_shard *shard = &g_shards[SHARD_IDX(hash)]; + size_t idx; + + GPR_TIMER_BEGIN("grpc_mdelem_from_metadata_strings", 0); + + gpr_mu_lock(&shard->mu); + + idx = TABLE_IDX(hash, shard->capacity); + /* search for an existing pair */ + for (md = shard->elems[idx]; md; md = md->bucket_next) { + if (grpc_slice_eq(key, md->key) && grpc_slice_eq(value, md->value)) { + REF_MD_LOCKED(shard, md); + gpr_mu_unlock(&shard->mu); + GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); + return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED); + } + } + + /* not found: create a new pair */ + md = (interned_metadata *)gpr_malloc(sizeof(interned_metadata)); + gpr_atm_rel_store(&md->refcnt, 1); + md->key = grpc_slice_ref_internal(key); + md->value = grpc_slice_ref_internal(value); + md->user_data = 0; + md->destroy_user_data = 0; + md->bucket_next = shard->elems[idx]; + shard->elems[idx] = md; + gpr_mu_init(&md->mu_user_data); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(md->key); + char *value_str = grpc_slice_to_c_string(md->value); + gpr_log(GPR_DEBUG, "ELM NEW:%p:%" PRIdPTR ": '%s' = '%s'", (void *)md, + gpr_atm_no_barrier_load(&md->refcnt), key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + shard->count++; + + if (shard->count > shard->capacity * 2) { + rehash_mdtab(exec_ctx, shard); + } + + gpr_mu_unlock(&shard->mu); + + GPR_TIMER_END("grpc_mdelem_from_metadata_strings", 0); + + return GRPC_MAKE_MDELEM(md, GRPC_MDELEM_STORAGE_INTERNED); +} + +grpc_mdelem grpc_mdelem_from_slices(grpc_exec_ctx *exec_ctx, grpc_slice key, + grpc_slice value) { + grpc_mdelem out = grpc_mdelem_create(exec_ctx, key, value, NULL); + grpc_slice_unref_internal(exec_ctx, key); + grpc_slice_unref_internal(exec_ctx, value); + return out; +} + +grpc_mdelem grpc_mdelem_from_grpc_metadata(grpc_exec_ctx *exec_ctx, + grpc_metadata *metadata) { + bool changed = false; + grpc_slice key_slice = + grpc_slice_maybe_static_intern(metadata->key, &changed); + grpc_slice value_slice = + grpc_slice_maybe_static_intern(metadata->value, &changed); + return grpc_mdelem_create(exec_ctx, key_slice, value_slice, + changed ? NULL : (grpc_mdelem_data *)metadata); +} + +static size_t get_base64_encoded_size(size_t raw_length) { + static const uint8_t tail_xtra[3] = {0, 2, 3}; + return raw_length / 3 * 4 + tail_xtra[raw_length % 3]; +} + +size_t grpc_mdelem_get_size_in_hpack_table(grpc_mdelem elem) { + size_t overhead_and_key = 32 + GRPC_SLICE_LENGTH(GRPC_MDKEY(elem)); + size_t value_len = GRPC_SLICE_LENGTH(GRPC_MDVALUE(elem)); + if (grpc_is_binary_header(GRPC_MDKEY(elem))) { + return overhead_and_key + get_base64_encoded_size(value_len); + } else { + return overhead_and_key + value_len; + } +} + +grpc_mdelem grpc_mdelem_ref(grpc_mdelem gmd DEBUG_ARGS) { + switch (GRPC_MDELEM_STORAGE(gmd)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_STATIC: + break; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(md->key); + char *value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void *)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + /* we can assume the ref count is >= 1 as the application is calling + this function - meaning that no adjustment to mdtab_free is necessary, + simplifying the logic here to be just an atomic increment */ + /* use C assert to have this removed in opt builds */ + GPR_ASSERT(gpr_atm_no_barrier_load(&md->refcnt) >= 1); + gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); + break; + } + case GRPC_MDELEM_STORAGE_ALLOCATED: { + allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(md->key); + char *value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM REF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void *)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) + 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + /* we can assume the ref count is >= 1 as the application is calling + this function - meaning that no adjustment to mdtab_free is necessary, + simplifying the logic here to be just an atomic increment */ + /* use C assert to have this removed in opt builds */ + gpr_atm_no_barrier_fetch_add(&md->refcnt, 1); + break; + } + } + return gmd; +} + +void grpc_mdelem_unref(grpc_exec_ctx *exec_ctx, grpc_mdelem gmd DEBUG_ARGS) { + switch (GRPC_MDELEM_STORAGE(gmd)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_STATIC: + break; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata *md = (interned_metadata *)GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(md->key); + char *value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void *)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + uint32_t hash = GRPC_MDSTR_KV_HASH(grpc_slice_hash(md->key), + grpc_slice_hash(md->value)); + const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1); + GPR_ASSERT(prev_refcount >= 1); + if (1 == prev_refcount) { + /* once the refcount hits zero, some other thread can come along and + free md at any time: it's unsafe from this point on to access it */ + mdtab_shard *shard = &g_shards[SHARD_IDX(hash)]; + gpr_atm_no_barrier_fetch_add(&shard->free_estimate, 1); + } + break; + } + case GRPC_MDELEM_STORAGE_ALLOCATED: { + allocated_metadata *md = (allocated_metadata *)GRPC_MDELEM_DATA(gmd); +#ifndef NDEBUG + if (GRPC_TRACER_ON(grpc_trace_metadata)) { + char *key_str = grpc_slice_to_c_string(md->key); + char *value_str = grpc_slice_to_c_string(md->value); + gpr_log(file, line, GPR_LOG_SEVERITY_DEBUG, + "ELM UNREF:%p:%" PRIdPTR "->%" PRIdPTR ": '%s' = '%s'", + (void *)md, gpr_atm_no_barrier_load(&md->refcnt), + gpr_atm_no_barrier_load(&md->refcnt) - 1, key_str, value_str); + gpr_free(key_str); + gpr_free(value_str); + } +#endif + const gpr_atm prev_refcount = gpr_atm_full_fetch_add(&md->refcnt, -1); + GPR_ASSERT(prev_refcount >= 1); + if (1 == prev_refcount) { + grpc_slice_unref_internal(exec_ctx, md->key); + grpc_slice_unref_internal(exec_ctx, md->value); + gpr_free(md); + } + break; + } + } +} + +void *grpc_mdelem_get_user_data(grpc_mdelem md, void (*destroy_func)(void *)) { + switch (GRPC_MDELEM_STORAGE(md)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_ALLOCATED: + return NULL; + case GRPC_MDELEM_STORAGE_STATIC: + return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) - + grpc_static_mdelem_table]; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md); + void *result; + if (gpr_atm_acq_load(&im->destroy_user_data) == (gpr_atm)destroy_func) { + return (void *)gpr_atm_no_barrier_load(&im->user_data); + } else { + return NULL; + } + return result; + } + } + GPR_UNREACHABLE_CODE(return NULL); +} + +void *grpc_mdelem_set_user_data(grpc_mdelem md, void (*destroy_func)(void *), + void *user_data) { + switch (GRPC_MDELEM_STORAGE(md)) { + case GRPC_MDELEM_STORAGE_EXTERNAL: + case GRPC_MDELEM_STORAGE_ALLOCATED: + destroy_func(user_data); + return NULL; + case GRPC_MDELEM_STORAGE_STATIC: + destroy_func(user_data); + return (void *)grpc_static_mdelem_user_data[GRPC_MDELEM_DATA(md) - + grpc_static_mdelem_table]; + case GRPC_MDELEM_STORAGE_INTERNED: { + interned_metadata *im = (interned_metadata *)GRPC_MDELEM_DATA(md); + GPR_ASSERT(!is_mdelem_static(md)); + GPR_ASSERT((user_data == NULL) == (destroy_func == NULL)); + gpr_mu_lock(&im->mu_user_data); + if (gpr_atm_no_barrier_load(&im->destroy_user_data)) { + /* user data can only be set once */ + gpr_mu_unlock(&im->mu_user_data); + if (destroy_func != NULL) { + destroy_func(user_data); + } + return (void *)gpr_atm_no_barrier_load(&im->user_data); + } + gpr_atm_no_barrier_store(&im->user_data, (gpr_atm)user_data); + gpr_atm_rel_store(&im->destroy_user_data, (gpr_atm)destroy_func); + gpr_mu_unlock(&im->mu_user_data); + return user_data; + } + } + GPR_UNREACHABLE_CODE(return NULL); +} + +bool grpc_mdelem_eq(grpc_mdelem a, grpc_mdelem b) { + if (a.payload == b.payload) return true; + if (GRPC_MDELEM_IS_INTERNED(a) && GRPC_MDELEM_IS_INTERNED(b)) return false; + if (GRPC_MDISNULL(a) || GRPC_MDISNULL(b)) return false; + return grpc_slice_eq(GRPC_MDKEY(a), GRPC_MDKEY(b)) && + grpc_slice_eq(GRPC_MDVALUE(a), GRPC_MDVALUE(b)); +} diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c deleted file mode 100644 index 54388bdcda..0000000000 --- a/src/core/lib/transport/metadata_batch.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/metadata_batch.h" - -#include -#include - -#include -#include - -#include "src/core/lib/profiling/timers.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" - -static void assert_valid_list(grpc_mdelem_list *list) { -#ifndef NDEBUG - grpc_linked_mdelem *l; - - GPR_ASSERT((list->head == NULL) == (list->tail == NULL)); - if (!list->head) return; - GPR_ASSERT(list->head->prev == NULL); - GPR_ASSERT(list->tail->next == NULL); - GPR_ASSERT((list->head == list->tail) == (list->head->next == NULL)); - - size_t verified_count = 0; - for (l = list->head; l; l = l->next) { - GPR_ASSERT(!GRPC_MDISNULL(l->md)); - GPR_ASSERT((l->prev == NULL) == (l == list->head)); - GPR_ASSERT((l->next == NULL) == (l == list->tail)); - if (l->next) GPR_ASSERT(l->next->prev == l); - if (l->prev) GPR_ASSERT(l->prev->next == l); - verified_count++; - } - GPR_ASSERT(list->count == verified_count); -#endif /* NDEBUG */ -} - -static void assert_valid_callouts(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch) { -#ifndef NDEBUG - for (grpc_linked_mdelem *l = batch->list.head; l != NULL; l = l->next) { - grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md)); - grpc_metadata_batch_callouts_index callout_idx = - GRPC_BATCH_INDEX_OF(key_interned); - if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) { - GPR_ASSERT(batch->idx.array[callout_idx] == l); - } - grpc_slice_unref_internal(exec_ctx, key_interned); - } -#endif -} - -#ifndef NDEBUG -void grpc_metadata_batch_assert_ok(grpc_metadata_batch *batch) { - assert_valid_list(&batch->list); -} -#endif /* NDEBUG */ - -void grpc_metadata_batch_init(grpc_metadata_batch *batch) { - memset(batch, 0, sizeof(*batch)); - batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); -} - -void grpc_metadata_batch_destroy(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch) { - grpc_linked_mdelem *l; - for (l = batch->list.head; l; l = l->next) { - GRPC_MDELEM_UNREF(exec_ctx, l->md); - } -} - -grpc_error *grpc_attach_md_to_error(grpc_error *src, grpc_mdelem md) { - grpc_error *out = grpc_error_set_str( - grpc_error_set_str(src, GRPC_ERROR_STR_KEY, - grpc_slice_ref_internal(GRPC_MDKEY(md))), - GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md))); - return out; -} - -static grpc_error *maybe_link_callout(grpc_metadata_batch *batch, - grpc_linked_mdelem *storage) - GRPC_MUST_USE_RESULT; - -static grpc_error *maybe_link_callout(grpc_metadata_batch *batch, - grpc_linked_mdelem *storage) { - grpc_metadata_batch_callouts_index idx = - GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); - if (idx == GRPC_BATCH_CALLOUTS_COUNT) { - return GRPC_ERROR_NONE; - } - if (batch->idx.array[idx] == NULL) { - if (grpc_static_callout_is_default[idx]) ++batch->list.default_count; - batch->idx.array[idx] = storage; - return GRPC_ERROR_NONE; - } - return grpc_attach_md_to_error( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), - storage->md); -} - -static void maybe_unlink_callout(grpc_metadata_batch *batch, - grpc_linked_mdelem *storage) { - grpc_metadata_batch_callouts_index idx = - GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); - if (idx == GRPC_BATCH_CALLOUTS_COUNT) { - return; - } - if (grpc_static_callout_is_default[idx]) --batch->list.default_count; - GPR_ASSERT(batch->idx.array[idx] != NULL); - batch->idx.array[idx] = NULL; -} - -grpc_error *grpc_metadata_batch_add_head(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_linked_mdelem *storage, - grpc_mdelem elem_to_add) { - GPR_ASSERT(!GRPC_MDISNULL(elem_to_add)); - storage->md = elem_to_add; - return grpc_metadata_batch_link_head(exec_ctx, batch, storage); -} - -static void link_head(grpc_mdelem_list *list, grpc_linked_mdelem *storage) { - assert_valid_list(list); - GPR_ASSERT(!GRPC_MDISNULL(storage->md)); - storage->prev = NULL; - storage->next = list->head; - if (list->head != NULL) { - list->head->prev = storage; - } else { - list->tail = storage; - } - list->head = storage; - list->count++; - assert_valid_list(list); -} - -grpc_error *grpc_metadata_batch_link_head(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_linked_mdelem *storage) { - assert_valid_callouts(exec_ctx, batch); - grpc_error *err = maybe_link_callout(batch, storage); - if (err != GRPC_ERROR_NONE) { - assert_valid_callouts(exec_ctx, batch); - return err; - } - link_head(&batch->list, storage); - assert_valid_callouts(exec_ctx, batch); - return GRPC_ERROR_NONE; -} - -grpc_error *grpc_metadata_batch_add_tail(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_linked_mdelem *storage, - grpc_mdelem elem_to_add) { - GPR_ASSERT(!GRPC_MDISNULL(elem_to_add)); - storage->md = elem_to_add; - return grpc_metadata_batch_link_tail(exec_ctx, batch, storage); -} - -static void link_tail(grpc_mdelem_list *list, grpc_linked_mdelem *storage) { - assert_valid_list(list); - GPR_ASSERT(!GRPC_MDISNULL(storage->md)); - storage->prev = list->tail; - storage->next = NULL; - storage->reserved = NULL; - if (list->tail != NULL) { - list->tail->next = storage; - } else { - list->head = storage; - } - list->tail = storage; - list->count++; - assert_valid_list(list); -} - -grpc_error *grpc_metadata_batch_link_tail(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_linked_mdelem *storage) { - assert_valid_callouts(exec_ctx, batch); - grpc_error *err = maybe_link_callout(batch, storage); - if (err != GRPC_ERROR_NONE) { - assert_valid_callouts(exec_ctx, batch); - return err; - } - link_tail(&batch->list, storage); - assert_valid_callouts(exec_ctx, batch); - return GRPC_ERROR_NONE; -} - -static void unlink_storage(grpc_mdelem_list *list, - grpc_linked_mdelem *storage) { - assert_valid_list(list); - if (storage->prev != NULL) { - storage->prev->next = storage->next; - } else { - list->head = storage->next; - } - if (storage->next != NULL) { - storage->next->prev = storage->prev; - } else { - list->tail = storage->prev; - } - list->count--; - assert_valid_list(list); -} - -void grpc_metadata_batch_remove(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_linked_mdelem *storage) { - assert_valid_callouts(exec_ctx, batch); - maybe_unlink_callout(batch, storage); - unlink_storage(&batch->list, storage); - GRPC_MDELEM_UNREF(exec_ctx, storage->md); - assert_valid_callouts(exec_ctx, batch); -} - -void grpc_metadata_batch_set_value(grpc_exec_ctx *exec_ctx, - grpc_linked_mdelem *storage, - grpc_slice value) { - grpc_mdelem old_mdelem = storage->md; - grpc_mdelem new_mdelem = grpc_mdelem_from_slices( - exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value); - storage->md = new_mdelem; - GRPC_MDELEM_UNREF(exec_ctx, old_mdelem); -} - -grpc_error *grpc_metadata_batch_substitute(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_linked_mdelem *storage, - grpc_mdelem new_mdelem) { - assert_valid_callouts(exec_ctx, batch); - grpc_error *error = GRPC_ERROR_NONE; - grpc_mdelem old_mdelem = storage->md; - if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) { - maybe_unlink_callout(batch, storage); - storage->md = new_mdelem; - error = maybe_link_callout(batch, storage); - if (error != GRPC_ERROR_NONE) { - unlink_storage(&batch->list, storage); - GRPC_MDELEM_UNREF(exec_ctx, storage->md); - } - } else { - storage->md = new_mdelem; - } - GRPC_MDELEM_UNREF(exec_ctx, old_mdelem); - assert_valid_callouts(exec_ctx, batch); - return error; -} - -void grpc_metadata_batch_clear(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch) { - grpc_metadata_batch_destroy(exec_ctx, batch); - grpc_metadata_batch_init(batch); -} - -bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) { - return batch->list.head == NULL && - gpr_time_cmp(gpr_inf_future(batch->deadline.clock_type), - batch->deadline) == 0; -} - -size_t grpc_metadata_batch_size(grpc_metadata_batch *batch) { - size_t size = 0; - for (grpc_linked_mdelem *elem = batch->list.head; elem != NULL; - elem = elem->next) { - size += GRPC_MDELEM_LENGTH(elem->md); - } - return size; -} - -static void add_error(grpc_error **composite, grpc_error *error, - const char *composite_error_string) { - if (error == GRPC_ERROR_NONE) return; - if (*composite == GRPC_ERROR_NONE) { - *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string); - } - *composite = grpc_error_add_child(*composite, error); -} - -grpc_error *grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx, - grpc_metadata_batch *batch, - grpc_metadata_batch_filter_func func, - void *user_data, - const char *composite_error_string) { - grpc_linked_mdelem *l = batch->list.head; - grpc_error *error = GRPC_ERROR_NONE; - while (l) { - grpc_linked_mdelem *next = l->next; - grpc_filtered_mdelem new_mdelem = func(exec_ctx, user_data, l->md); - add_error(&error, new_mdelem.error, composite_error_string); - if (GRPC_MDISNULL(new_mdelem.md)) { - grpc_metadata_batch_remove(exec_ctx, batch, l); - } else if (new_mdelem.md.payload != l->md.payload) { - grpc_metadata_batch_substitute(exec_ctx, batch, l, new_mdelem.md); - } - l = next; - } - return error; -} diff --git a/src/core/lib/transport/metadata_batch.cc b/src/core/lib/transport/metadata_batch.cc new file mode 100644 index 0000000000..54388bdcda --- /dev/null +++ b/src/core/lib/transport/metadata_batch.cc @@ -0,0 +1,315 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/metadata_batch.h" + +#include +#include + +#include +#include + +#include "src/core/lib/profiling/timers.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" + +static void assert_valid_list(grpc_mdelem_list *list) { +#ifndef NDEBUG + grpc_linked_mdelem *l; + + GPR_ASSERT((list->head == NULL) == (list->tail == NULL)); + if (!list->head) return; + GPR_ASSERT(list->head->prev == NULL); + GPR_ASSERT(list->tail->next == NULL); + GPR_ASSERT((list->head == list->tail) == (list->head->next == NULL)); + + size_t verified_count = 0; + for (l = list->head; l; l = l->next) { + GPR_ASSERT(!GRPC_MDISNULL(l->md)); + GPR_ASSERT((l->prev == NULL) == (l == list->head)); + GPR_ASSERT((l->next == NULL) == (l == list->tail)); + if (l->next) GPR_ASSERT(l->next->prev == l); + if (l->prev) GPR_ASSERT(l->prev->next == l); + verified_count++; + } + GPR_ASSERT(list->count == verified_count); +#endif /* NDEBUG */ +} + +static void assert_valid_callouts(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch) { +#ifndef NDEBUG + for (grpc_linked_mdelem *l = batch->list.head; l != NULL; l = l->next) { + grpc_slice key_interned = grpc_slice_intern(GRPC_MDKEY(l->md)); + grpc_metadata_batch_callouts_index callout_idx = + GRPC_BATCH_INDEX_OF(key_interned); + if (callout_idx != GRPC_BATCH_CALLOUTS_COUNT) { + GPR_ASSERT(batch->idx.array[callout_idx] == l); + } + grpc_slice_unref_internal(exec_ctx, key_interned); + } +#endif +} + +#ifndef NDEBUG +void grpc_metadata_batch_assert_ok(grpc_metadata_batch *batch) { + assert_valid_list(&batch->list); +} +#endif /* NDEBUG */ + +void grpc_metadata_batch_init(grpc_metadata_batch *batch) { + memset(batch, 0, sizeof(*batch)); + batch->deadline = gpr_inf_future(GPR_CLOCK_REALTIME); +} + +void grpc_metadata_batch_destroy(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch) { + grpc_linked_mdelem *l; + for (l = batch->list.head; l; l = l->next) { + GRPC_MDELEM_UNREF(exec_ctx, l->md); + } +} + +grpc_error *grpc_attach_md_to_error(grpc_error *src, grpc_mdelem md) { + grpc_error *out = grpc_error_set_str( + grpc_error_set_str(src, GRPC_ERROR_STR_KEY, + grpc_slice_ref_internal(GRPC_MDKEY(md))), + GRPC_ERROR_STR_VALUE, grpc_slice_ref_internal(GRPC_MDVALUE(md))); + return out; +} + +static grpc_error *maybe_link_callout(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) + GRPC_MUST_USE_RESULT; + +static grpc_error *maybe_link_callout(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + grpc_metadata_batch_callouts_index idx = + GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); + if (idx == GRPC_BATCH_CALLOUTS_COUNT) { + return GRPC_ERROR_NONE; + } + if (batch->idx.array[idx] == NULL) { + if (grpc_static_callout_is_default[idx]) ++batch->list.default_count; + batch->idx.array[idx] = storage; + return GRPC_ERROR_NONE; + } + return grpc_attach_md_to_error( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unallowed duplicate metadata"), + storage->md); +} + +static void maybe_unlink_callout(grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + grpc_metadata_batch_callouts_index idx = + GRPC_BATCH_INDEX_OF(GRPC_MDKEY(storage->md)); + if (idx == GRPC_BATCH_CALLOUTS_COUNT) { + return; + } + if (grpc_static_callout_is_default[idx]) --batch->list.default_count; + GPR_ASSERT(batch->idx.array[idx] != NULL); + batch->idx.array[idx] = NULL; +} + +grpc_error *grpc_metadata_batch_add_head(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem elem_to_add) { + GPR_ASSERT(!GRPC_MDISNULL(elem_to_add)); + storage->md = elem_to_add; + return grpc_metadata_batch_link_head(exec_ctx, batch, storage); +} + +static void link_head(grpc_mdelem_list *list, grpc_linked_mdelem *storage) { + assert_valid_list(list); + GPR_ASSERT(!GRPC_MDISNULL(storage->md)); + storage->prev = NULL; + storage->next = list->head; + if (list->head != NULL) { + list->head->prev = storage; + } else { + list->tail = storage; + } + list->head = storage; + list->count++; + assert_valid_list(list); +} + +grpc_error *grpc_metadata_batch_link_head(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + assert_valid_callouts(exec_ctx, batch); + grpc_error *err = maybe_link_callout(batch, storage); + if (err != GRPC_ERROR_NONE) { + assert_valid_callouts(exec_ctx, batch); + return err; + } + link_head(&batch->list, storage); + assert_valid_callouts(exec_ctx, batch); + return GRPC_ERROR_NONE; +} + +grpc_error *grpc_metadata_batch_add_tail(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem elem_to_add) { + GPR_ASSERT(!GRPC_MDISNULL(elem_to_add)); + storage->md = elem_to_add; + return grpc_metadata_batch_link_tail(exec_ctx, batch, storage); +} + +static void link_tail(grpc_mdelem_list *list, grpc_linked_mdelem *storage) { + assert_valid_list(list); + GPR_ASSERT(!GRPC_MDISNULL(storage->md)); + storage->prev = list->tail; + storage->next = NULL; + storage->reserved = NULL; + if (list->tail != NULL) { + list->tail->next = storage; + } else { + list->head = storage; + } + list->tail = storage; + list->count++; + assert_valid_list(list); +} + +grpc_error *grpc_metadata_batch_link_tail(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + assert_valid_callouts(exec_ctx, batch); + grpc_error *err = maybe_link_callout(batch, storage); + if (err != GRPC_ERROR_NONE) { + assert_valid_callouts(exec_ctx, batch); + return err; + } + link_tail(&batch->list, storage); + assert_valid_callouts(exec_ctx, batch); + return GRPC_ERROR_NONE; +} + +static void unlink_storage(grpc_mdelem_list *list, + grpc_linked_mdelem *storage) { + assert_valid_list(list); + if (storage->prev != NULL) { + storage->prev->next = storage->next; + } else { + list->head = storage->next; + } + if (storage->next != NULL) { + storage->next->prev = storage->prev; + } else { + list->tail = storage->prev; + } + list->count--; + assert_valid_list(list); +} + +void grpc_metadata_batch_remove(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_linked_mdelem *storage) { + assert_valid_callouts(exec_ctx, batch); + maybe_unlink_callout(batch, storage); + unlink_storage(&batch->list, storage); + GRPC_MDELEM_UNREF(exec_ctx, storage->md); + assert_valid_callouts(exec_ctx, batch); +} + +void grpc_metadata_batch_set_value(grpc_exec_ctx *exec_ctx, + grpc_linked_mdelem *storage, + grpc_slice value) { + grpc_mdelem old_mdelem = storage->md; + grpc_mdelem new_mdelem = grpc_mdelem_from_slices( + exec_ctx, grpc_slice_ref_internal(GRPC_MDKEY(old_mdelem)), value); + storage->md = new_mdelem; + GRPC_MDELEM_UNREF(exec_ctx, old_mdelem); +} + +grpc_error *grpc_metadata_batch_substitute(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_linked_mdelem *storage, + grpc_mdelem new_mdelem) { + assert_valid_callouts(exec_ctx, batch); + grpc_error *error = GRPC_ERROR_NONE; + grpc_mdelem old_mdelem = storage->md; + if (!grpc_slice_eq(GRPC_MDKEY(new_mdelem), GRPC_MDKEY(old_mdelem))) { + maybe_unlink_callout(batch, storage); + storage->md = new_mdelem; + error = maybe_link_callout(batch, storage); + if (error != GRPC_ERROR_NONE) { + unlink_storage(&batch->list, storage); + GRPC_MDELEM_UNREF(exec_ctx, storage->md); + } + } else { + storage->md = new_mdelem; + } + GRPC_MDELEM_UNREF(exec_ctx, old_mdelem); + assert_valid_callouts(exec_ctx, batch); + return error; +} + +void grpc_metadata_batch_clear(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch) { + grpc_metadata_batch_destroy(exec_ctx, batch); + grpc_metadata_batch_init(batch); +} + +bool grpc_metadata_batch_is_empty(grpc_metadata_batch *batch) { + return batch->list.head == NULL && + gpr_time_cmp(gpr_inf_future(batch->deadline.clock_type), + batch->deadline) == 0; +} + +size_t grpc_metadata_batch_size(grpc_metadata_batch *batch) { + size_t size = 0; + for (grpc_linked_mdelem *elem = batch->list.head; elem != NULL; + elem = elem->next) { + size += GRPC_MDELEM_LENGTH(elem->md); + } + return size; +} + +static void add_error(grpc_error **composite, grpc_error *error, + const char *composite_error_string) { + if (error == GRPC_ERROR_NONE) return; + if (*composite == GRPC_ERROR_NONE) { + *composite = GRPC_ERROR_CREATE_FROM_COPIED_STRING(composite_error_string); + } + *composite = grpc_error_add_child(*composite, error); +} + +grpc_error *grpc_metadata_batch_filter(grpc_exec_ctx *exec_ctx, + grpc_metadata_batch *batch, + grpc_metadata_batch_filter_func func, + void *user_data, + const char *composite_error_string) { + grpc_linked_mdelem *l = batch->list.head; + grpc_error *error = GRPC_ERROR_NONE; + while (l) { + grpc_linked_mdelem *next = l->next; + grpc_filtered_mdelem new_mdelem = func(exec_ctx, user_data, l->md); + add_error(&error, new_mdelem.error, composite_error_string); + if (GRPC_MDISNULL(new_mdelem.md)) { + grpc_metadata_batch_remove(exec_ctx, batch, l); + } else if (new_mdelem.md.payload != l->md.payload) { + grpc_metadata_batch_substitute(exec_ctx, batch, l, new_mdelem.md); + } + l = next; + } + return error; +} diff --git a/src/core/lib/transport/pid_controller.c b/src/core/lib/transport/pid_controller.c deleted file mode 100644 index 4b304f17b2..0000000000 --- a/src/core/lib/transport/pid_controller.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/pid_controller.h" -#include - -void grpc_pid_controller_init(grpc_pid_controller *pid_controller, - grpc_pid_controller_args args) { - pid_controller->args = args; - pid_controller->last_control_value = args.initial_control_value; - grpc_pid_controller_reset(pid_controller); -} - -void grpc_pid_controller_reset(grpc_pid_controller *pid_controller) { - pid_controller->last_error = 0.0; - pid_controller->last_dc_dt = 0.0; - pid_controller->error_integral = 0.0; -} - -double grpc_pid_controller_update(grpc_pid_controller *pid_controller, - double error, double dt) { - if (dt == 0) return pid_controller->last_control_value; - /* integrate error using the trapezoid rule */ - pid_controller->error_integral += - dt * (pid_controller->last_error + error) * 0.5; - pid_controller->error_integral = GPR_CLAMP( - pid_controller->error_integral, -pid_controller->args.integral_range, - pid_controller->args.integral_range); - double diff_error = (error - pid_controller->last_error) / dt; - /* calculate derivative of control value vs time */ - double dc_dt = pid_controller->args.gain_p * error + - pid_controller->args.gain_i * pid_controller->error_integral + - pid_controller->args.gain_d * diff_error; - /* and perform trapezoidal integration */ - double new_control_value = pid_controller->last_control_value + - dt * (pid_controller->last_dc_dt + dc_dt) * 0.5; - new_control_value = - GPR_CLAMP(new_control_value, pid_controller->args.min_control_value, - pid_controller->args.max_control_value); - pid_controller->last_error = error; - pid_controller->last_dc_dt = dc_dt; - pid_controller->last_control_value = new_control_value; - return new_control_value; -} - -double grpc_pid_controller_last(grpc_pid_controller *pid_controller) { - return pid_controller->last_control_value; -} diff --git a/src/core/lib/transport/pid_controller.cc b/src/core/lib/transport/pid_controller.cc new file mode 100644 index 0000000000..4b304f17b2 --- /dev/null +++ b/src/core/lib/transport/pid_controller.cc @@ -0,0 +1,63 @@ +/* + * + * Copyright 2016 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/pid_controller.h" +#include + +void grpc_pid_controller_init(grpc_pid_controller *pid_controller, + grpc_pid_controller_args args) { + pid_controller->args = args; + pid_controller->last_control_value = args.initial_control_value; + grpc_pid_controller_reset(pid_controller); +} + +void grpc_pid_controller_reset(grpc_pid_controller *pid_controller) { + pid_controller->last_error = 0.0; + pid_controller->last_dc_dt = 0.0; + pid_controller->error_integral = 0.0; +} + +double grpc_pid_controller_update(grpc_pid_controller *pid_controller, + double error, double dt) { + if (dt == 0) return pid_controller->last_control_value; + /* integrate error using the trapezoid rule */ + pid_controller->error_integral += + dt * (pid_controller->last_error + error) * 0.5; + pid_controller->error_integral = GPR_CLAMP( + pid_controller->error_integral, -pid_controller->args.integral_range, + pid_controller->args.integral_range); + double diff_error = (error - pid_controller->last_error) / dt; + /* calculate derivative of control value vs time */ + double dc_dt = pid_controller->args.gain_p * error + + pid_controller->args.gain_i * pid_controller->error_integral + + pid_controller->args.gain_d * diff_error; + /* and perform trapezoidal integration */ + double new_control_value = pid_controller->last_control_value + + dt * (pid_controller->last_dc_dt + dc_dt) * 0.5; + new_control_value = + GPR_CLAMP(new_control_value, pid_controller->args.min_control_value, + pid_controller->args.max_control_value); + pid_controller->last_error = error; + pid_controller->last_dc_dt = dc_dt; + pid_controller->last_control_value = new_control_value; + return new_control_value; +} + +double grpc_pid_controller_last(grpc_pid_controller *pid_controller) { + return pid_controller->last_control_value; +} diff --git a/src/core/lib/transport/service_config.c b/src/core/lib/transport/service_config.c deleted file mode 100644 index 070a13a2b4..0000000000 --- a/src/core/lib/transport/service_config.c +++ /dev/null @@ -1,246 +0,0 @@ -// -// Copyright 2015 gRPC authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#include "src/core/lib/transport/service_config.h" - -#include - -#include -#include -#include -#include - -#include "src/core/lib/json/json.h" -#include "src/core/lib/slice/slice_hash_table.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" - -// The main purpose of the code here is to parse the service config in -// JSON form, which will look like this: -// -// { -// "loadBalancingPolicy": "string", // optional -// "methodConfig": [ // array of one or more method_config objects -// { -// "name": [ // array of one or more name objects -// { -// "service": "string", // required -// "method": "string", // optional -// } -// ], -// // remaining fields are optional. -// // see https://developers.google.com/protocol-buffers/docs/proto3#json -// // for format details. -// "waitForReady": bool, -// "timeout": "duration_string", -// "maxRequestMessageBytes": "int64_string", -// "maxResponseMessageBytes": "int64_string", -// } -// ] -// } - -struct grpc_service_config { - char* json_string; // Underlying storage for json_tree. - grpc_json* json_tree; -}; - -grpc_service_config* grpc_service_config_create(const char* json_string) { - grpc_service_config* service_config = - (grpc_service_config*)gpr_malloc(sizeof(*service_config)); - service_config->json_string = gpr_strdup(json_string); - service_config->json_tree = - grpc_json_parse_string(service_config->json_string); - if (service_config->json_tree == NULL) { - gpr_log(GPR_INFO, "failed to parse JSON for service config"); - gpr_free(service_config->json_string); - gpr_free(service_config); - return NULL; - } - return service_config; -} - -void grpc_service_config_destroy(grpc_service_config* service_config) { - grpc_json_destroy(service_config->json_tree); - gpr_free(service_config->json_string); - gpr_free(service_config); -} - -void grpc_service_config_parse_global_params( - const grpc_service_config* service_config, - void (*process_json)(const grpc_json* json, void* arg), void* arg) { - const grpc_json* json = service_config->json_tree; - if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return; - for (grpc_json* field = json->child; field != NULL; field = field->next) { - if (field->key == NULL) return; - if (strcmp(field->key, "methodConfig") == 0) continue; - process_json(field, arg); - } -} - -const char* grpc_service_config_get_lb_policy_name( - const grpc_service_config* service_config) { - const grpc_json* json = service_config->json_tree; - if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL; - const char* lb_policy_name = NULL; - for (grpc_json* field = json->child; field != NULL; field = field->next) { - if (field->key == NULL) return NULL; - if (strcmp(field->key, "loadBalancingPolicy") == 0) { - if (lb_policy_name != NULL) return NULL; // Duplicate. - if (field->type != GRPC_JSON_STRING) return NULL; - lb_policy_name = field->value; - } - } - return lb_policy_name; -} - -// Returns the number of names specified in the method config \a json. -static size_t count_names_in_method_config_json(grpc_json* json) { - size_t num_names = 0; - for (grpc_json* field = json->child; field != NULL; field = field->next) { - if (field->key != NULL && strcmp(field->key, "name") == 0) ++num_names; - } - return num_names; -} - -// Returns a path string for the JSON name object specified by \a json. -// Returns NULL on error. Caller takes ownership of result. -static char* parse_json_method_name(grpc_json* json) { - if (json->type != GRPC_JSON_OBJECT) return NULL; - const char* service_name = NULL; - const char* method_name = NULL; - for (grpc_json* child = json->child; child != NULL; child = child->next) { - if (child->key == NULL) return NULL; - if (child->type != GRPC_JSON_STRING) return NULL; - if (strcmp(child->key, "service") == 0) { - if (service_name != NULL) return NULL; // Duplicate. - if (child->value == NULL) return NULL; - service_name = child->value; - } else if (strcmp(child->key, "method") == 0) { - if (method_name != NULL) return NULL; // Duplicate. - if (child->value == NULL) return NULL; - method_name = child->value; - } - } - if (service_name == NULL) return NULL; // Required field. - char* path; - gpr_asprintf(&path, "/%s/%s", service_name, - method_name == NULL ? "*" : method_name); - return path; -} - -// Parses the method config from \a json. Adds an entry to \a entries for -// each name found, incrementing \a idx for each entry added. -// Returns false on error. -static bool parse_json_method_config( - grpc_exec_ctx* exec_ctx, grpc_json* json, - void* (*create_value)(const grpc_json* method_config_json), - grpc_slice_hash_table_entry* entries, size_t* idx) { - // Construct value. - void* method_config = create_value(json); - if (method_config == NULL) return false; - // Construct list of paths. - bool success = false; - gpr_strvec paths; - gpr_strvec_init(&paths); - for (grpc_json* child = json->child; child != NULL; child = child->next) { - if (child->key == NULL) continue; - if (strcmp(child->key, "name") == 0) { - if (child->type != GRPC_JSON_ARRAY) goto done; - for (grpc_json* name = child->child; name != NULL; name = name->next) { - char* path = parse_json_method_name(name); - gpr_strvec_add(&paths, path); - } - } - } - if (paths.count == 0) goto done; // No names specified. - // Add entry for each path. - for (size_t i = 0; i < paths.count; ++i) { - entries[*idx].key = grpc_slice_from_copied_string(paths.strs[i]); - entries[*idx].value = method_config; - ++*idx; - } - success = true; -done: - gpr_strvec_destroy(&paths); - return success; -} - -grpc_slice_hash_table* grpc_service_config_create_method_config_table( - grpc_exec_ctx* exec_ctx, const grpc_service_config* service_config, - void* (*create_value)(const grpc_json* method_config_json), - void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value)) { - const grpc_json* json = service_config->json_tree; - // Traverse parsed JSON tree. - if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL; - size_t num_entries = 0; - grpc_slice_hash_table_entry* entries = NULL; - for (grpc_json* field = json->child; field != NULL; field = field->next) { - if (field->key == NULL) return NULL; - if (strcmp(field->key, "methodConfig") == 0) { - if (entries != NULL) return NULL; // Duplicate. - if (field->type != GRPC_JSON_ARRAY) return NULL; - // Find number of entries. - for (grpc_json* method = field->child; method != NULL; - method = method->next) { - num_entries += count_names_in_method_config_json(method); - } - // Populate method config table entries. - entries = (grpc_slice_hash_table_entry*)gpr_malloc( - num_entries * sizeof(grpc_slice_hash_table_entry)); - size_t idx = 0; - for (grpc_json* method = field->child; method != NULL; - method = method->next) { - if (!parse_json_method_config(exec_ctx, method, create_value, entries, - &idx)) { - return NULL; - } - } - GPR_ASSERT(idx == num_entries); - } - } - // Instantiate method config table. - grpc_slice_hash_table* method_config_table = NULL; - if (entries != NULL) { - method_config_table = - grpc_slice_hash_table_create(num_entries, entries, destroy_value, NULL); - gpr_free(entries); - } - return method_config_table; -} - -void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx, - const grpc_slice_hash_table* table, - grpc_slice path) { - void* value = grpc_slice_hash_table_get(table, path); - // If we didn't find a match for the path, try looking for a wildcard - // entry (i.e., change "/service/method" to "/service/*"). - if (value == NULL) { - char* path_str = grpc_slice_to_c_string(path); - const char* sep = strrchr(path_str, '/') + 1; - const size_t len = (size_t)(sep - path_str); - char* buf = (char*)gpr_malloc(len + 2); // '*' and NUL - memcpy(buf, path_str, len); - buf[len] = '*'; - buf[len + 1] = '\0'; - grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); - gpr_free(buf); - value = grpc_slice_hash_table_get(table, wildcard_path); - grpc_slice_unref_internal(exec_ctx, wildcard_path); - gpr_free(path_str); - } - return value; -} diff --git a/src/core/lib/transport/service_config.cc b/src/core/lib/transport/service_config.cc new file mode 100644 index 0000000000..070a13a2b4 --- /dev/null +++ b/src/core/lib/transport/service_config.cc @@ -0,0 +1,246 @@ +// +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "src/core/lib/transport/service_config.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/json/json.h" +#include "src/core/lib/slice/slice_hash_table.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" + +// The main purpose of the code here is to parse the service config in +// JSON form, which will look like this: +// +// { +// "loadBalancingPolicy": "string", // optional +// "methodConfig": [ // array of one or more method_config objects +// { +// "name": [ // array of one or more name objects +// { +// "service": "string", // required +// "method": "string", // optional +// } +// ], +// // remaining fields are optional. +// // see https://developers.google.com/protocol-buffers/docs/proto3#json +// // for format details. +// "waitForReady": bool, +// "timeout": "duration_string", +// "maxRequestMessageBytes": "int64_string", +// "maxResponseMessageBytes": "int64_string", +// } +// ] +// } + +struct grpc_service_config { + char* json_string; // Underlying storage for json_tree. + grpc_json* json_tree; +}; + +grpc_service_config* grpc_service_config_create(const char* json_string) { + grpc_service_config* service_config = + (grpc_service_config*)gpr_malloc(sizeof(*service_config)); + service_config->json_string = gpr_strdup(json_string); + service_config->json_tree = + grpc_json_parse_string(service_config->json_string); + if (service_config->json_tree == NULL) { + gpr_log(GPR_INFO, "failed to parse JSON for service config"); + gpr_free(service_config->json_string); + gpr_free(service_config); + return NULL; + } + return service_config; +} + +void grpc_service_config_destroy(grpc_service_config* service_config) { + grpc_json_destroy(service_config->json_tree); + gpr_free(service_config->json_string); + gpr_free(service_config); +} + +void grpc_service_config_parse_global_params( + const grpc_service_config* service_config, + void (*process_json)(const grpc_json* json, void* arg), void* arg) { + const grpc_json* json = service_config->json_tree; + if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) return; + if (strcmp(field->key, "methodConfig") == 0) continue; + process_json(field, arg); + } +} + +const char* grpc_service_config_get_lb_policy_name( + const grpc_service_config* service_config) { + const grpc_json* json = service_config->json_tree; + if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL; + const char* lb_policy_name = NULL; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) return NULL; + if (strcmp(field->key, "loadBalancingPolicy") == 0) { + if (lb_policy_name != NULL) return NULL; // Duplicate. + if (field->type != GRPC_JSON_STRING) return NULL; + lb_policy_name = field->value; + } + } + return lb_policy_name; +} + +// Returns the number of names specified in the method config \a json. +static size_t count_names_in_method_config_json(grpc_json* json) { + size_t num_names = 0; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key != NULL && strcmp(field->key, "name") == 0) ++num_names; + } + return num_names; +} + +// Returns a path string for the JSON name object specified by \a json. +// Returns NULL on error. Caller takes ownership of result. +static char* parse_json_method_name(grpc_json* json) { + if (json->type != GRPC_JSON_OBJECT) return NULL; + const char* service_name = NULL; + const char* method_name = NULL; + for (grpc_json* child = json->child; child != NULL; child = child->next) { + if (child->key == NULL) return NULL; + if (child->type != GRPC_JSON_STRING) return NULL; + if (strcmp(child->key, "service") == 0) { + if (service_name != NULL) return NULL; // Duplicate. + if (child->value == NULL) return NULL; + service_name = child->value; + } else if (strcmp(child->key, "method") == 0) { + if (method_name != NULL) return NULL; // Duplicate. + if (child->value == NULL) return NULL; + method_name = child->value; + } + } + if (service_name == NULL) return NULL; // Required field. + char* path; + gpr_asprintf(&path, "/%s/%s", service_name, + method_name == NULL ? "*" : method_name); + return path; +} + +// Parses the method config from \a json. Adds an entry to \a entries for +// each name found, incrementing \a idx for each entry added. +// Returns false on error. +static bool parse_json_method_config( + grpc_exec_ctx* exec_ctx, grpc_json* json, + void* (*create_value)(const grpc_json* method_config_json), + grpc_slice_hash_table_entry* entries, size_t* idx) { + // Construct value. + void* method_config = create_value(json); + if (method_config == NULL) return false; + // Construct list of paths. + bool success = false; + gpr_strvec paths; + gpr_strvec_init(&paths); + for (grpc_json* child = json->child; child != NULL; child = child->next) { + if (child->key == NULL) continue; + if (strcmp(child->key, "name") == 0) { + if (child->type != GRPC_JSON_ARRAY) goto done; + for (grpc_json* name = child->child; name != NULL; name = name->next) { + char* path = parse_json_method_name(name); + gpr_strvec_add(&paths, path); + } + } + } + if (paths.count == 0) goto done; // No names specified. + // Add entry for each path. + for (size_t i = 0; i < paths.count; ++i) { + entries[*idx].key = grpc_slice_from_copied_string(paths.strs[i]); + entries[*idx].value = method_config; + ++*idx; + } + success = true; +done: + gpr_strvec_destroy(&paths); + return success; +} + +grpc_slice_hash_table* grpc_service_config_create_method_config_table( + grpc_exec_ctx* exec_ctx, const grpc_service_config* service_config, + void* (*create_value)(const grpc_json* method_config_json), + void (*destroy_value)(grpc_exec_ctx* exec_ctx, void* value)) { + const grpc_json* json = service_config->json_tree; + // Traverse parsed JSON tree. + if (json->type != GRPC_JSON_OBJECT || json->key != NULL) return NULL; + size_t num_entries = 0; + grpc_slice_hash_table_entry* entries = NULL; + for (grpc_json* field = json->child; field != NULL; field = field->next) { + if (field->key == NULL) return NULL; + if (strcmp(field->key, "methodConfig") == 0) { + if (entries != NULL) return NULL; // Duplicate. + if (field->type != GRPC_JSON_ARRAY) return NULL; + // Find number of entries. + for (grpc_json* method = field->child; method != NULL; + method = method->next) { + num_entries += count_names_in_method_config_json(method); + } + // Populate method config table entries. + entries = (grpc_slice_hash_table_entry*)gpr_malloc( + num_entries * sizeof(grpc_slice_hash_table_entry)); + size_t idx = 0; + for (grpc_json* method = field->child; method != NULL; + method = method->next) { + if (!parse_json_method_config(exec_ctx, method, create_value, entries, + &idx)) { + return NULL; + } + } + GPR_ASSERT(idx == num_entries); + } + } + // Instantiate method config table. + grpc_slice_hash_table* method_config_table = NULL; + if (entries != NULL) { + method_config_table = + grpc_slice_hash_table_create(num_entries, entries, destroy_value, NULL); + gpr_free(entries); + } + return method_config_table; +} + +void* grpc_method_config_table_get(grpc_exec_ctx* exec_ctx, + const grpc_slice_hash_table* table, + grpc_slice path) { + void* value = grpc_slice_hash_table_get(table, path); + // If we didn't find a match for the path, try looking for a wildcard + // entry (i.e., change "/service/method" to "/service/*"). + if (value == NULL) { + char* path_str = grpc_slice_to_c_string(path); + const char* sep = strrchr(path_str, '/') + 1; + const size_t len = (size_t)(sep - path_str); + char* buf = (char*)gpr_malloc(len + 2); // '*' and NUL + memcpy(buf, path_str, len); + buf[len] = '*'; + buf[len + 1] = '\0'; + grpc_slice wildcard_path = grpc_slice_from_copied_string(buf); + gpr_free(buf); + value = grpc_slice_hash_table_get(table, wildcard_path); + grpc_slice_unref_internal(exec_ctx, wildcard_path); + gpr_free(path_str); + } + return value; +} diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c deleted file mode 100644 index 472cf888ea..0000000000 --- a/src/core/lib/transport/static_metadata.c +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * WARNING: Auto-generated code. - * - * To make changes to this file, change - * tools/codegen/core/gen_static_metadata.py, and then re-run it. - * - * See metadata.h for an explanation of the interface here, and metadata.c for - * an explanation of what's going on. - */ - -#include "src/core/lib/transport/static_metadata.h" - -#include "src/core/lib/slice/slice_internal.h" - -static uint8_t g_bytes[] = { - 58, 112, 97, 116, 104, 58, 109, 101, 116, 104, 111, 100, 58, 115, 116, - 97, 116, 117, 115, 58, 97, 117, 116, 104, 111, 114, 105, 116, 121, 58, - 115, 99, 104, 101, 109, 101, 116, 101, 103, 114, 112, 99, 45, 109, 101, - 115, 115, 97, 103, 101, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, - 115, 103, 114, 112, 99, 45, 112, 97, 121, 108, 111, 97, 100, 45, 98, - 105, 110, 103, 114, 112, 99, 45, 101, 110, 99, 111, 100, 105, 110, 103, - 103, 114, 112, 99, 45, 97, 99, 99, 101, 112, 116, 45, 101, 110, 99, - 111, 100, 105, 110, 103, 103, 114, 112, 99, 45, 115, 101, 114, 118, 101, - 114, 45, 115, 116, 97, 116, 115, 45, 98, 105, 110, 103, 114, 112, 99, - 45, 116, 97, 103, 115, 45, 98, 105, 110, 103, 114, 112, 99, 45, 116, - 114, 97, 99, 101, 45, 98, 105, 110, 99, 111, 110, 116, 101, 110, 116, - 45, 116, 121, 112, 101, 99, 111, 110, 116, 101, 110, 116, 45, 101, 110, - 99, 111, 100, 105, 110, 103, 97, 99, 99, 101, 112, 116, 45, 101, 110, - 99, 111, 100, 105, 110, 103, 103, 114, 112, 99, 45, 105, 110, 116, 101, - 114, 110, 97, 108, 45, 101, 110, 99, 111, 100, 105, 110, 103, 45, 114, - 101, 113, 117, 101, 115, 116, 103, 114, 112, 99, 45, 105, 110, 116, 101, - 114, 110, 97, 108, 45, 115, 116, 114, 101, 97, 109, 45, 101, 110, 99, - 111, 100, 105, 110, 103, 45, 114, 101, 113, 117, 101, 115, 116, 117, 115, - 101, 114, 45, 97, 103, 101, 110, 116, 104, 111, 115, 116, 108, 98, 45, - 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 116, 105, 109, 101, 111, - 117, 116, 103, 114, 112, 99, 46, 119, 97, 105, 116, 95, 102, 111, 114, - 95, 114, 101, 97, 100, 121, 103, 114, 112, 99, 46, 116, 105, 109, 101, - 111, 117, 116, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 113, - 117, 101, 115, 116, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, 121, - 116, 101, 115, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 115, - 112, 111, 110, 115, 101, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, - 121, 116, 101, 115, 47, 103, 114, 112, 99, 46, 108, 98, 46, 118, 49, - 46, 76, 111, 97, 100, 66, 97, 108, 97, 110, 99, 101, 114, 47, 66, - 97, 108, 97, 110, 99, 101, 76, 111, 97, 100, 48, 49, 50, 105, 100, - 101, 110, 116, 105, 116, 121, 103, 122, 105, 112, 100, 101, 102, 108, 97, - 116, 101, 116, 114, 97, 105, 108, 101, 114, 115, 97, 112, 112, 108, 105, - 99, 97, 116, 105, 111, 110, 47, 103, 114, 112, 99, 80, 79, 83, 84, - 50, 48, 48, 52, 48, 52, 104, 116, 116, 112, 104, 116, 116, 112, 115, - 103, 114, 112, 99, 71, 69, 84, 80, 85, 84, 47, 47, 105, 110, 100, - 101, 120, 46, 104, 116, 109, 108, 50, 48, 52, 50, 48, 54, 51, 48, - 52, 52, 48, 48, 53, 48, 48, 97, 99, 99, 101, 112, 116, 45, 99, - 104, 97, 114, 115, 101, 116, 103, 122, 105, 112, 44, 32, 100, 101, 102, - 108, 97, 116, 101, 97, 99, 99, 101, 112, 116, 45, 108, 97, 110, 103, - 117, 97, 103, 101, 97, 99, 99, 101, 112, 116, 45, 114, 97, 110, 103, - 101, 115, 97, 99, 99, 101, 112, 116, 97, 99, 99, 101, 115, 115, 45, - 99, 111, 110, 116, 114, 111, 108, 45, 97, 108, 108, 111, 119, 45, 111, - 114, 105, 103, 105, 110, 97, 103, 101, 97, 108, 108, 111, 119, 97, 117, - 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 99, 97, 99, 104, - 101, 45, 99, 111, 110, 116, 114, 111, 108, 99, 111, 110, 116, 101, 110, - 116, 45, 100, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 99, 111, - 110, 116, 101, 110, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, 99, - 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 99, 111, - 110, 116, 101, 110, 116, 45, 108, 111, 99, 97, 116, 105, 111, 110, 99, - 111, 110, 116, 101, 110, 116, 45, 114, 97, 110, 103, 101, 99, 111, 111, - 107, 105, 101, 100, 97, 116, 101, 101, 116, 97, 103, 101, 120, 112, 101, - 99, 116, 101, 120, 112, 105, 114, 101, 115, 102, 114, 111, 109, 105, 102, - 45, 109, 97, 116, 99, 104, 105, 102, 45, 109, 111, 100, 105, 102, 105, - 101, 100, 45, 115, 105, 110, 99, 101, 105, 102, 45, 110, 111, 110, 101, - 45, 109, 97, 116, 99, 104, 105, 102, 45, 114, 97, 110, 103, 101, 105, - 102, 45, 117, 110, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, 105, - 110, 99, 101, 108, 97, 115, 116, 45, 109, 111, 100, 105, 102, 105, 101, - 100, 108, 98, 45, 99, 111, 115, 116, 45, 98, 105, 110, 108, 105, 110, - 107, 108, 111, 99, 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, 111, - 114, 119, 97, 114, 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, 116, - 104, 101, 110, 116, 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, 45, - 97, 117, 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, 97, - 110, 103, 101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, - 115, 104, 114, 101, 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, 101, - 114, 118, 101, 114, 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, 115, - 116, 114, 105, 99, 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, 116, - 45, 115, 101, 99, 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, 102, - 101, 114, 45, 101, 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, 121, - 118, 105, 97, 119, 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, 105, - 99, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, - 102, 108, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 103, - 122, 105, 112, 100, 101, 102, 108, 97, 116, 101, 44, 103, 122, 105, 112, - 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, 116, - 101, 44, 103, 122, 105, 112}; - -static void static_ref(void *unused) {} -static void static_unref(grpc_exec_ctx *exec_ctx, void *unused) {} -static const grpc_slice_refcount_vtable static_sub_vtable = { - static_ref, static_unref, grpc_slice_default_eq_impl, - grpc_slice_default_hash_impl}; -const grpc_slice_refcount_vtable grpc_static_metadata_vtable = { - static_ref, static_unref, grpc_static_slice_eq, grpc_static_slice_hash}; -static grpc_slice_refcount static_sub_refcnt = {&static_sub_vtable, - &static_sub_refcnt}; -grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = { - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, - {&grpc_static_metadata_vtable, &static_sub_refcnt}, -}; - -const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { - {&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, - {&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, - {&grpc_static_metadata_refcounts[6], {{g_bytes + 38, 12}}}, - {&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[8], {{g_bytes + 61, 16}}}, - {&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[11], {{g_bytes + 110, 21}}}, - {&grpc_static_metadata_refcounts[12], {{g_bytes + 131, 13}}}, - {&grpc_static_metadata_refcounts[13], {{g_bytes + 144, 14}}}, - {&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[17], {{g_bytes + 201, 30}}}, - {&grpc_static_metadata_refcounts[18], {{g_bytes + 231, 37}}}, - {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, - {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, - {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, - {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}, - {&grpc_static_metadata_refcounts[24], {{g_bytes + 302, 19}}}, - {&grpc_static_metadata_refcounts[25], {{g_bytes + 321, 12}}}, - {&grpc_static_metadata_refcounts[26], {{g_bytes + 333, 30}}}, - {&grpc_static_metadata_refcounts[27], {{g_bytes + 363, 31}}}, - {&grpc_static_metadata_refcounts[28], {{g_bytes + 394, 36}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 1}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 431, 1}}}, - {&grpc_static_metadata_refcounts[31], {{g_bytes + 432, 1}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 445, 7}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 452, 8}}}, - {&grpc_static_metadata_refcounts[36], {{g_bytes + 460, 16}}}, - {&grpc_static_metadata_refcounts[37], {{g_bytes + 476, 4}}}, - {&grpc_static_metadata_refcounts[38], {{g_bytes + 480, 3}}}, - {&grpc_static_metadata_refcounts[39], {{g_bytes + 483, 3}}}, - {&grpc_static_metadata_refcounts[40], {{g_bytes + 486, 4}}}, - {&grpc_static_metadata_refcounts[41], {{g_bytes + 490, 5}}}, - {&grpc_static_metadata_refcounts[42], {{g_bytes + 495, 4}}}, - {&grpc_static_metadata_refcounts[43], {{g_bytes + 499, 3}}}, - {&grpc_static_metadata_refcounts[44], {{g_bytes + 502, 3}}}, - {&grpc_static_metadata_refcounts[45], {{g_bytes + 505, 1}}}, - {&grpc_static_metadata_refcounts[46], {{g_bytes + 506, 11}}}, - {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 3}}}, - {&grpc_static_metadata_refcounts[48], {{g_bytes + 520, 3}}}, - {&grpc_static_metadata_refcounts[49], {{g_bytes + 523, 3}}}, - {&grpc_static_metadata_refcounts[50], {{g_bytes + 526, 3}}}, - {&grpc_static_metadata_refcounts[51], {{g_bytes + 529, 3}}}, - {&grpc_static_metadata_refcounts[52], {{g_bytes + 532, 14}}}, - {&grpc_static_metadata_refcounts[53], {{g_bytes + 546, 13}}}, - {&grpc_static_metadata_refcounts[54], {{g_bytes + 559, 15}}}, - {&grpc_static_metadata_refcounts[55], {{g_bytes + 574, 13}}}, - {&grpc_static_metadata_refcounts[56], {{g_bytes + 587, 6}}}, - {&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 27}}}, - {&grpc_static_metadata_refcounts[58], {{g_bytes + 620, 3}}}, - {&grpc_static_metadata_refcounts[59], {{g_bytes + 623, 5}}}, - {&grpc_static_metadata_refcounts[60], {{g_bytes + 628, 13}}}, - {&grpc_static_metadata_refcounts[61], {{g_bytes + 641, 13}}}, - {&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 19}}}, - {&grpc_static_metadata_refcounts[63], {{g_bytes + 673, 16}}}, - {&grpc_static_metadata_refcounts[64], {{g_bytes + 689, 14}}}, - {&grpc_static_metadata_refcounts[65], {{g_bytes + 703, 16}}}, - {&grpc_static_metadata_refcounts[66], {{g_bytes + 719, 13}}}, - {&grpc_static_metadata_refcounts[67], {{g_bytes + 732, 6}}}, - {&grpc_static_metadata_refcounts[68], {{g_bytes + 738, 4}}}, - {&grpc_static_metadata_refcounts[69], {{g_bytes + 742, 4}}}, - {&grpc_static_metadata_refcounts[70], {{g_bytes + 746, 6}}}, - {&grpc_static_metadata_refcounts[71], {{g_bytes + 752, 7}}}, - {&grpc_static_metadata_refcounts[72], {{g_bytes + 759, 4}}}, - {&grpc_static_metadata_refcounts[73], {{g_bytes + 763, 8}}}, - {&grpc_static_metadata_refcounts[74], {{g_bytes + 771, 17}}}, - {&grpc_static_metadata_refcounts[75], {{g_bytes + 788, 13}}}, - {&grpc_static_metadata_refcounts[76], {{g_bytes + 801, 8}}}, - {&grpc_static_metadata_refcounts[77], {{g_bytes + 809, 19}}}, - {&grpc_static_metadata_refcounts[78], {{g_bytes + 828, 13}}}, - {&grpc_static_metadata_refcounts[79], {{g_bytes + 841, 11}}}, - {&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 4}}}, - {&grpc_static_metadata_refcounts[81], {{g_bytes + 856, 8}}}, - {&grpc_static_metadata_refcounts[82], {{g_bytes + 864, 12}}}, - {&grpc_static_metadata_refcounts[83], {{g_bytes + 876, 18}}}, - {&grpc_static_metadata_refcounts[84], {{g_bytes + 894, 19}}}, - {&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 5}}}, - {&grpc_static_metadata_refcounts[86], {{g_bytes + 918, 7}}}, - {&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 7}}}, - {&grpc_static_metadata_refcounts[88], {{g_bytes + 932, 11}}}, - {&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 6}}}, - {&grpc_static_metadata_refcounts[90], {{g_bytes + 949, 10}}}, - {&grpc_static_metadata_refcounts[91], {{g_bytes + 959, 25}}}, - {&grpc_static_metadata_refcounts[92], {{g_bytes + 984, 17}}}, - {&grpc_static_metadata_refcounts[93], {{g_bytes + 1001, 4}}}, - {&grpc_static_metadata_refcounts[94], {{g_bytes + 1005, 3}}}, - {&grpc_static_metadata_refcounts[95], {{g_bytes + 1008, 16}}}, - {&grpc_static_metadata_refcounts[96], {{g_bytes + 1024, 16}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1040, 13}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1053, 12}}}, - {&grpc_static_metadata_refcounts[99], {{g_bytes + 1065, 21}}}, -}; - -uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4}; - -static const int8_t elems_r[] = { - 11, 9, -3, 0, 10, 27, -74, 28, 0, 14, -7, 0, 0, 0, 18, 8, -2, - 0, 0, 13, 12, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, -50, 0, -33, -55, -56, -57, -58, -57, 0, 40, 39, 38, 37, 36, 35, 34, - 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 22, - 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 0}; -static uint32_t elems_phash(uint32_t i) { - i -= 45; - uint32_t x = i % 98; - uint32_t y = i / 98; - uint32_t h = x; - if (y < GPR_ARRAY_SIZE(elems_r)) { - uint32_t delta = (uint32_t)elems_r[y]; - h += delta; - } - return h; -} - -static const uint16_t elem_keys[] = { - 1032, 1033, 1034, 247, 248, 249, 250, 251, 1623, 143, 144, 45, - 46, 440, 441, 442, 1523, 1632, 1633, 932, 933, 934, 729, 730, - 1423, 1532, 1533, 535, 731, 1923, 2023, 2123, 5223, 5523, 5623, 5723, - 5823, 1436, 1653, 5923, 6023, 6123, 6223, 6323, 6423, 6523, 6623, 6723, - 6823, 6923, 7023, 7123, 7223, 5423, 7323, 7423, 7523, 7623, 7723, 7823, - 7923, 8023, 8123, 8223, 1096, 1097, 1098, 1099, 8323, 8423, 8523, 8623, - 8723, 8823, 8923, 9023, 9123, 9223, 9323, 323, 9423, 9523, 1697, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 137, 238, 239, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0}; -static const uint8_t elem_idxs[] = { - 76, 79, 77, 19, 20, 21, 22, 23, 25, 15, 16, 17, 18, 11, - 12, 13, 38, 83, 84, 3, 4, 5, 0, 1, 43, 36, 37, 6, - 2, 72, 50, 57, 24, 28, 29, 30, 31, 7, 26, 32, 33, 34, - 35, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 27, 51, 52, - 53, 54, 55, 56, 58, 59, 60, 61, 78, 80, 81, 82, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 73, 14, 74, 75, 85, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 8, 9, 10}; - -grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { - if (a == -1 || b == -1) return GRPC_MDNULL; - uint32_t k = (uint32_t)(a * 100 + b); - uint32_t h = elems_phash(k); - return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && - elem_idxs[h] != 255 - ? GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[elem_idxs[h]], - GRPC_MDELEM_STORAGE_STATIC) - : GRPC_MDNULL; -} - -grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { - {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 1}}}}, - {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[30], {{g_bytes + 431, 1}}}}, - {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, - {&grpc_static_metadata_refcounts[31], {{g_bytes + 432, 1}}}}, - {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, - {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, - {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 445, 7}}}}, - {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, - {&grpc_static_metadata_refcounts[35], {{g_bytes + 452, 8}}}}, - {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[36], {{g_bytes + 460, 16}}}}, - {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[37], {{g_bytes + 476, 4}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[38], {{g_bytes + 480, 3}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[39], {{g_bytes + 483, 3}}}}, - {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[40], {{g_bytes + 486, 4}}}}, - {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[41], {{g_bytes + 490, 5}}}}, - {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, - {&grpc_static_metadata_refcounts[42], {{g_bytes + 495, 4}}}}, - {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[43], {{g_bytes + 499, 3}}}}, - {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, - {&grpc_static_metadata_refcounts[44], {{g_bytes + 502, 3}}}}, - {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[45], {{g_bytes + 505, 1}}}}, - {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, - {&grpc_static_metadata_refcounts[46], {{g_bytes + 506, 11}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 3}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[48], {{g_bytes + 520, 3}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[49], {{g_bytes + 523, 3}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[50], {{g_bytes + 526, 3}}}}, - {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, - {&grpc_static_metadata_refcounts[51], {{g_bytes + 529, 3}}}}, - {{&grpc_static_metadata_refcounts[52], {{g_bytes + 532, 14}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[53], {{g_bytes + 546, 13}}}}, - {{&grpc_static_metadata_refcounts[54], {{g_bytes + 559, 15}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[55], {{g_bytes + 574, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[56], {{g_bytes + 587, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 27}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[58], {{g_bytes + 620, 3}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[59], {{g_bytes + 623, 5}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[60], {{g_bytes + 628, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[61], {{g_bytes + 641, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, - {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, - {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[63], {{g_bytes + 673, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[64], {{g_bytes + 689, 14}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[65], {{g_bytes + 703, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[66], {{g_bytes + 719, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[67], {{g_bytes + 732, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[68], {{g_bytes + 738, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[69], {{g_bytes + 742, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[70], {{g_bytes + 746, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[71], {{g_bytes + 752, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[72], {{g_bytes + 759, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[73], {{g_bytes + 763, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[74], {{g_bytes + 771, 17}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[75], {{g_bytes + 788, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[76], {{g_bytes + 801, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[77], {{g_bytes + 809, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[78], {{g_bytes + 828, 13}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[79], {{g_bytes + 841, 11}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[81], {{g_bytes + 856, 8}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[82], {{g_bytes + 864, 12}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[83], {{g_bytes + 876, 18}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[84], {{g_bytes + 894, 19}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 5}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[86], {{g_bytes + 918, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 7}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[88], {{g_bytes + 932, 11}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 6}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[90], {{g_bytes + 949, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[91], {{g_bytes + 959, 25}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[92], {{g_bytes + 984, 17}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[93], {{g_bytes + 1001, 4}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1005, 3}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1008, 16}}}, - {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[34], {{g_bytes + 445, 7}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[96], {{g_bytes + 1024, 16}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1040, 13}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[98], {{g_bytes + 1053, 12}}}}, - {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, - {&grpc_static_metadata_refcounts[99], {{g_bytes + 1065, 21}}}}, - {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, - {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, - {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, - {&grpc_static_metadata_refcounts[97], {{g_bytes + 1040, 13}}}}, -}; -bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { - true, // :path - true, // :method - true, // :status - true, // :authority - true, // :scheme - true, // te - true, // grpc-message - true, // grpc-status - true, // grpc-payload-bin - true, // grpc-encoding - true, // grpc-accept-encoding - true, // grpc-server-stats-bin - true, // grpc-tags-bin - true, // grpc-trace-bin - true, // content-type - true, // content-encoding - true, // accept-encoding - true, // grpc-internal-encoding-request - true, // grpc-internal-stream-encoding-request - true, // user-agent - true, // host - true, // lb-token -}; - -const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 76, 77, 78, - 79, 80, 81, 82}; - -const uint8_t grpc_static_accept_stream_encoding_metadata[4] = {0, 83, 84, 85}; diff --git a/src/core/lib/transport/static_metadata.cc b/src/core/lib/transport/static_metadata.cc new file mode 100644 index 0000000000..472cf888ea --- /dev/null +++ b/src/core/lib/transport/static_metadata.cc @@ -0,0 +1,582 @@ +/* + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * WARNING: Auto-generated code. + * + * To make changes to this file, change + * tools/codegen/core/gen_static_metadata.py, and then re-run it. + * + * See metadata.h for an explanation of the interface here, and metadata.c for + * an explanation of what's going on. + */ + +#include "src/core/lib/transport/static_metadata.h" + +#include "src/core/lib/slice/slice_internal.h" + +static uint8_t g_bytes[] = { + 58, 112, 97, 116, 104, 58, 109, 101, 116, 104, 111, 100, 58, 115, 116, + 97, 116, 117, 115, 58, 97, 117, 116, 104, 111, 114, 105, 116, 121, 58, + 115, 99, 104, 101, 109, 101, 116, 101, 103, 114, 112, 99, 45, 109, 101, + 115, 115, 97, 103, 101, 103, 114, 112, 99, 45, 115, 116, 97, 116, 117, + 115, 103, 114, 112, 99, 45, 112, 97, 121, 108, 111, 97, 100, 45, 98, + 105, 110, 103, 114, 112, 99, 45, 101, 110, 99, 111, 100, 105, 110, 103, + 103, 114, 112, 99, 45, 97, 99, 99, 101, 112, 116, 45, 101, 110, 99, + 111, 100, 105, 110, 103, 103, 114, 112, 99, 45, 115, 101, 114, 118, 101, + 114, 45, 115, 116, 97, 116, 115, 45, 98, 105, 110, 103, 114, 112, 99, + 45, 116, 97, 103, 115, 45, 98, 105, 110, 103, 114, 112, 99, 45, 116, + 114, 97, 99, 101, 45, 98, 105, 110, 99, 111, 110, 116, 101, 110, 116, + 45, 116, 121, 112, 101, 99, 111, 110, 116, 101, 110, 116, 45, 101, 110, + 99, 111, 100, 105, 110, 103, 97, 99, 99, 101, 112, 116, 45, 101, 110, + 99, 111, 100, 105, 110, 103, 103, 114, 112, 99, 45, 105, 110, 116, 101, + 114, 110, 97, 108, 45, 101, 110, 99, 111, 100, 105, 110, 103, 45, 114, + 101, 113, 117, 101, 115, 116, 103, 114, 112, 99, 45, 105, 110, 116, 101, + 114, 110, 97, 108, 45, 115, 116, 114, 101, 97, 109, 45, 101, 110, 99, + 111, 100, 105, 110, 103, 45, 114, 101, 113, 117, 101, 115, 116, 117, 115, + 101, 114, 45, 97, 103, 101, 110, 116, 104, 111, 115, 116, 108, 98, 45, + 116, 111, 107, 101, 110, 103, 114, 112, 99, 45, 116, 105, 109, 101, 111, + 117, 116, 103, 114, 112, 99, 46, 119, 97, 105, 116, 95, 102, 111, 114, + 95, 114, 101, 97, 100, 121, 103, 114, 112, 99, 46, 116, 105, 109, 101, + 111, 117, 116, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 113, + 117, 101, 115, 116, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, 121, + 116, 101, 115, 103, 114, 112, 99, 46, 109, 97, 120, 95, 114, 101, 115, + 112, 111, 110, 115, 101, 95, 109, 101, 115, 115, 97, 103, 101, 95, 98, + 121, 116, 101, 115, 47, 103, 114, 112, 99, 46, 108, 98, 46, 118, 49, + 46, 76, 111, 97, 100, 66, 97, 108, 97, 110, 99, 101, 114, 47, 66, + 97, 108, 97, 110, 99, 101, 76, 111, 97, 100, 48, 49, 50, 105, 100, + 101, 110, 116, 105, 116, 121, 103, 122, 105, 112, 100, 101, 102, 108, 97, + 116, 101, 116, 114, 97, 105, 108, 101, 114, 115, 97, 112, 112, 108, 105, + 99, 97, 116, 105, 111, 110, 47, 103, 114, 112, 99, 80, 79, 83, 84, + 50, 48, 48, 52, 48, 52, 104, 116, 116, 112, 104, 116, 116, 112, 115, + 103, 114, 112, 99, 71, 69, 84, 80, 85, 84, 47, 47, 105, 110, 100, + 101, 120, 46, 104, 116, 109, 108, 50, 48, 52, 50, 48, 54, 51, 48, + 52, 52, 48, 48, 53, 48, 48, 97, 99, 99, 101, 112, 116, 45, 99, + 104, 97, 114, 115, 101, 116, 103, 122, 105, 112, 44, 32, 100, 101, 102, + 108, 97, 116, 101, 97, 99, 99, 101, 112, 116, 45, 108, 97, 110, 103, + 117, 97, 103, 101, 97, 99, 99, 101, 112, 116, 45, 114, 97, 110, 103, + 101, 115, 97, 99, 99, 101, 112, 116, 97, 99, 99, 101, 115, 115, 45, + 99, 111, 110, 116, 114, 111, 108, 45, 97, 108, 108, 111, 119, 45, 111, + 114, 105, 103, 105, 110, 97, 103, 101, 97, 108, 108, 111, 119, 97, 117, + 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 99, 97, 99, 104, + 101, 45, 99, 111, 110, 116, 114, 111, 108, 99, 111, 110, 116, 101, 110, + 116, 45, 100, 105, 115, 112, 111, 115, 105, 116, 105, 111, 110, 99, 111, + 110, 116, 101, 110, 116, 45, 108, 97, 110, 103, 117, 97, 103, 101, 99, + 111, 110, 116, 101, 110, 116, 45, 108, 101, 110, 103, 116, 104, 99, 111, + 110, 116, 101, 110, 116, 45, 108, 111, 99, 97, 116, 105, 111, 110, 99, + 111, 110, 116, 101, 110, 116, 45, 114, 97, 110, 103, 101, 99, 111, 111, + 107, 105, 101, 100, 97, 116, 101, 101, 116, 97, 103, 101, 120, 112, 101, + 99, 116, 101, 120, 112, 105, 114, 101, 115, 102, 114, 111, 109, 105, 102, + 45, 109, 97, 116, 99, 104, 105, 102, 45, 109, 111, 100, 105, 102, 105, + 101, 100, 45, 115, 105, 110, 99, 101, 105, 102, 45, 110, 111, 110, 101, + 45, 109, 97, 116, 99, 104, 105, 102, 45, 114, 97, 110, 103, 101, 105, + 102, 45, 117, 110, 109, 111, 100, 105, 102, 105, 101, 100, 45, 115, 105, + 110, 99, 101, 108, 97, 115, 116, 45, 109, 111, 100, 105, 102, 105, 101, + 100, 108, 98, 45, 99, 111, 115, 116, 45, 98, 105, 110, 108, 105, 110, + 107, 108, 111, 99, 97, 116, 105, 111, 110, 109, 97, 120, 45, 102, 111, + 114, 119, 97, 114, 100, 115, 112, 114, 111, 120, 121, 45, 97, 117, 116, + 104, 101, 110, 116, 105, 99, 97, 116, 101, 112, 114, 111, 120, 121, 45, + 97, 117, 116, 104, 111, 114, 105, 122, 97, 116, 105, 111, 110, 114, 97, + 110, 103, 101, 114, 101, 102, 101, 114, 101, 114, 114, 101, 102, 114, 101, + 115, 104, 114, 101, 116, 114, 121, 45, 97, 102, 116, 101, 114, 115, 101, + 114, 118, 101, 114, 115, 101, 116, 45, 99, 111, 111, 107, 105, 101, 115, + 116, 114, 105, 99, 116, 45, 116, 114, 97, 110, 115, 112, 111, 114, 116, + 45, 115, 101, 99, 117, 114, 105, 116, 121, 116, 114, 97, 110, 115, 102, + 101, 114, 45, 101, 110, 99, 111, 100, 105, 110, 103, 118, 97, 114, 121, + 118, 105, 97, 119, 119, 119, 45, 97, 117, 116, 104, 101, 110, 116, 105, + 99, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, + 102, 108, 97, 116, 101, 105, 100, 101, 110, 116, 105, 116, 121, 44, 103, + 122, 105, 112, 100, 101, 102, 108, 97, 116, 101, 44, 103, 122, 105, 112, + 105, 100, 101, 110, 116, 105, 116, 121, 44, 100, 101, 102, 108, 97, 116, + 101, 44, 103, 122, 105, 112}; + +static void static_ref(void *unused) {} +static void static_unref(grpc_exec_ctx *exec_ctx, void *unused) {} +static const grpc_slice_refcount_vtable static_sub_vtable = { + static_ref, static_unref, grpc_slice_default_eq_impl, + grpc_slice_default_hash_impl}; +const grpc_slice_refcount_vtable grpc_static_metadata_vtable = { + static_ref, static_unref, grpc_static_slice_eq, grpc_static_slice_hash}; +static grpc_slice_refcount static_sub_refcnt = {&static_sub_vtable, + &static_sub_refcnt}; +grpc_slice_refcount grpc_static_metadata_refcounts[GRPC_STATIC_MDSTR_COUNT] = { + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, + {&grpc_static_metadata_vtable, &static_sub_refcnt}, +}; + +const grpc_slice grpc_static_slice_table[GRPC_STATIC_MDSTR_COUNT] = { + {&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, + {&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, + {&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, + {&grpc_static_metadata_refcounts[6], {{g_bytes + 38, 12}}}, + {&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[8], {{g_bytes + 61, 16}}}, + {&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[11], {{g_bytes + 110, 21}}}, + {&grpc_static_metadata_refcounts[12], {{g_bytes + 131, 13}}}, + {&grpc_static_metadata_refcounts[13], {{g_bytes + 144, 14}}}, + {&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, + {&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[17], {{g_bytes + 201, 30}}}, + {&grpc_static_metadata_refcounts[18], {{g_bytes + 231, 37}}}, + {&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, + {&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, + {&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, + {&grpc_static_metadata_refcounts[22], {{g_bytes + 290, 12}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}, + {&grpc_static_metadata_refcounts[24], {{g_bytes + 302, 19}}}, + {&grpc_static_metadata_refcounts[25], {{g_bytes + 321, 12}}}, + {&grpc_static_metadata_refcounts[26], {{g_bytes + 333, 30}}}, + {&grpc_static_metadata_refcounts[27], {{g_bytes + 363, 31}}}, + {&grpc_static_metadata_refcounts[28], {{g_bytes + 394, 36}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 1}}}, + {&grpc_static_metadata_refcounts[30], {{g_bytes + 431, 1}}}, + {&grpc_static_metadata_refcounts[31], {{g_bytes + 432, 1}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}, + {&grpc_static_metadata_refcounts[34], {{g_bytes + 445, 7}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 452, 8}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 460, 16}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 476, 4}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 480, 3}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 483, 3}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 486, 4}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 490, 5}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 495, 4}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 499, 3}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 502, 3}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 505, 1}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 506, 11}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 3}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 520, 3}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 523, 3}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 526, 3}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 529, 3}}}, + {&grpc_static_metadata_refcounts[52], {{g_bytes + 532, 14}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 546, 13}}}, + {&grpc_static_metadata_refcounts[54], {{g_bytes + 559, 15}}}, + {&grpc_static_metadata_refcounts[55], {{g_bytes + 574, 13}}}, + {&grpc_static_metadata_refcounts[56], {{g_bytes + 587, 6}}}, + {&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 27}}}, + {&grpc_static_metadata_refcounts[58], {{g_bytes + 620, 3}}}, + {&grpc_static_metadata_refcounts[59], {{g_bytes + 623, 5}}}, + {&grpc_static_metadata_refcounts[60], {{g_bytes + 628, 13}}}, + {&grpc_static_metadata_refcounts[61], {{g_bytes + 641, 13}}}, + {&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 19}}}, + {&grpc_static_metadata_refcounts[63], {{g_bytes + 673, 16}}}, + {&grpc_static_metadata_refcounts[64], {{g_bytes + 689, 14}}}, + {&grpc_static_metadata_refcounts[65], {{g_bytes + 703, 16}}}, + {&grpc_static_metadata_refcounts[66], {{g_bytes + 719, 13}}}, + {&grpc_static_metadata_refcounts[67], {{g_bytes + 732, 6}}}, + {&grpc_static_metadata_refcounts[68], {{g_bytes + 738, 4}}}, + {&grpc_static_metadata_refcounts[69], {{g_bytes + 742, 4}}}, + {&grpc_static_metadata_refcounts[70], {{g_bytes + 746, 6}}}, + {&grpc_static_metadata_refcounts[71], {{g_bytes + 752, 7}}}, + {&grpc_static_metadata_refcounts[72], {{g_bytes + 759, 4}}}, + {&grpc_static_metadata_refcounts[73], {{g_bytes + 763, 8}}}, + {&grpc_static_metadata_refcounts[74], {{g_bytes + 771, 17}}}, + {&grpc_static_metadata_refcounts[75], {{g_bytes + 788, 13}}}, + {&grpc_static_metadata_refcounts[76], {{g_bytes + 801, 8}}}, + {&grpc_static_metadata_refcounts[77], {{g_bytes + 809, 19}}}, + {&grpc_static_metadata_refcounts[78], {{g_bytes + 828, 13}}}, + {&grpc_static_metadata_refcounts[79], {{g_bytes + 841, 11}}}, + {&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 4}}}, + {&grpc_static_metadata_refcounts[81], {{g_bytes + 856, 8}}}, + {&grpc_static_metadata_refcounts[82], {{g_bytes + 864, 12}}}, + {&grpc_static_metadata_refcounts[83], {{g_bytes + 876, 18}}}, + {&grpc_static_metadata_refcounts[84], {{g_bytes + 894, 19}}}, + {&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 5}}}, + {&grpc_static_metadata_refcounts[86], {{g_bytes + 918, 7}}}, + {&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 7}}}, + {&grpc_static_metadata_refcounts[88], {{g_bytes + 932, 11}}}, + {&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 6}}}, + {&grpc_static_metadata_refcounts[90], {{g_bytes + 949, 10}}}, + {&grpc_static_metadata_refcounts[91], {{g_bytes + 959, 25}}}, + {&grpc_static_metadata_refcounts[92], {{g_bytes + 984, 17}}}, + {&grpc_static_metadata_refcounts[93], {{g_bytes + 1001, 4}}}, + {&grpc_static_metadata_refcounts[94], {{g_bytes + 1005, 3}}}, + {&grpc_static_metadata_refcounts[95], {{g_bytes + 1008, 16}}}, + {&grpc_static_metadata_refcounts[96], {{g_bytes + 1024, 16}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1040, 13}}}, + {&grpc_static_metadata_refcounts[98], {{g_bytes + 1053, 12}}}, + {&grpc_static_metadata_refcounts[99], {{g_bytes + 1065, 21}}}, +}; + +uintptr_t grpc_static_mdelem_user_data[GRPC_STATIC_MDELEM_COUNT] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 4, 6, 6, 8, 8, 2, 4, 4}; + +static const int8_t elems_r[] = { + 11, 9, -3, 0, 10, 27, -74, 28, 0, 14, -7, 0, 0, 0, 18, 8, -2, + 0, 0, 13, 12, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -50, 0, -33, -55, -56, -57, -58, -57, 0, 40, 39, 38, 37, 36, 35, 34, + 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 22, + 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 12, 11, 0}; +static uint32_t elems_phash(uint32_t i) { + i -= 45; + uint32_t x = i % 98; + uint32_t y = i / 98; + uint32_t h = x; + if (y < GPR_ARRAY_SIZE(elems_r)) { + uint32_t delta = (uint32_t)elems_r[y]; + h += delta; + } + return h; +} + +static const uint16_t elem_keys[] = { + 1032, 1033, 1034, 247, 248, 249, 250, 251, 1623, 143, 144, 45, + 46, 440, 441, 442, 1523, 1632, 1633, 932, 933, 934, 729, 730, + 1423, 1532, 1533, 535, 731, 1923, 2023, 2123, 5223, 5523, 5623, 5723, + 5823, 1436, 1653, 5923, 6023, 6123, 6223, 6323, 6423, 6523, 6623, 6723, + 6823, 6923, 7023, 7123, 7223, 5423, 7323, 7423, 7523, 7623, 7723, 7823, + 7923, 8023, 8123, 8223, 1096, 1097, 1098, 1099, 8323, 8423, 8523, 8623, + 8723, 8823, 8923, 9023, 9123, 9223, 9323, 323, 9423, 9523, 1697, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 137, 238, 239, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0}; +static const uint8_t elem_idxs[] = { + 76, 79, 77, 19, 20, 21, 22, 23, 25, 15, 16, 17, 18, 11, + 12, 13, 38, 83, 84, 3, 4, 5, 0, 1, 43, 36, 37, 6, + 2, 72, 50, 57, 24, 28, 29, 30, 31, 7, 26, 32, 33, 34, + 35, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 27, 51, 52, + 53, 54, 55, 56, 58, 59, 60, 61, 78, 80, 81, 82, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 73, 14, 74, 75, 85, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 8, 9, 10}; + +grpc_mdelem grpc_static_mdelem_for_static_strings(int a, int b) { + if (a == -1 || b == -1) return GRPC_MDNULL; + uint32_t k = (uint32_t)(a * 100 + b); + uint32_t h = elems_phash(k); + return h < GPR_ARRAY_SIZE(elem_keys) && elem_keys[h] == k && + elem_idxs[h] != 255 + ? GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[elem_idxs[h]], + GRPC_MDELEM_STORAGE_STATIC) + : GRPC_MDNULL; +} + +grpc_mdelem_data grpc_static_mdelem_table[GRPC_STATIC_MDELEM_COUNT] = { + {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[29], {{g_bytes + 430, 1}}}}, + {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[30], {{g_bytes + 431, 1}}}}, + {{&grpc_static_metadata_refcounts[7], {{g_bytes + 50, 11}}}, + {&grpc_static_metadata_refcounts[31], {{g_bytes + 432, 1}}}}, + {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, + {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, + {{&grpc_static_metadata_refcounts[9], {{g_bytes + 77, 13}}}, + {&grpc_static_metadata_refcounts[34], {{g_bytes + 445, 7}}}}, + {{&grpc_static_metadata_refcounts[5], {{g_bytes + 36, 2}}}, + {&grpc_static_metadata_refcounts[35], {{g_bytes + 452, 8}}}}, + {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, + {&grpc_static_metadata_refcounts[36], {{g_bytes + 460, 16}}}}, + {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[37], {{g_bytes + 476, 4}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[38], {{g_bytes + 480, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[39], {{g_bytes + 483, 3}}}}, + {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[40], {{g_bytes + 486, 4}}}}, + {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[41], {{g_bytes + 490, 5}}}}, + {{&grpc_static_metadata_refcounts[4], {{g_bytes + 29, 7}}}, + {&grpc_static_metadata_refcounts[42], {{g_bytes + 495, 4}}}}, + {{&grpc_static_metadata_refcounts[3], {{g_bytes + 19, 10}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[43], {{g_bytes + 499, 3}}}}, + {{&grpc_static_metadata_refcounts[1], {{g_bytes + 5, 7}}}, + {&grpc_static_metadata_refcounts[44], {{g_bytes + 502, 3}}}}, + {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, + {&grpc_static_metadata_refcounts[45], {{g_bytes + 505, 1}}}}, + {{&grpc_static_metadata_refcounts[0], {{g_bytes + 0, 5}}}, + {&grpc_static_metadata_refcounts[46], {{g_bytes + 506, 11}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[47], {{g_bytes + 517, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[48], {{g_bytes + 520, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[49], {{g_bytes + 523, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[50], {{g_bytes + 526, 3}}}}, + {{&grpc_static_metadata_refcounts[2], {{g_bytes + 12, 7}}}, + {&grpc_static_metadata_refcounts[51], {{g_bytes + 529, 3}}}}, + {{&grpc_static_metadata_refcounts[52], {{g_bytes + 532, 14}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[53], {{g_bytes + 546, 13}}}}, + {{&grpc_static_metadata_refcounts[54], {{g_bytes + 559, 15}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[55], {{g_bytes + 574, 13}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[56], {{g_bytes + 587, 6}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[57], {{g_bytes + 593, 27}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[58], {{g_bytes + 620, 3}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[59], {{g_bytes + 623, 5}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[60], {{g_bytes + 628, 13}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[61], {{g_bytes + 641, 13}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[62], {{g_bytes + 654, 19}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, + {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, + {{&grpc_static_metadata_refcounts[15], {{g_bytes + 170, 16}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[63], {{g_bytes + 673, 16}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[64], {{g_bytes + 689, 14}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[65], {{g_bytes + 703, 16}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[66], {{g_bytes + 719, 13}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[14], {{g_bytes + 158, 12}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[67], {{g_bytes + 732, 6}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[68], {{g_bytes + 738, 4}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[69], {{g_bytes + 742, 4}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[70], {{g_bytes + 746, 6}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[71], {{g_bytes + 752, 7}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[72], {{g_bytes + 759, 4}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[20], {{g_bytes + 278, 4}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[73], {{g_bytes + 763, 8}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[74], {{g_bytes + 771, 17}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[75], {{g_bytes + 788, 13}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[76], {{g_bytes + 801, 8}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[77], {{g_bytes + 809, 19}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[78], {{g_bytes + 828, 13}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[21], {{g_bytes + 282, 8}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[79], {{g_bytes + 841, 11}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[80], {{g_bytes + 852, 4}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[81], {{g_bytes + 856, 8}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[82], {{g_bytes + 864, 12}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[83], {{g_bytes + 876, 18}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[84], {{g_bytes + 894, 19}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[85], {{g_bytes + 913, 5}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[86], {{g_bytes + 918, 7}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[87], {{g_bytes + 925, 7}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[88], {{g_bytes + 932, 11}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[89], {{g_bytes + 943, 6}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[90], {{g_bytes + 949, 10}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[91], {{g_bytes + 959, 25}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[92], {{g_bytes + 984, 17}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[19], {{g_bytes + 268, 10}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[93], {{g_bytes + 1001, 4}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[94], {{g_bytes + 1005, 3}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[95], {{g_bytes + 1008, 16}}}, + {&grpc_static_metadata_refcounts[23], {{g_bytes + 302, 0}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[34], {{g_bytes + 445, 7}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[96], {{g_bytes + 1024, 16}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1040, 13}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[98], {{g_bytes + 1053, 12}}}}, + {{&grpc_static_metadata_refcounts[10], {{g_bytes + 90, 20}}}, + {&grpc_static_metadata_refcounts[99], {{g_bytes + 1065, 21}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[32], {{g_bytes + 433, 8}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[33], {{g_bytes + 441, 4}}}}, + {{&grpc_static_metadata_refcounts[16], {{g_bytes + 186, 15}}}, + {&grpc_static_metadata_refcounts[97], {{g_bytes + 1040, 13}}}}, +}; +bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = { + true, // :path + true, // :method + true, // :status + true, // :authority + true, // :scheme + true, // te + true, // grpc-message + true, // grpc-status + true, // grpc-payload-bin + true, // grpc-encoding + true, // grpc-accept-encoding + true, // grpc-server-stats-bin + true, // grpc-tags-bin + true, // grpc-trace-bin + true, // content-type + true, // content-encoding + true, // accept-encoding + true, // grpc-internal-encoding-request + true, // grpc-internal-stream-encoding-request + true, // user-agent + true, // host + true, // lb-token +}; + +const uint8_t grpc_static_accept_encoding_metadata[8] = {0, 76, 77, 78, + 79, 80, 81, 82}; + +const uint8_t grpc_static_accept_stream_encoding_metadata[4] = {0, 83, 84, 85}; diff --git a/src/core/lib/transport/status_conversion.c b/src/core/lib/transport/status_conversion.c deleted file mode 100644 index a40d333284..0000000000 --- a/src/core/lib/transport/status_conversion.c +++ /dev/null @@ -1,98 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/status_conversion.h" - -grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status) { - switch (status) { - case GRPC_STATUS_OK: - return GRPC_HTTP2_NO_ERROR; - case GRPC_STATUS_CANCELLED: - return GRPC_HTTP2_CANCEL; - case GRPC_STATUS_DEADLINE_EXCEEDED: - return GRPC_HTTP2_CANCEL; - case GRPC_STATUS_RESOURCE_EXHAUSTED: - return GRPC_HTTP2_ENHANCE_YOUR_CALM; - case GRPC_STATUS_PERMISSION_DENIED: - return GRPC_HTTP2_INADEQUATE_SECURITY; - case GRPC_STATUS_UNAVAILABLE: - return GRPC_HTTP2_REFUSED_STREAM; - default: - return GRPC_HTTP2_INTERNAL_ERROR; - } -} - -grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error, - gpr_timespec deadline) { - switch (error) { - case GRPC_HTTP2_NO_ERROR: - /* should never be received */ - return GRPC_STATUS_INTERNAL; - case GRPC_HTTP2_CANCEL: - /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been - * exceeded */ - return gpr_time_cmp(gpr_now(deadline.clock_type), deadline) >= 0 - ? GRPC_STATUS_DEADLINE_EXCEEDED - : GRPC_STATUS_CANCELLED; - case GRPC_HTTP2_ENHANCE_YOUR_CALM: - return GRPC_STATUS_RESOURCE_EXHAUSTED; - case GRPC_HTTP2_INADEQUATE_SECURITY: - return GRPC_STATUS_PERMISSION_DENIED; - case GRPC_HTTP2_REFUSED_STREAM: - return GRPC_STATUS_UNAVAILABLE; - default: - return GRPC_STATUS_INTERNAL; - } -} - -grpc_status_code grpc_http2_status_to_grpc_status(int status) { - switch (status) { - /* these HTTP2 status codes are called out explicitly in status.proto */ - case 200: - return GRPC_STATUS_OK; - case 400: - return GRPC_STATUS_INVALID_ARGUMENT; - case 401: - return GRPC_STATUS_UNAUTHENTICATED; - case 403: - return GRPC_STATUS_PERMISSION_DENIED; - case 404: - return GRPC_STATUS_NOT_FOUND; - case 409: - return GRPC_STATUS_ABORTED; - case 412: - return GRPC_STATUS_FAILED_PRECONDITION; - case 429: - return GRPC_STATUS_RESOURCE_EXHAUSTED; - case 499: - return GRPC_STATUS_CANCELLED; - case 500: - return GRPC_STATUS_UNKNOWN; - case 501: - return GRPC_STATUS_UNIMPLEMENTED; - case 503: - return GRPC_STATUS_UNAVAILABLE; - case 504: - return GRPC_STATUS_DEADLINE_EXCEEDED; - /* everything else is unknown */ - default: - return GRPC_STATUS_UNKNOWN; - } -} - -int grpc_status_to_http2_status(grpc_status_code status) { return 200; } diff --git a/src/core/lib/transport/status_conversion.cc b/src/core/lib/transport/status_conversion.cc new file mode 100644 index 0000000000..a40d333284 --- /dev/null +++ b/src/core/lib/transport/status_conversion.cc @@ -0,0 +1,98 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/status_conversion.h" + +grpc_http2_error_code grpc_status_to_http2_error(grpc_status_code status) { + switch (status) { + case GRPC_STATUS_OK: + return GRPC_HTTP2_NO_ERROR; + case GRPC_STATUS_CANCELLED: + return GRPC_HTTP2_CANCEL; + case GRPC_STATUS_DEADLINE_EXCEEDED: + return GRPC_HTTP2_CANCEL; + case GRPC_STATUS_RESOURCE_EXHAUSTED: + return GRPC_HTTP2_ENHANCE_YOUR_CALM; + case GRPC_STATUS_PERMISSION_DENIED: + return GRPC_HTTP2_INADEQUATE_SECURITY; + case GRPC_STATUS_UNAVAILABLE: + return GRPC_HTTP2_REFUSED_STREAM; + default: + return GRPC_HTTP2_INTERNAL_ERROR; + } +} + +grpc_status_code grpc_http2_error_to_grpc_status(grpc_http2_error_code error, + gpr_timespec deadline) { + switch (error) { + case GRPC_HTTP2_NO_ERROR: + /* should never be received */ + return GRPC_STATUS_INTERNAL; + case GRPC_HTTP2_CANCEL: + /* http2 cancel translates to STATUS_CANCELLED iff deadline hasn't been + * exceeded */ + return gpr_time_cmp(gpr_now(deadline.clock_type), deadline) >= 0 + ? GRPC_STATUS_DEADLINE_EXCEEDED + : GRPC_STATUS_CANCELLED; + case GRPC_HTTP2_ENHANCE_YOUR_CALM: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case GRPC_HTTP2_INADEQUATE_SECURITY: + return GRPC_STATUS_PERMISSION_DENIED; + case GRPC_HTTP2_REFUSED_STREAM: + return GRPC_STATUS_UNAVAILABLE; + default: + return GRPC_STATUS_INTERNAL; + } +} + +grpc_status_code grpc_http2_status_to_grpc_status(int status) { + switch (status) { + /* these HTTP2 status codes are called out explicitly in status.proto */ + case 200: + return GRPC_STATUS_OK; + case 400: + return GRPC_STATUS_INVALID_ARGUMENT; + case 401: + return GRPC_STATUS_UNAUTHENTICATED; + case 403: + return GRPC_STATUS_PERMISSION_DENIED; + case 404: + return GRPC_STATUS_NOT_FOUND; + case 409: + return GRPC_STATUS_ABORTED; + case 412: + return GRPC_STATUS_FAILED_PRECONDITION; + case 429: + return GRPC_STATUS_RESOURCE_EXHAUSTED; + case 499: + return GRPC_STATUS_CANCELLED; + case 500: + return GRPC_STATUS_UNKNOWN; + case 501: + return GRPC_STATUS_UNIMPLEMENTED; + case 503: + return GRPC_STATUS_UNAVAILABLE; + case 504: + return GRPC_STATUS_DEADLINE_EXCEEDED; + /* everything else is unknown */ + default: + return GRPC_STATUS_UNKNOWN; + } +} + +int grpc_status_to_http2_status(grpc_status_code status) { return 200; } diff --git a/src/core/lib/transport/timeout_encoding.c b/src/core/lib/transport/timeout_encoding.c deleted file mode 100644 index 02f179d6a3..0000000000 --- a/src/core/lib/transport/timeout_encoding.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/timeout_encoding.h" - -#include -#include - -#include -#include "src/core/lib/support/string.h" - -static int64_t round_up(int64_t x, int64_t divisor) { - return (x / divisor + (x % divisor != 0)) * divisor; -} - -/* round an integer up to the next value with three significant figures */ -static int64_t round_up_to_three_sig_figs(int64_t x) { - if (x < 1000) return x; - if (x < 10000) return round_up(x, 10); - if (x < 100000) return round_up(x, 100); - if (x < 1000000) return round_up(x, 1000); - if (x < 10000000) return round_up(x, 10000); - if (x < 100000000) return round_up(x, 100000); - if (x < 1000000000) return round_up(x, 1000000); - return round_up(x, 10000000); -} - -/* encode our minimum viable timeout value */ -static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); } - -static void enc_ext(char *buffer, int64_t value, char ext) { - int n = int64_ttoa(value, buffer); - buffer[n] = ext; - buffer[n + 1] = 0; -} - -static void enc_seconds(char *buffer, int64_t sec) { - if (sec % 3600 == 0) { - enc_ext(buffer, sec / 3600, 'H'); - } else if (sec % 60 == 0) { - enc_ext(buffer, sec / 60, 'M'); - } else { - enc_ext(buffer, sec, 'S'); - } -} - -static void enc_nanos(char *buffer, int64_t x) { - x = round_up_to_three_sig_figs(x); - if (x < 100000) { - if (x % 1000 == 0) { - enc_ext(buffer, x / 1000, 'u'); - } else { - enc_ext(buffer, x, 'n'); - } - } else if (x < 100000000) { - if (x % 1000000 == 0) { - enc_ext(buffer, x / 1000000, 'm'); - } else { - enc_ext(buffer, x / 1000, 'u'); - } - } else if (x < 1000000000) { - enc_ext(buffer, x / 1000000, 'm'); - } else { - /* note that this is only ever called with times of less than one second, - so if we reach here the time must have been rounded up to a whole second - (and no more) */ - memcpy(buffer, "1S", 3); - } -} - -static void enc_micros(char *buffer, int64_t x) { - x = round_up_to_three_sig_figs(x); - if (x < 100000) { - if (x % 1000 == 0) { - enc_ext(buffer, x / 1000, 'm'); - } else { - enc_ext(buffer, x, 'u'); - } - } else if (x < 100000000) { - if (x % 1000000 == 0) { - enc_ext(buffer, x / 1000000, 'S'); - } else { - enc_ext(buffer, x / 1000, 'm'); - } - } else { - enc_ext(buffer, x / 1000000, 'S'); - } -} - -void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer) { - if (timeout.tv_sec < 0) { - enc_tiny(buffer); - } else if (timeout.tv_sec == 0) { - enc_nanos(buffer, timeout.tv_nsec); - } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) { - enc_micros(buffer, - (int64_t)(timeout.tv_sec * 1000000) + - (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0))); - } else { - enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0)); - } -} - -static int is_all_whitespace(const char *p, const char *end) { - while (p != end && *p == ' ') p++; - return p == end; -} - -int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout) { - int32_t x = 0; - const uint8_t *p = GRPC_SLICE_START_PTR(text); - const uint8_t *end = GRPC_SLICE_END_PTR(text); - int have_digit = 0; - /* skip whitespace */ - for (; p != end && *p == ' '; p++) - ; - /* decode numeric part */ - for (; p != end && *p >= '0' && *p <= '9'; p++) { - int32_t digit = (int32_t)(*p - (uint8_t)'0'); - have_digit = 1; - /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */ - if (x >= (100 * 1000 * 1000)) { - if (x != (100 * 1000 * 1000) || digit != 0) { - *timeout = gpr_inf_future(GPR_TIMESPAN); - return 1; - } - } - x = x * 10 + digit; - } - if (!have_digit) return 0; - /* skip whitespace */ - for (; p != end && *p == ' '; p++) - ; - if (p == end) return 0; - /* decode unit specifier */ - switch (*p) { - case 'n': - *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN); - break; - case 'u': - *timeout = gpr_time_from_micros(x, GPR_TIMESPAN); - break; - case 'm': - *timeout = gpr_time_from_millis(x, GPR_TIMESPAN); - break; - case 'S': - *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN); - break; - case 'M': - *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN); - break; - case 'H': - *timeout = gpr_time_from_hours(x, GPR_TIMESPAN); - break; - default: - return 0; - } - p++; - return is_all_whitespace((const char *)p, (const char *)end); -} diff --git a/src/core/lib/transport/timeout_encoding.cc b/src/core/lib/transport/timeout_encoding.cc new file mode 100644 index 0000000000..02f179d6a3 --- /dev/null +++ b/src/core/lib/transport/timeout_encoding.cc @@ -0,0 +1,175 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/timeout_encoding.h" + +#include +#include + +#include +#include "src/core/lib/support/string.h" + +static int64_t round_up(int64_t x, int64_t divisor) { + return (x / divisor + (x % divisor != 0)) * divisor; +} + +/* round an integer up to the next value with three significant figures */ +static int64_t round_up_to_three_sig_figs(int64_t x) { + if (x < 1000) return x; + if (x < 10000) return round_up(x, 10); + if (x < 100000) return round_up(x, 100); + if (x < 1000000) return round_up(x, 1000); + if (x < 10000000) return round_up(x, 10000); + if (x < 100000000) return round_up(x, 100000); + if (x < 1000000000) return round_up(x, 1000000); + return round_up(x, 10000000); +} + +/* encode our minimum viable timeout value */ +static void enc_tiny(char *buffer) { memcpy(buffer, "1n", 3); } + +static void enc_ext(char *buffer, int64_t value, char ext) { + int n = int64_ttoa(value, buffer); + buffer[n] = ext; + buffer[n + 1] = 0; +} + +static void enc_seconds(char *buffer, int64_t sec) { + if (sec % 3600 == 0) { + enc_ext(buffer, sec / 3600, 'H'); + } else if (sec % 60 == 0) { + enc_ext(buffer, sec / 60, 'M'); + } else { + enc_ext(buffer, sec, 'S'); + } +} + +static void enc_nanos(char *buffer, int64_t x) { + x = round_up_to_three_sig_figs(x); + if (x < 100000) { + if (x % 1000 == 0) { + enc_ext(buffer, x / 1000, 'u'); + } else { + enc_ext(buffer, x, 'n'); + } + } else if (x < 100000000) { + if (x % 1000000 == 0) { + enc_ext(buffer, x / 1000000, 'm'); + } else { + enc_ext(buffer, x / 1000, 'u'); + } + } else if (x < 1000000000) { + enc_ext(buffer, x / 1000000, 'm'); + } else { + /* note that this is only ever called with times of less than one second, + so if we reach here the time must have been rounded up to a whole second + (and no more) */ + memcpy(buffer, "1S", 3); + } +} + +static void enc_micros(char *buffer, int64_t x) { + x = round_up_to_three_sig_figs(x); + if (x < 100000) { + if (x % 1000 == 0) { + enc_ext(buffer, x / 1000, 'm'); + } else { + enc_ext(buffer, x, 'u'); + } + } else if (x < 100000000) { + if (x % 1000000 == 0) { + enc_ext(buffer, x / 1000000, 'S'); + } else { + enc_ext(buffer, x / 1000, 'm'); + } + } else { + enc_ext(buffer, x / 1000000, 'S'); + } +} + +void grpc_http2_encode_timeout(gpr_timespec timeout, char *buffer) { + if (timeout.tv_sec < 0) { + enc_tiny(buffer); + } else if (timeout.tv_sec == 0) { + enc_nanos(buffer, timeout.tv_nsec); + } else if (timeout.tv_sec < 1000 && timeout.tv_nsec != 0) { + enc_micros(buffer, + (int64_t)(timeout.tv_sec * 1000000) + + (timeout.tv_nsec / 1000 + (timeout.tv_nsec % 1000 != 0))); + } else { + enc_seconds(buffer, timeout.tv_sec + (timeout.tv_nsec != 0)); + } +} + +static int is_all_whitespace(const char *p, const char *end) { + while (p != end && *p == ' ') p++; + return p == end; +} + +int grpc_http2_decode_timeout(grpc_slice text, gpr_timespec *timeout) { + int32_t x = 0; + const uint8_t *p = GRPC_SLICE_START_PTR(text); + const uint8_t *end = GRPC_SLICE_END_PTR(text); + int have_digit = 0; + /* skip whitespace */ + for (; p != end && *p == ' '; p++) + ; + /* decode numeric part */ + for (; p != end && *p >= '0' && *p <= '9'; p++) { + int32_t digit = (int32_t)(*p - (uint8_t)'0'); + have_digit = 1; + /* spec allows max. 8 digits, but we allow values up to 1,000,000,000 */ + if (x >= (100 * 1000 * 1000)) { + if (x != (100 * 1000 * 1000) || digit != 0) { + *timeout = gpr_inf_future(GPR_TIMESPAN); + return 1; + } + } + x = x * 10 + digit; + } + if (!have_digit) return 0; + /* skip whitespace */ + for (; p != end && *p == ' '; p++) + ; + if (p == end) return 0; + /* decode unit specifier */ + switch (*p) { + case 'n': + *timeout = gpr_time_from_nanos(x, GPR_TIMESPAN); + break; + case 'u': + *timeout = gpr_time_from_micros(x, GPR_TIMESPAN); + break; + case 'm': + *timeout = gpr_time_from_millis(x, GPR_TIMESPAN); + break; + case 'S': + *timeout = gpr_time_from_seconds(x, GPR_TIMESPAN); + break; + case 'M': + *timeout = gpr_time_from_minutes(x, GPR_TIMESPAN); + break; + case 'H': + *timeout = gpr_time_from_hours(x, GPR_TIMESPAN); + break; + default: + return 0; + } + p++; + return is_all_whitespace((const char *)p, (const char *)end); +} diff --git a/src/core/lib/transport/transport.c b/src/core/lib/transport/transport.c deleted file mode 100644 index 682a820b48..0000000000 --- a/src/core/lib/transport/transport.c +++ /dev/null @@ -1,289 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/transport/transport.h" - -#include - -#include -#include -#include -#include - -#include "src/core/lib/iomgr/executor.h" -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/transport_impl.h" - -#ifndef NDEBUG -grpc_tracer_flag grpc_trace_stream_refcount = - GRPC_TRACER_INITIALIZER(false, "stream_refcount"); -#endif - -#ifndef NDEBUG -void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason) { - if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); - gpr_log(GPR_DEBUG, "%s %p:%p REF %" PRIdPTR "->%" PRIdPTR " %s", - refcount->object_type, refcount, refcount->destroy.cb_arg, val, - val + 1, reason); - } -#else -void grpc_stream_ref(grpc_stream_refcount *refcount) { -#endif - gpr_ref_non_zero(&refcount->refs); -} - -#ifndef NDEBUG -void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount, - const char *reason) { - if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) { - gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); - gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s", - refcount->object_type, refcount, refcount->destroy.cb_arg, val, - val - 1, reason); - } -#else -void grpc_stream_unref(grpc_exec_ctx *exec_ctx, - grpc_stream_refcount *refcount) { -#endif - if (gpr_unref(&refcount->refs)) { - if (exec_ctx->flags & GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP) { - /* Ick. - The thread we're running on MAY be owned (indirectly) by a call-stack. - If that's the case, destroying the call-stack MAY try to destroy the - thread, which is a tangled mess that we just don't want to ever have to - cope with. - Throw this over to the executor (on a core-owned thread) and process it - there. */ - refcount->destroy.scheduler = - grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); - } - GRPC_CLOSURE_SCHED(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE); - } -} - -#define STREAM_REF_FROM_SLICE_REF(p) \ - ((grpc_stream_refcount *)(((uint8_t *)p) - \ - offsetof(grpc_stream_refcount, slice_refcount))) - -static void slice_stream_ref(void *p) { -#ifndef NDEBUG - grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p), "slice"); -#else - grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p)); -#endif -} - -static void slice_stream_unref(grpc_exec_ctx *exec_ctx, void *p) { -#ifndef NDEBUG - grpc_stream_unref(exec_ctx, STREAM_REF_FROM_SLICE_REF(p), "slice"); -#else - grpc_stream_unref(exec_ctx, STREAM_REF_FROM_SLICE_REF(p)); -#endif -} - -grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount *refcount, - void *buffer, size_t length) { - slice_stream_ref(&refcount->slice_refcount); - grpc_slice res; - res.refcount = &refcount->slice_refcount, - res.data.refcounted.bytes = (uint8_t *)buffer; - res.data.refcounted.length = length; - return res; -} - -static const grpc_slice_refcount_vtable stream_ref_slice_vtable = { - .ref = slice_stream_ref, - .unref = slice_stream_unref, - .eq = grpc_slice_default_eq_impl, - .hash = grpc_slice_default_hash_impl}; - -#ifndef NDEBUG -void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, - grpc_iomgr_cb_func cb, void *cb_arg, - const char *object_type) { - refcount->object_type = object_type; -#else -void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, - grpc_iomgr_cb_func cb, void *cb_arg) { -#endif - gpr_ref_init(&refcount->refs, initial_refs); - GRPC_CLOSURE_INIT(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx); - refcount->slice_refcount.vtable = &stream_ref_slice_vtable; - refcount->slice_refcount.sub_refcount = &refcount->slice_refcount; -} - -static void move64(uint64_t *from, uint64_t *to) { - *to += *from; - *from = 0; -} - -void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats *from, - grpc_transport_one_way_stats *to) { - move64(&from->framing_bytes, &to->framing_bytes); - move64(&from->data_bytes, &to->data_bytes); - move64(&from->header_bytes, &to->header_bytes); -} - -void grpc_transport_move_stats(grpc_transport_stream_stats *from, - grpc_transport_stream_stats *to) { - grpc_transport_move_one_way_stats(&from->incoming, &to->incoming); - grpc_transport_move_one_way_stats(&from->outgoing, &to->outgoing); -} - -size_t grpc_transport_stream_size(grpc_transport *transport) { - return transport->vtable->sizeof_stream; -} - -void grpc_transport_destroy(grpc_exec_ctx *exec_ctx, - grpc_transport *transport) { - transport->vtable->destroy(exec_ctx, transport); -} - -int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, grpc_stream *stream, - grpc_stream_refcount *refcount, - const void *server_data, gpr_arena *arena) { - return transport->vtable->init_stream(exec_ctx, transport, stream, refcount, - server_data, arena); -} - -void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_stream *stream, - grpc_transport_stream_op_batch *op) { - transport->vtable->perform_stream_op(exec_ctx, transport, stream, op); -} - -void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_transport_op *op) { - transport->vtable->perform_op(exec_ctx, transport, op); -} - -void grpc_transport_set_pops(grpc_exec_ctx *exec_ctx, grpc_transport *transport, - grpc_stream *stream, - grpc_polling_entity *pollent) { - grpc_pollset *pollset; - grpc_pollset_set *pollset_set; - if ((pollset = grpc_polling_entity_pollset(pollent)) != NULL) { - transport->vtable->set_pollset(exec_ctx, transport, stream, pollset); - } else if ((pollset_set = grpc_polling_entity_pollset_set(pollent)) != NULL) { - transport->vtable->set_pollset_set(exec_ctx, transport, stream, - pollset_set); - } else { - abort(); - } -} - -void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx, - grpc_transport *transport, - grpc_stream *stream, - grpc_closure *then_schedule_closure) { - transport->vtable->destroy_stream(exec_ctx, transport, stream, - then_schedule_closure); -} - -grpc_endpoint *grpc_transport_get_endpoint(grpc_exec_ctx *exec_ctx, - grpc_transport *transport) { - return transport->vtable->get_endpoint(exec_ctx, transport); -} - -// This comment should be sung to the tune of -// "Supercalifragilisticexpialidocious": -// -// grpc_transport_stream_op_batch_finish_with_failure -// is a function that must always unref cancel_error -// though it lives in lib, it handles transport stream ops sure -// it's grpc_transport_stream_op_batch_finish_with_failure -void grpc_transport_stream_op_batch_finish_with_failure( - grpc_exec_ctx *exec_ctx, grpc_transport_stream_op_batch *batch, - grpc_error *error, grpc_call_combiner *call_combiner) { - if (batch->send_message) { - grpc_byte_stream_destroy(exec_ctx, - batch->payload->send_message.send_message); - } - if (batch->recv_message) { - GRPC_CALL_COMBINER_START(exec_ctx, call_combiner, - batch->payload->recv_message.recv_message_ready, - GRPC_ERROR_REF(error), - "failing recv_message_ready"); - } - if (batch->recv_initial_metadata) { - GRPC_CALL_COMBINER_START( - exec_ctx, call_combiner, - batch->payload->recv_initial_metadata.recv_initial_metadata_ready, - GRPC_ERROR_REF(error), "failing recv_initial_metadata_ready"); - } - GRPC_CLOSURE_SCHED(exec_ctx, batch->on_complete, error); - if (batch->cancel_stream) { - GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error); - } -} - -typedef struct { - grpc_closure outer_on_complete; - grpc_closure *inner_on_complete; - grpc_transport_op op; -} made_transport_op; - -static void destroy_made_transport_op(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - made_transport_op *op = (made_transport_op *)arg; - GRPC_CLOSURE_SCHED(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error)); - gpr_free(op); -} - -grpc_transport_op *grpc_make_transport_op(grpc_closure *on_complete) { - made_transport_op *op = (made_transport_op *)gpr_malloc(sizeof(*op)); - GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_op, op, - grpc_schedule_on_exec_ctx); - op->inner_on_complete = on_complete; - memset(&op->op, 0, sizeof(op->op)); - op->op.on_consumed = &op->outer_on_complete; - return &op->op; -} - -typedef struct { - grpc_closure outer_on_complete; - grpc_closure *inner_on_complete; - grpc_transport_stream_op_batch op; - grpc_transport_stream_op_batch_payload payload; -} made_transport_stream_op; - -static void destroy_made_transport_stream_op(grpc_exec_ctx *exec_ctx, void *arg, - grpc_error *error) { - made_transport_stream_op *op = (made_transport_stream_op *)arg; - grpc_closure *c = op->inner_on_complete; - gpr_free(op); - GRPC_CLOSURE_RUN(exec_ctx, c, GRPC_ERROR_REF(error)); -} - -grpc_transport_stream_op_batch *grpc_make_transport_stream_op( - grpc_closure *on_complete) { - made_transport_stream_op *op = - (made_transport_stream_op *)gpr_zalloc(sizeof(*op)); - op->op.payload = &op->payload; - GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_stream_op, - op, grpc_schedule_on_exec_ctx); - op->inner_on_complete = on_complete; - op->op.on_complete = &op->outer_on_complete; - return &op->op; -} diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc new file mode 100644 index 0000000000..682a820b48 --- /dev/null +++ b/src/core/lib/transport/transport.cc @@ -0,0 +1,289 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/transport/transport.h" + +#include + +#include +#include +#include +#include + +#include "src/core/lib/iomgr/executor.h" +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/transport_impl.h" + +#ifndef NDEBUG +grpc_tracer_flag grpc_trace_stream_refcount = + GRPC_TRACER_INITIALIZER(false, "stream_refcount"); +#endif + +#ifndef NDEBUG +void grpc_stream_ref(grpc_stream_refcount *refcount, const char *reason) { + if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); + gpr_log(GPR_DEBUG, "%s %p:%p REF %" PRIdPTR "->%" PRIdPTR " %s", + refcount->object_type, refcount, refcount->destroy.cb_arg, val, + val + 1, reason); + } +#else +void grpc_stream_ref(grpc_stream_refcount *refcount) { +#endif + gpr_ref_non_zero(&refcount->refs); +} + +#ifndef NDEBUG +void grpc_stream_unref(grpc_exec_ctx *exec_ctx, grpc_stream_refcount *refcount, + const char *reason) { + if (GRPC_TRACER_ON(grpc_trace_stream_refcount)) { + gpr_atm val = gpr_atm_no_barrier_load(&refcount->refs.count); + gpr_log(GPR_DEBUG, "%s %p:%p UNREF %" PRIdPTR "->%" PRIdPTR " %s", + refcount->object_type, refcount, refcount->destroy.cb_arg, val, + val - 1, reason); + } +#else +void grpc_stream_unref(grpc_exec_ctx *exec_ctx, + grpc_stream_refcount *refcount) { +#endif + if (gpr_unref(&refcount->refs)) { + if (exec_ctx->flags & GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP) { + /* Ick. + The thread we're running on MAY be owned (indirectly) by a call-stack. + If that's the case, destroying the call-stack MAY try to destroy the + thread, which is a tangled mess that we just don't want to ever have to + cope with. + Throw this over to the executor (on a core-owned thread) and process it + there. */ + refcount->destroy.scheduler = + grpc_executor_scheduler(GRPC_EXECUTOR_SHORT); + } + GRPC_CLOSURE_SCHED(exec_ctx, &refcount->destroy, GRPC_ERROR_NONE); + } +} + +#define STREAM_REF_FROM_SLICE_REF(p) \ + ((grpc_stream_refcount *)(((uint8_t *)p) - \ + offsetof(grpc_stream_refcount, slice_refcount))) + +static void slice_stream_ref(void *p) { +#ifndef NDEBUG + grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p), "slice"); +#else + grpc_stream_ref(STREAM_REF_FROM_SLICE_REF(p)); +#endif +} + +static void slice_stream_unref(grpc_exec_ctx *exec_ctx, void *p) { +#ifndef NDEBUG + grpc_stream_unref(exec_ctx, STREAM_REF_FROM_SLICE_REF(p), "slice"); +#else + grpc_stream_unref(exec_ctx, STREAM_REF_FROM_SLICE_REF(p)); +#endif +} + +grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount *refcount, + void *buffer, size_t length) { + slice_stream_ref(&refcount->slice_refcount); + grpc_slice res; + res.refcount = &refcount->slice_refcount, + res.data.refcounted.bytes = (uint8_t *)buffer; + res.data.refcounted.length = length; + return res; +} + +static const grpc_slice_refcount_vtable stream_ref_slice_vtable = { + .ref = slice_stream_ref, + .unref = slice_stream_unref, + .eq = grpc_slice_default_eq_impl, + .hash = grpc_slice_default_hash_impl}; + +#ifndef NDEBUG +void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, + grpc_iomgr_cb_func cb, void *cb_arg, + const char *object_type) { + refcount->object_type = object_type; +#else +void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, + grpc_iomgr_cb_func cb, void *cb_arg) { +#endif + gpr_ref_init(&refcount->refs, initial_refs); + GRPC_CLOSURE_INIT(&refcount->destroy, cb, cb_arg, grpc_schedule_on_exec_ctx); + refcount->slice_refcount.vtable = &stream_ref_slice_vtable; + refcount->slice_refcount.sub_refcount = &refcount->slice_refcount; +} + +static void move64(uint64_t *from, uint64_t *to) { + *to += *from; + *from = 0; +} + +void grpc_transport_move_one_way_stats(grpc_transport_one_way_stats *from, + grpc_transport_one_way_stats *to) { + move64(&from->framing_bytes, &to->framing_bytes); + move64(&from->data_bytes, &to->data_bytes); + move64(&from->header_bytes, &to->header_bytes); +} + +void grpc_transport_move_stats(grpc_transport_stream_stats *from, + grpc_transport_stream_stats *to) { + grpc_transport_move_one_way_stats(&from->incoming, &to->incoming); + grpc_transport_move_one_way_stats(&from->outgoing, &to->outgoing); +} + +size_t grpc_transport_stream_size(grpc_transport *transport) { + return transport->vtable->sizeof_stream; +} + +void grpc_transport_destroy(grpc_exec_ctx *exec_ctx, + grpc_transport *transport) { + transport->vtable->destroy(exec_ctx, transport); +} + +int grpc_transport_init_stream(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, grpc_stream *stream, + grpc_stream_refcount *refcount, + const void *server_data, gpr_arena *arena) { + return transport->vtable->init_stream(exec_ctx, transport, stream, refcount, + server_data, arena); +} + +void grpc_transport_perform_stream_op(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, + grpc_stream *stream, + grpc_transport_stream_op_batch *op) { + transport->vtable->perform_stream_op(exec_ctx, transport, stream, op); +} + +void grpc_transport_perform_op(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, + grpc_transport_op *op) { + transport->vtable->perform_op(exec_ctx, transport, op); +} + +void grpc_transport_set_pops(grpc_exec_ctx *exec_ctx, grpc_transport *transport, + grpc_stream *stream, + grpc_polling_entity *pollent) { + grpc_pollset *pollset; + grpc_pollset_set *pollset_set; + if ((pollset = grpc_polling_entity_pollset(pollent)) != NULL) { + transport->vtable->set_pollset(exec_ctx, transport, stream, pollset); + } else if ((pollset_set = grpc_polling_entity_pollset_set(pollent)) != NULL) { + transport->vtable->set_pollset_set(exec_ctx, transport, stream, + pollset_set); + } else { + abort(); + } +} + +void grpc_transport_destroy_stream(grpc_exec_ctx *exec_ctx, + grpc_transport *transport, + grpc_stream *stream, + grpc_closure *then_schedule_closure) { + transport->vtable->destroy_stream(exec_ctx, transport, stream, + then_schedule_closure); +} + +grpc_endpoint *grpc_transport_get_endpoint(grpc_exec_ctx *exec_ctx, + grpc_transport *transport) { + return transport->vtable->get_endpoint(exec_ctx, transport); +} + +// This comment should be sung to the tune of +// "Supercalifragilisticexpialidocious": +// +// grpc_transport_stream_op_batch_finish_with_failure +// is a function that must always unref cancel_error +// though it lives in lib, it handles transport stream ops sure +// it's grpc_transport_stream_op_batch_finish_with_failure +void grpc_transport_stream_op_batch_finish_with_failure( + grpc_exec_ctx *exec_ctx, grpc_transport_stream_op_batch *batch, + grpc_error *error, grpc_call_combiner *call_combiner) { + if (batch->send_message) { + grpc_byte_stream_destroy(exec_ctx, + batch->payload->send_message.send_message); + } + if (batch->recv_message) { + GRPC_CALL_COMBINER_START(exec_ctx, call_combiner, + batch->payload->recv_message.recv_message_ready, + GRPC_ERROR_REF(error), + "failing recv_message_ready"); + } + if (batch->recv_initial_metadata) { + GRPC_CALL_COMBINER_START( + exec_ctx, call_combiner, + batch->payload->recv_initial_metadata.recv_initial_metadata_ready, + GRPC_ERROR_REF(error), "failing recv_initial_metadata_ready"); + } + GRPC_CLOSURE_SCHED(exec_ctx, batch->on_complete, error); + if (batch->cancel_stream) { + GRPC_ERROR_UNREF(batch->payload->cancel_stream.cancel_error); + } +} + +typedef struct { + grpc_closure outer_on_complete; + grpc_closure *inner_on_complete; + grpc_transport_op op; +} made_transport_op; + +static void destroy_made_transport_op(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + made_transport_op *op = (made_transport_op *)arg; + GRPC_CLOSURE_SCHED(exec_ctx, op->inner_on_complete, GRPC_ERROR_REF(error)); + gpr_free(op); +} + +grpc_transport_op *grpc_make_transport_op(grpc_closure *on_complete) { + made_transport_op *op = (made_transport_op *)gpr_malloc(sizeof(*op)); + GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_op, op, + grpc_schedule_on_exec_ctx); + op->inner_on_complete = on_complete; + memset(&op->op, 0, sizeof(op->op)); + op->op.on_consumed = &op->outer_on_complete; + return &op->op; +} + +typedef struct { + grpc_closure outer_on_complete; + grpc_closure *inner_on_complete; + grpc_transport_stream_op_batch op; + grpc_transport_stream_op_batch_payload payload; +} made_transport_stream_op; + +static void destroy_made_transport_stream_op(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + made_transport_stream_op *op = (made_transport_stream_op *)arg; + grpc_closure *c = op->inner_on_complete; + gpr_free(op); + GRPC_CLOSURE_RUN(exec_ctx, c, GRPC_ERROR_REF(error)); +} + +grpc_transport_stream_op_batch *grpc_make_transport_stream_op( + grpc_closure *on_complete) { + made_transport_stream_op *op = + (made_transport_stream_op *)gpr_zalloc(sizeof(*op)); + op->op.payload = &op->payload; + GRPC_CLOSURE_INIT(&op->outer_on_complete, destroy_made_transport_stream_op, + op, grpc_schedule_on_exec_ctx); + op->inner_on_complete = on_complete; + op->op.on_complete = &op->outer_on_complete; + return &op->op; +} diff --git a/src/core/lib/transport/transport_op_string.c b/src/core/lib/transport/transport_op_string.c deleted file mode 100644 index 858664715c..0000000000 --- a/src/core/lib/transport/transport_op_string.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/lib/channel/channel_stack.h" - -#include -#include -#include - -#include -#include -#include -#include "src/core/lib/slice/slice_string_helpers.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/transport/connectivity_state.h" - -/* These routines are here to facilitate debugging - they produce string - representations of various transport data structures */ - -static void put_metadata(gpr_strvec *b, grpc_mdelem md) { - gpr_strvec_add(b, gpr_strdup("key=")); - gpr_strvec_add( - b, grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_HEX | GPR_DUMP_ASCII)); - - gpr_strvec_add(b, gpr_strdup(" value=")); - gpr_strvec_add( - b, grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII)); -} - -static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) { - grpc_linked_mdelem *m; - for (m = md.list.head; m != NULL; m = m->next) { - if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", ")); - put_metadata(b, m->md); - } - if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) { - char *tmp; - gpr_asprintf(&tmp, " deadline=%" PRId64 ".%09d", md.deadline.tv_sec, - md.deadline.tv_nsec); - gpr_strvec_add(b, tmp); - } -} - -char *grpc_transport_stream_op_batch_string( - grpc_transport_stream_op_batch *op) { - char *tmp; - char *out; - - gpr_strvec b; - gpr_strvec_init(&b); - - if (op->send_initial_metadata) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{")); - put_metadata_list( - &b, *op->payload->send_initial_metadata.send_initial_metadata); - gpr_strvec_add(&b, gpr_strdup("}")); - } - - if (op->send_message) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", - op->payload->send_message.send_message->flags, - op->payload->send_message.send_message->length); - gpr_strvec_add(&b, tmp); - } - - if (op->send_trailing_metadata) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{")); - put_metadata_list( - &b, *op->payload->send_trailing_metadata.send_trailing_metadata); - gpr_strvec_add(&b, gpr_strdup("}")); - } - - if (op->recv_initial_metadata) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA")); - } - - if (op->recv_message) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE")); - } - - if (op->recv_trailing_metadata) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA")); - } - - if (op->cancel_stream) { - gpr_strvec_add(&b, gpr_strdup(" ")); - const char *msg = - grpc_error_string(op->payload->cancel_stream.cancel_error); - gpr_asprintf(&tmp, "CANCEL:%s", msg); - - gpr_strvec_add(&b, tmp); - } - - if (op->collect_stats) { - gpr_strvec_add(&b, gpr_strdup(" ")); - gpr_asprintf(&tmp, "COLLECT_STATS:%p", - op->payload->collect_stats.collect_stats); - gpr_strvec_add(&b, tmp); - } - - out = gpr_strvec_flatten(&b, NULL); - gpr_strvec_destroy(&b); - - return out; -} - -char *grpc_transport_op_string(grpc_transport_op *op) { - char *tmp; - char *out; - bool first = true; - - gpr_strvec b; - gpr_strvec_init(&b); - - if (op->on_connectivity_state_change != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - if (op->connectivity_state != NULL) { - gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:from=%s", - op->on_connectivity_state_change, - grpc_connectivity_state_name(*op->connectivity_state)); - gpr_strvec_add(&b, tmp); - } else { - gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:unsubscribe", - op->on_connectivity_state_change); - gpr_strvec_add(&b, tmp); - } - } - - if (op->disconnect_with_error != GRPC_ERROR_NONE) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - const char *err = grpc_error_string(op->disconnect_with_error); - gpr_asprintf(&tmp, "DISCONNECT:%s", err); - gpr_strvec_add(&b, tmp); - } - - if (op->goaway_error) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - const char *msg = grpc_error_string(op->goaway_error); - gpr_asprintf(&tmp, "SEND_GOAWAY:%s", msg); - - gpr_strvec_add(&b, tmp); - } - - if (op->set_accept_stream) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - gpr_asprintf(&tmp, "SET_ACCEPT_STREAM:%p(%p,...)", op->set_accept_stream_fn, - op->set_accept_stream_user_data); - gpr_strvec_add(&b, tmp); - } - - if (op->bind_pollset != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET")); - } - - if (op->bind_pollset_set != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET_SET")); - } - - if (op->send_ping != NULL) { - if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); - first = false; - gpr_strvec_add(&b, gpr_strdup("SEND_PING")); - } - - out = gpr_strvec_flatten(&b, NULL); - gpr_strvec_destroy(&b); - - return out; -} - -void grpc_call_log_op(const char *file, int line, gpr_log_severity severity, - grpc_call_element *elem, - grpc_transport_stream_op_batch *op) { - char *str = grpc_transport_stream_op_batch_string(op); - gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str); - gpr_free(str); -} diff --git a/src/core/lib/transport/transport_op_string.cc b/src/core/lib/transport/transport_op_string.cc new file mode 100644 index 0000000000..858664715c --- /dev/null +++ b/src/core/lib/transport/transport_op_string.cc @@ -0,0 +1,206 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/lib/channel/channel_stack.h" + +#include +#include +#include + +#include +#include +#include +#include "src/core/lib/slice/slice_string_helpers.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/transport/connectivity_state.h" + +/* These routines are here to facilitate debugging - they produce string + representations of various transport data structures */ + +static void put_metadata(gpr_strvec *b, grpc_mdelem md) { + gpr_strvec_add(b, gpr_strdup("key=")); + gpr_strvec_add( + b, grpc_dump_slice(GRPC_MDKEY(md), GPR_DUMP_HEX | GPR_DUMP_ASCII)); + + gpr_strvec_add(b, gpr_strdup(" value=")); + gpr_strvec_add( + b, grpc_dump_slice(GRPC_MDVALUE(md), GPR_DUMP_HEX | GPR_DUMP_ASCII)); +} + +static void put_metadata_list(gpr_strvec *b, grpc_metadata_batch md) { + grpc_linked_mdelem *m; + for (m = md.list.head; m != NULL; m = m->next) { + if (m != md.list.head) gpr_strvec_add(b, gpr_strdup(", ")); + put_metadata(b, m->md); + } + if (gpr_time_cmp(md.deadline, gpr_inf_future(md.deadline.clock_type)) != 0) { + char *tmp; + gpr_asprintf(&tmp, " deadline=%" PRId64 ".%09d", md.deadline.tv_sec, + md.deadline.tv_nsec); + gpr_strvec_add(b, tmp); + } +} + +char *grpc_transport_stream_op_batch_string( + grpc_transport_stream_op_batch *op) { + char *tmp; + char *out; + + gpr_strvec b; + gpr_strvec_init(&b); + + if (op->send_initial_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("SEND_INITIAL_METADATA{")); + put_metadata_list( + &b, *op->payload->send_initial_metadata.send_initial_metadata); + gpr_strvec_add(&b, gpr_strdup("}")); + } + + if (op->send_message) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_asprintf(&tmp, "SEND_MESSAGE:flags=0x%08x:len=%d", + op->payload->send_message.send_message->flags, + op->payload->send_message.send_message->length); + gpr_strvec_add(&b, tmp); + } + + if (op->send_trailing_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("SEND_TRAILING_METADATA{")); + put_metadata_list( + &b, *op->payload->send_trailing_metadata.send_trailing_metadata); + gpr_strvec_add(&b, gpr_strdup("}")); + } + + if (op->recv_initial_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("RECV_INITIAL_METADATA")); + } + + if (op->recv_message) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("RECV_MESSAGE")); + } + + if (op->recv_trailing_metadata) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_strvec_add(&b, gpr_strdup("RECV_TRAILING_METADATA")); + } + + if (op->cancel_stream) { + gpr_strvec_add(&b, gpr_strdup(" ")); + const char *msg = + grpc_error_string(op->payload->cancel_stream.cancel_error); + gpr_asprintf(&tmp, "CANCEL:%s", msg); + + gpr_strvec_add(&b, tmp); + } + + if (op->collect_stats) { + gpr_strvec_add(&b, gpr_strdup(" ")); + gpr_asprintf(&tmp, "COLLECT_STATS:%p", + op->payload->collect_stats.collect_stats); + gpr_strvec_add(&b, tmp); + } + + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); + + return out; +} + +char *grpc_transport_op_string(grpc_transport_op *op) { + char *tmp; + char *out; + bool first = true; + + gpr_strvec b; + gpr_strvec_init(&b); + + if (op->on_connectivity_state_change != NULL) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + if (op->connectivity_state != NULL) { + gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:from=%s", + op->on_connectivity_state_change, + grpc_connectivity_state_name(*op->connectivity_state)); + gpr_strvec_add(&b, tmp); + } else { + gpr_asprintf(&tmp, "ON_CONNECTIVITY_STATE_CHANGE:p=%p:unsubscribe", + op->on_connectivity_state_change); + gpr_strvec_add(&b, tmp); + } + } + + if (op->disconnect_with_error != GRPC_ERROR_NONE) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + const char *err = grpc_error_string(op->disconnect_with_error); + gpr_asprintf(&tmp, "DISCONNECT:%s", err); + gpr_strvec_add(&b, tmp); + } + + if (op->goaway_error) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + const char *msg = grpc_error_string(op->goaway_error); + gpr_asprintf(&tmp, "SEND_GOAWAY:%s", msg); + + gpr_strvec_add(&b, tmp); + } + + if (op->set_accept_stream) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_asprintf(&tmp, "SET_ACCEPT_STREAM:%p(%p,...)", op->set_accept_stream_fn, + op->set_accept_stream_user_data); + gpr_strvec_add(&b, tmp); + } + + if (op->bind_pollset != NULL) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET")); + } + + if (op->bind_pollset_set != NULL) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_strvec_add(&b, gpr_strdup("BIND_POLLSET_SET")); + } + + if (op->send_ping != NULL) { + if (!first) gpr_strvec_add(&b, gpr_strdup(" ")); + first = false; + gpr_strvec_add(&b, gpr_strdup("SEND_PING")); + } + + out = gpr_strvec_flatten(&b, NULL); + gpr_strvec_destroy(&b); + + return out; +} + +void grpc_call_log_op(const char *file, int line, gpr_log_severity severity, + grpc_call_element *elem, + grpc_transport_stream_op_batch *op) { + char *str = grpc_transport_stream_op_batch_string(op); + gpr_log(file, line, severity, "OP[%s:%p]: %s", elem->filter->name, elem, str); + gpr_free(str); +} diff --git a/src/core/tsi/fake_transport_security.c b/src/core/tsi/fake_transport_security.c deleted file mode 100644 index 0a992b5fd2..0000000000 --- a/src/core/tsi/fake_transport_security.c +++ /dev/null @@ -1,770 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/tsi/fake_transport_security.h" - -#include -#include - -#include -#include -#include -#include -#include "src/core/lib/slice/slice_internal.h" -#include "src/core/tsi/transport_security_grpc.h" - -/* --- Constants. ---*/ -#define TSI_FAKE_FRAME_HEADER_SIZE 4 -#define TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE 64 -#define TSI_FAKE_DEFAULT_FRAME_SIZE 16384 -#define TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE 256 - -/* --- Structure definitions. ---*/ - -/* a frame is encoded like this: - | size | data | - where the size field value is the size of the size field plus the size of - the data encoded in little endian on 4 bytes. */ -typedef struct { - unsigned char *data; - size_t size; - size_t allocated_size; - size_t offset; - int needs_draining; -} tsi_fake_frame; - -typedef enum { - TSI_FAKE_CLIENT_INIT = 0, - TSI_FAKE_SERVER_INIT = 1, - TSI_FAKE_CLIENT_FINISHED = 2, - TSI_FAKE_SERVER_FINISHED = 3, - TSI_FAKE_HANDSHAKE_MESSAGE_MAX = 4 -} tsi_fake_handshake_message; - -typedef struct { - tsi_handshaker base; - int is_client; - tsi_fake_handshake_message next_message_to_send; - int needs_incoming_message; - tsi_fake_frame incoming_frame; - tsi_fake_frame outgoing_frame; - unsigned char *outgoing_bytes_buffer; - size_t outgoing_bytes_buffer_size; - tsi_result result; -} tsi_fake_handshaker; - -typedef struct { - tsi_frame_protector base; - tsi_fake_frame protect_frame; - tsi_fake_frame unprotect_frame; - size_t max_frame_size; -} tsi_fake_frame_protector; - -typedef struct { - tsi_zero_copy_grpc_protector base; - grpc_slice_buffer header_sb; - grpc_slice_buffer protected_sb; - size_t max_frame_size; - size_t parsed_frame_size; -} tsi_fake_zero_copy_grpc_protector; - -/* --- Utils. ---*/ - -static const char *tsi_fake_handshake_message_strings[] = { - "CLIENT_INIT", "SERVER_INIT", "CLIENT_FINISHED", "SERVER_FINISHED"}; - -static const char *tsi_fake_handshake_message_to_string(int msg) { - if (msg < 0 || msg >= TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { - gpr_log(GPR_ERROR, "Invalid message %d", msg); - return "UNKNOWN"; - } - return tsi_fake_handshake_message_strings[msg]; -} - -static tsi_result tsi_fake_handshake_message_from_string( - const char *msg_string, tsi_fake_handshake_message *msg) { - int i; - for (i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) { - if (strncmp(msg_string, tsi_fake_handshake_message_strings[i], - strlen(tsi_fake_handshake_message_strings[i])) == 0) { - *msg = (tsi_fake_handshake_message)i; - return TSI_OK; - } - } - gpr_log(GPR_ERROR, "Invalid handshake message."); - return TSI_DATA_CORRUPTED; -} - -static uint32_t load32_little_endian(const unsigned char *buf) { - return ((uint32_t)(buf[0]) | (uint32_t)(buf[1] << 8) | - (uint32_t)(buf[2] << 16) | (uint32_t)(buf[3] << 24)); -} - -static void store32_little_endian(uint32_t value, unsigned char *buf) { - buf[3] = (unsigned char)((value >> 24) & 0xFF); - buf[2] = (unsigned char)((value >> 16) & 0xFF); - buf[1] = (unsigned char)((value >> 8) & 0xFF); - buf[0] = (unsigned char)((value)&0xFF); -} - -static uint32_t read_frame_size(const grpc_slice_buffer *sb) { - GPR_ASSERT(sb != NULL && sb->length >= TSI_FAKE_FRAME_HEADER_SIZE); - uint8_t frame_size_buffer[TSI_FAKE_FRAME_HEADER_SIZE]; - uint8_t *buf = frame_size_buffer; - /* Copies the first 4 bytes to a temporary buffer. */ - size_t remaining = TSI_FAKE_FRAME_HEADER_SIZE; - for (size_t i = 0; i < sb->count; i++) { - size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]); - if (remaining <= slice_length) { - memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining); - remaining = 0; - break; - } else { - memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length); - buf += slice_length; - remaining -= slice_length; - } - } - GPR_ASSERT(remaining == 0); - return load32_little_endian(frame_size_buffer); -} - -static void tsi_fake_frame_reset(tsi_fake_frame *frame, int needs_draining) { - frame->offset = 0; - frame->needs_draining = needs_draining; - if (!needs_draining) frame->size = 0; -} - -/* Checks if the frame's allocated size is at least frame->size, and reallocs - * more memory if necessary. */ -static void tsi_fake_frame_ensure_size(tsi_fake_frame *frame) { - if (frame->data == NULL) { - frame->allocated_size = frame->size; - frame->data = (unsigned char *)gpr_malloc(frame->allocated_size); - } else if (frame->size > frame->allocated_size) { - unsigned char *new_data = - (unsigned char *)gpr_realloc(frame->data, frame->size); - frame->data = new_data; - frame->allocated_size = frame->size; - } -} - -/* Decodes the serialized fake frame contained in incoming_bytes, and fills - * frame with the contents of the decoded frame. - * This method should not be called if frame->needs_framing is not 0. */ -static tsi_result tsi_fake_frame_decode(const unsigned char *incoming_bytes, - size_t *incoming_bytes_size, - tsi_fake_frame *frame) { - size_t available_size = *incoming_bytes_size; - size_t to_read_size = 0; - const unsigned char *bytes_cursor = incoming_bytes; - - if (frame->needs_draining) return TSI_INTERNAL_ERROR; - if (frame->data == NULL) { - frame->allocated_size = TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE; - frame->data = (unsigned char *)gpr_malloc(frame->allocated_size); - } - - if (frame->offset < TSI_FAKE_FRAME_HEADER_SIZE) { - to_read_size = TSI_FAKE_FRAME_HEADER_SIZE - frame->offset; - if (to_read_size > available_size) { - /* Just fill what we can and exit. */ - memcpy(frame->data + frame->offset, bytes_cursor, available_size); - bytes_cursor += available_size; - frame->offset += available_size; - *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); - return TSI_INCOMPLETE_DATA; - } - memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); - bytes_cursor += to_read_size; - frame->offset += to_read_size; - available_size -= to_read_size; - frame->size = load32_little_endian(frame->data); - tsi_fake_frame_ensure_size(frame); - } - - to_read_size = frame->size - frame->offset; - if (to_read_size > available_size) { - memcpy(frame->data + frame->offset, bytes_cursor, available_size); - frame->offset += available_size; - bytes_cursor += available_size; - *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); - return TSI_INCOMPLETE_DATA; - } - memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); - bytes_cursor += to_read_size; - *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); - tsi_fake_frame_reset(frame, 1 /* needs_draining */); - return TSI_OK; -} - -/* Encodes a fake frame into its wire format and places the result in - * outgoing_bytes. outgoing_bytes_size indicates the size of the encoded frame. - * This method should not be called if frame->needs_framing is 0. */ -static tsi_result tsi_fake_frame_encode(unsigned char *outgoing_bytes, - size_t *outgoing_bytes_size, - tsi_fake_frame *frame) { - size_t to_write_size = frame->size - frame->offset; - if (!frame->needs_draining) return TSI_INTERNAL_ERROR; - if (*outgoing_bytes_size < to_write_size) { - memcpy(outgoing_bytes, frame->data + frame->offset, *outgoing_bytes_size); - frame->offset += *outgoing_bytes_size; - return TSI_INCOMPLETE_DATA; - } - memcpy(outgoing_bytes, frame->data + frame->offset, to_write_size); - *outgoing_bytes_size = to_write_size; - tsi_fake_frame_reset(frame, 0 /* needs_draining */); - return TSI_OK; -} - -/* Sets the payload of a fake frame to contain the given data blob, where - * data_size indicates the size of data. */ -static tsi_result tsi_fake_frame_set_data(unsigned char *data, size_t data_size, - tsi_fake_frame *frame) { - frame->offset = 0; - frame->size = data_size + TSI_FAKE_FRAME_HEADER_SIZE; - tsi_fake_frame_ensure_size(frame); - store32_little_endian((uint32_t)frame->size, frame->data); - memcpy(frame->data + TSI_FAKE_FRAME_HEADER_SIZE, data, data_size); - tsi_fake_frame_reset(frame, 1 /* needs draining */); - return TSI_OK; -} - -/* Destroys the contents of a fake frame. */ -static void tsi_fake_frame_destruct(tsi_fake_frame *frame) { - if (frame->data != NULL) gpr_free(frame->data); -} - -/* --- tsi_frame_protector methods implementation. ---*/ - -static tsi_result fake_protector_protect(tsi_frame_protector *self, - const unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size, - unsigned char *protected_output_frames, - size_t *protected_output_frames_size) { - tsi_result result = TSI_OK; - tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; - unsigned char frame_header[TSI_FAKE_FRAME_HEADER_SIZE]; - tsi_fake_frame *frame = &impl->protect_frame; - size_t saved_output_size = *protected_output_frames_size; - size_t drained_size = 0; - size_t *num_bytes_written = protected_output_frames_size; - *num_bytes_written = 0; - - /* Try to drain first. */ - if (frame->needs_draining) { - drained_size = saved_output_size - *num_bytes_written; - result = - tsi_fake_frame_encode(protected_output_frames, &drained_size, frame); - *num_bytes_written += drained_size; - protected_output_frames += drained_size; - if (result != TSI_OK) { - if (result == TSI_INCOMPLETE_DATA) { - *unprotected_bytes_size = 0; - result = TSI_OK; - } - return result; - } - } - - /* Now process the unprotected_bytes. */ - if (frame->needs_draining) return TSI_INTERNAL_ERROR; - if (frame->size == 0) { - /* New frame, create a header. */ - size_t written_in_frame_size = 0; - store32_little_endian((uint32_t)impl->max_frame_size, frame_header); - written_in_frame_size = TSI_FAKE_FRAME_HEADER_SIZE; - result = tsi_fake_frame_decode(frame_header, &written_in_frame_size, frame); - if (result != TSI_INCOMPLETE_DATA) { - gpr_log(GPR_ERROR, "tsi_fake_frame_decode returned %s", - tsi_result_to_string(result)); - return result; - } - } - result = - tsi_fake_frame_decode(unprotected_bytes, unprotected_bytes_size, frame); - if (result != TSI_OK) { - if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; - return result; - } - - /* Try to drain again. */ - if (!frame->needs_draining) return TSI_INTERNAL_ERROR; - if (frame->offset != 0) return TSI_INTERNAL_ERROR; - drained_size = saved_output_size - *num_bytes_written; - result = tsi_fake_frame_encode(protected_output_frames, &drained_size, frame); - *num_bytes_written += drained_size; - if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; - return result; -} - -static tsi_result fake_protector_protect_flush( - tsi_frame_protector *self, unsigned char *protected_output_frames, - size_t *protected_output_frames_size, size_t *still_pending_size) { - tsi_result result = TSI_OK; - tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; - tsi_fake_frame *frame = &impl->protect_frame; - if (!frame->needs_draining) { - /* Create a short frame. */ - frame->size = frame->offset; - frame->offset = 0; - frame->needs_draining = 1; - store32_little_endian((uint32_t)frame->size, - frame->data); /* Overwrite header. */ - } - result = tsi_fake_frame_encode(protected_output_frames, - protected_output_frames_size, frame); - if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; - *still_pending_size = frame->size - frame->offset; - return result; -} - -static tsi_result fake_protector_unprotect( - tsi_frame_protector *self, const unsigned char *protected_frames_bytes, - size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size) { - tsi_result result = TSI_OK; - tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; - tsi_fake_frame *frame = &impl->unprotect_frame; - size_t saved_output_size = *unprotected_bytes_size; - size_t drained_size = 0; - size_t *num_bytes_written = unprotected_bytes_size; - *num_bytes_written = 0; - - /* Try to drain first. */ - if (frame->needs_draining) { - /* Go past the header if needed. */ - if (frame->offset == 0) frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; - drained_size = saved_output_size - *num_bytes_written; - result = tsi_fake_frame_encode(unprotected_bytes, &drained_size, frame); - unprotected_bytes += drained_size; - *num_bytes_written += drained_size; - if (result != TSI_OK) { - if (result == TSI_INCOMPLETE_DATA) { - *protected_frames_bytes_size = 0; - result = TSI_OK; - } - return result; - } - } - - /* Now process the protected_bytes. */ - if (frame->needs_draining) return TSI_INTERNAL_ERROR; - result = tsi_fake_frame_decode(protected_frames_bytes, - protected_frames_bytes_size, frame); - if (result != TSI_OK) { - if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; - return result; - } - - /* Try to drain again. */ - if (!frame->needs_draining) return TSI_INTERNAL_ERROR; - if (frame->offset != 0) return TSI_INTERNAL_ERROR; - frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; /* Go past the header. */ - drained_size = saved_output_size - *num_bytes_written; - result = tsi_fake_frame_encode(unprotected_bytes, &drained_size, frame); - *num_bytes_written += drained_size; - if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; - return result; -} - -static void fake_protector_destroy(tsi_frame_protector *self) { - tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; - tsi_fake_frame_destruct(&impl->protect_frame); - tsi_fake_frame_destruct(&impl->unprotect_frame); - gpr_free(self); -} - -static const tsi_frame_protector_vtable frame_protector_vtable = { - fake_protector_protect, fake_protector_protect_flush, - fake_protector_unprotect, fake_protector_destroy, -}; - -/* --- tsi_zero_copy_grpc_protector methods implementation. ---*/ - -static tsi_result fake_zero_copy_grpc_protector_protect( - grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, - grpc_slice_buffer *unprotected_slices, - grpc_slice_buffer *protected_slices) { - if (self == NULL || unprotected_slices == NULL || protected_slices == NULL) { - return TSI_INVALID_ARGUMENT; - } - tsi_fake_zero_copy_grpc_protector *impl = - (tsi_fake_zero_copy_grpc_protector *)self; - /* Protects each frame. */ - while (unprotected_slices->length > 0) { - size_t frame_length = - GPR_MIN(impl->max_frame_size, - unprotected_slices->length + TSI_FAKE_FRAME_HEADER_SIZE); - grpc_slice slice = GRPC_SLICE_MALLOC(TSI_FAKE_FRAME_HEADER_SIZE); - store32_little_endian((uint32_t)frame_length, GRPC_SLICE_START_PTR(slice)); - grpc_slice_buffer_add(protected_slices, slice); - size_t data_length = frame_length - TSI_FAKE_FRAME_HEADER_SIZE; - grpc_slice_buffer_move_first(unprotected_slices, data_length, - protected_slices); - } - return TSI_OK; -} - -static tsi_result fake_zero_copy_grpc_protector_unprotect( - grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, - grpc_slice_buffer *protected_slices, - grpc_slice_buffer *unprotected_slices) { - if (self == NULL || unprotected_slices == NULL || protected_slices == NULL) { - return TSI_INVALID_ARGUMENT; - } - tsi_fake_zero_copy_grpc_protector *impl = - (tsi_fake_zero_copy_grpc_protector *)self; - grpc_slice_buffer_move_into(protected_slices, &impl->protected_sb); - /* Unprotect each frame, if we get a full frame. */ - while (impl->protected_sb.length >= TSI_FAKE_FRAME_HEADER_SIZE) { - if (impl->parsed_frame_size == 0) { - impl->parsed_frame_size = read_frame_size(&impl->protected_sb); - if (impl->parsed_frame_size <= 4) { - gpr_log(GPR_ERROR, "Invalid frame size."); - return TSI_DATA_CORRUPTED; - } - } - /* If we do not have a full frame, return with OK status. */ - if (impl->protected_sb.length < impl->parsed_frame_size) break; - /* Strips header bytes. */ - grpc_slice_buffer_move_first(&impl->protected_sb, - TSI_FAKE_FRAME_HEADER_SIZE, &impl->header_sb); - /* Moves data to unprotected slices. */ - grpc_slice_buffer_move_first( - &impl->protected_sb, - impl->parsed_frame_size - TSI_FAKE_FRAME_HEADER_SIZE, - unprotected_slices); - impl->parsed_frame_size = 0; - grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &impl->header_sb); - } - return TSI_OK; -} - -static void fake_zero_copy_grpc_protector_destroy( - grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self) { - if (self == NULL) return; - tsi_fake_zero_copy_grpc_protector *impl = - (tsi_fake_zero_copy_grpc_protector *)self; - grpc_slice_buffer_destroy_internal(exec_ctx, &impl->header_sb); - grpc_slice_buffer_destroy_internal(exec_ctx, &impl->protected_sb); - gpr_free(impl); -} - -static const tsi_zero_copy_grpc_protector_vtable - zero_copy_grpc_protector_vtable = { - fake_zero_copy_grpc_protector_protect, - fake_zero_copy_grpc_protector_unprotect, - fake_zero_copy_grpc_protector_destroy, -}; - -/* --- tsi_handshaker_result methods implementation. ---*/ - -typedef struct { - tsi_handshaker_result base; - unsigned char *unused_bytes; - size_t unused_bytes_size; -} fake_handshaker_result; - -static tsi_result fake_handshaker_result_extract_peer( - const tsi_handshaker_result *self, tsi_peer *peer) { - /* Construct a tsi_peer with 1 property: certificate type. */ - tsi_result result = tsi_construct_peer(1, peer); - if (result != TSI_OK) return result; - result = tsi_construct_string_peer_property_from_cstring( - TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_FAKE_CERTIFICATE_TYPE, - &peer->properties[0]); - if (result != TSI_OK) tsi_peer_destruct(peer); - return result; -} - -static tsi_result fake_handshaker_result_create_zero_copy_grpc_protector( - void *exec_ctx, const tsi_handshaker_result *self, - size_t *max_output_protected_frame_size, - tsi_zero_copy_grpc_protector **protector) { - *protector = - tsi_create_fake_zero_copy_grpc_protector(max_output_protected_frame_size); - return TSI_OK; -} - -static tsi_result fake_handshaker_result_create_frame_protector( - const tsi_handshaker_result *self, size_t *max_output_protected_frame_size, - tsi_frame_protector **protector) { - *protector = tsi_create_fake_frame_protector(max_output_protected_frame_size); - return TSI_OK; -} - -static tsi_result fake_handshaker_result_get_unused_bytes( - const tsi_handshaker_result *self, const unsigned char **bytes, - size_t *bytes_size) { - fake_handshaker_result *result = (fake_handshaker_result *)self; - *bytes_size = result->unused_bytes_size; - *bytes = result->unused_bytes; - return TSI_OK; -} - -static void fake_handshaker_result_destroy(tsi_handshaker_result *self) { - fake_handshaker_result *result = (fake_handshaker_result *)self; - gpr_free(result->unused_bytes); - gpr_free(self); -} - -static const tsi_handshaker_result_vtable handshaker_result_vtable = { - fake_handshaker_result_extract_peer, - fake_handshaker_result_create_zero_copy_grpc_protector, - fake_handshaker_result_create_frame_protector, - fake_handshaker_result_get_unused_bytes, - fake_handshaker_result_destroy, -}; - -static tsi_result fake_handshaker_result_create( - const unsigned char *unused_bytes, size_t unused_bytes_size, - tsi_handshaker_result **handshaker_result) { - if ((unused_bytes_size > 0 && unused_bytes == NULL) || - handshaker_result == NULL) { - return TSI_INVALID_ARGUMENT; - } - fake_handshaker_result *result = - (fake_handshaker_result *)gpr_zalloc(sizeof(*result)); - result->base.vtable = &handshaker_result_vtable; - if (unused_bytes_size > 0) { - result->unused_bytes = (unsigned char *)gpr_malloc(unused_bytes_size); - memcpy(result->unused_bytes, unused_bytes, unused_bytes_size); - } - result->unused_bytes_size = unused_bytes_size; - *handshaker_result = &result->base; - return TSI_OK; -} - -/* --- tsi_handshaker methods implementation. ---*/ - -static tsi_result fake_handshaker_get_bytes_to_send_to_peer( - tsi_handshaker *self, unsigned char *bytes, size_t *bytes_size) { - tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; - tsi_result result = TSI_OK; - if (impl->needs_incoming_message || impl->result == TSI_OK) { - *bytes_size = 0; - return TSI_OK; - } - if (!impl->outgoing_frame.needs_draining) { - tsi_fake_handshake_message next_message_to_send = - (tsi_fake_handshake_message)(impl->next_message_to_send + 2); - const char *msg_string = - tsi_fake_handshake_message_to_string(impl->next_message_to_send); - result = tsi_fake_frame_set_data((unsigned char *)msg_string, - strlen(msg_string), &impl->outgoing_frame); - if (result != TSI_OK) return result; - if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { - next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX; - } - if (GRPC_TRACER_ON(tsi_tracing_enabled)) { - gpr_log(GPR_INFO, "%s prepared %s.", - impl->is_client ? "Client" : "Server", - tsi_fake_handshake_message_to_string(impl->next_message_to_send)); - } - impl->next_message_to_send = next_message_to_send; - } - result = tsi_fake_frame_encode(bytes, bytes_size, &impl->outgoing_frame); - if (result != TSI_OK) return result; - if (!impl->is_client && - impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { - /* We're done. */ - if (GRPC_TRACER_ON(tsi_tracing_enabled)) { - gpr_log(GPR_INFO, "Server is done."); - } - impl->result = TSI_OK; - } else { - impl->needs_incoming_message = 1; - } - return TSI_OK; -} - -static tsi_result fake_handshaker_process_bytes_from_peer( - tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { - tsi_result result = TSI_OK; - tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; - tsi_fake_handshake_message expected_msg = - (tsi_fake_handshake_message)(impl->next_message_to_send - 1); - tsi_fake_handshake_message received_msg; - - if (!impl->needs_incoming_message || impl->result == TSI_OK) { - *bytes_size = 0; - return TSI_OK; - } - result = tsi_fake_frame_decode(bytes, bytes_size, &impl->incoming_frame); - if (result != TSI_OK) return result; - - /* We now have a complete frame. */ - result = tsi_fake_handshake_message_from_string( - (const char *)impl->incoming_frame.data + TSI_FAKE_FRAME_HEADER_SIZE, - &received_msg); - if (result != TSI_OK) { - impl->result = result; - return result; - } - if (received_msg != expected_msg) { - gpr_log(GPR_ERROR, "Invalid received message (%s instead of %s)", - tsi_fake_handshake_message_to_string(received_msg), - tsi_fake_handshake_message_to_string(expected_msg)); - } - if (GRPC_TRACER_ON(tsi_tracing_enabled)) { - gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server", - tsi_fake_handshake_message_to_string(received_msg)); - } - tsi_fake_frame_reset(&impl->incoming_frame, 0 /* needs_draining */); - impl->needs_incoming_message = 0; - if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { - /* We're done. */ - if (GRPC_TRACER_ON(tsi_tracing_enabled)) { - gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server"); - } - impl->result = TSI_OK; - } - return TSI_OK; -} - -static tsi_result fake_handshaker_get_result(tsi_handshaker *self) { - tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; - return impl->result; -} - -static void fake_handshaker_destroy(tsi_handshaker *self) { - tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; - tsi_fake_frame_destruct(&impl->incoming_frame); - tsi_fake_frame_destruct(&impl->outgoing_frame); - gpr_free(impl->outgoing_bytes_buffer); - gpr_free(self); -} - -static tsi_result fake_handshaker_next( - tsi_handshaker *self, const unsigned char *received_bytes, - size_t received_bytes_size, const unsigned char **bytes_to_send, - size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result, - tsi_handshaker_on_next_done_cb cb, void *user_data) { - /* Sanity check the arguments. */ - if ((received_bytes_size > 0 && received_bytes == NULL) || - bytes_to_send == NULL || bytes_to_send_size == NULL || - handshaker_result == NULL) { - return TSI_INVALID_ARGUMENT; - } - tsi_fake_handshaker *handshaker = (tsi_fake_handshaker *)self; - tsi_result result = TSI_OK; - - /* Decode and process a handshake frame from the peer. */ - size_t consumed_bytes_size = received_bytes_size; - if (received_bytes_size > 0) { - result = fake_handshaker_process_bytes_from_peer(self, received_bytes, - &consumed_bytes_size); - if (result != TSI_OK) return result; - } - - /* Create a handshake message to send to the peer and encode it as a fake - * frame. */ - size_t offset = 0; - do { - size_t sent_bytes_size = handshaker->outgoing_bytes_buffer_size - offset; - result = fake_handshaker_get_bytes_to_send_to_peer( - self, handshaker->outgoing_bytes_buffer + offset, &sent_bytes_size); - offset += sent_bytes_size; - if (result == TSI_INCOMPLETE_DATA) { - handshaker->outgoing_bytes_buffer_size *= 2; - handshaker->outgoing_bytes_buffer = - (unsigned char *)gpr_realloc(handshaker->outgoing_bytes_buffer, - handshaker->outgoing_bytes_buffer_size); - } - } while (result == TSI_INCOMPLETE_DATA); - if (result != TSI_OK) return result; - *bytes_to_send = handshaker->outgoing_bytes_buffer; - *bytes_to_send_size = offset; - - /* Check if the handshake was completed. */ - if (fake_handshaker_get_result(self) == TSI_HANDSHAKE_IN_PROGRESS) { - *handshaker_result = NULL; - } else { - /* Calculate the unused bytes. */ - const unsigned char *unused_bytes = NULL; - size_t unused_bytes_size = received_bytes_size - consumed_bytes_size; - if (unused_bytes_size > 0) { - unused_bytes = received_bytes + consumed_bytes_size; - } - - /* Create a handshaker_result containing the unused bytes. */ - result = fake_handshaker_result_create(unused_bytes, unused_bytes_size, - handshaker_result); - if (result == TSI_OK) { - /* Indicate that the handshake has completed and that a handshaker_result - * has been created. */ - self->handshaker_result_created = true; - } - } - return result; -} - -static const tsi_handshaker_vtable handshaker_vtable = { - NULL, /* get_bytes_to_send_to_peer -- deprecated */ - NULL, /* process_bytes_from_peer -- deprecated */ - NULL, /* get_result -- deprecated */ - NULL, /* extract_peer -- deprecated */ - NULL, /* create_frame_protector -- deprecated */ - fake_handshaker_destroy, - fake_handshaker_next, -}; - -tsi_handshaker *tsi_create_fake_handshaker(int is_client) { - tsi_fake_handshaker *impl = (tsi_fake_handshaker *)gpr_zalloc(sizeof(*impl)); - impl->base.vtable = &handshaker_vtable; - impl->is_client = is_client; - impl->result = TSI_HANDSHAKE_IN_PROGRESS; - impl->outgoing_bytes_buffer_size = - TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE; - impl->outgoing_bytes_buffer = - (unsigned char *)gpr_malloc(impl->outgoing_bytes_buffer_size); - if (is_client) { - impl->needs_incoming_message = 0; - impl->next_message_to_send = TSI_FAKE_CLIENT_INIT; - } else { - impl->needs_incoming_message = 1; - impl->next_message_to_send = TSI_FAKE_SERVER_INIT; - } - return &impl->base; -} - -tsi_frame_protector *tsi_create_fake_frame_protector( - size_t *max_protected_frame_size) { - tsi_fake_frame_protector *impl = - (tsi_fake_frame_protector *)gpr_zalloc(sizeof(*impl)); - impl->max_frame_size = (max_protected_frame_size == NULL) - ? TSI_FAKE_DEFAULT_FRAME_SIZE - : *max_protected_frame_size; - impl->base.vtable = &frame_protector_vtable; - return &impl->base; -} - -tsi_zero_copy_grpc_protector *tsi_create_fake_zero_copy_grpc_protector( - size_t *max_protected_frame_size) { - tsi_fake_zero_copy_grpc_protector *impl = - (tsi_fake_zero_copy_grpc_protector *)gpr_zalloc(sizeof(*impl)); - grpc_slice_buffer_init(&impl->header_sb); - grpc_slice_buffer_init(&impl->protected_sb); - impl->max_frame_size = (max_protected_frame_size == NULL) - ? TSI_FAKE_DEFAULT_FRAME_SIZE - : *max_protected_frame_size; - impl->parsed_frame_size = 0; - impl->base.vtable = &zero_copy_grpc_protector_vtable; - return &impl->base; -} diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc new file mode 100644 index 0000000000..0a992b5fd2 --- /dev/null +++ b/src/core/tsi/fake_transport_security.cc @@ -0,0 +1,770 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/tsi/fake_transport_security.h" + +#include +#include + +#include +#include +#include +#include +#include "src/core/lib/slice/slice_internal.h" +#include "src/core/tsi/transport_security_grpc.h" + +/* --- Constants. ---*/ +#define TSI_FAKE_FRAME_HEADER_SIZE 4 +#define TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE 64 +#define TSI_FAKE_DEFAULT_FRAME_SIZE 16384 +#define TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE 256 + +/* --- Structure definitions. ---*/ + +/* a frame is encoded like this: + | size | data | + where the size field value is the size of the size field plus the size of + the data encoded in little endian on 4 bytes. */ +typedef struct { + unsigned char *data; + size_t size; + size_t allocated_size; + size_t offset; + int needs_draining; +} tsi_fake_frame; + +typedef enum { + TSI_FAKE_CLIENT_INIT = 0, + TSI_FAKE_SERVER_INIT = 1, + TSI_FAKE_CLIENT_FINISHED = 2, + TSI_FAKE_SERVER_FINISHED = 3, + TSI_FAKE_HANDSHAKE_MESSAGE_MAX = 4 +} tsi_fake_handshake_message; + +typedef struct { + tsi_handshaker base; + int is_client; + tsi_fake_handshake_message next_message_to_send; + int needs_incoming_message; + tsi_fake_frame incoming_frame; + tsi_fake_frame outgoing_frame; + unsigned char *outgoing_bytes_buffer; + size_t outgoing_bytes_buffer_size; + tsi_result result; +} tsi_fake_handshaker; + +typedef struct { + tsi_frame_protector base; + tsi_fake_frame protect_frame; + tsi_fake_frame unprotect_frame; + size_t max_frame_size; +} tsi_fake_frame_protector; + +typedef struct { + tsi_zero_copy_grpc_protector base; + grpc_slice_buffer header_sb; + grpc_slice_buffer protected_sb; + size_t max_frame_size; + size_t parsed_frame_size; +} tsi_fake_zero_copy_grpc_protector; + +/* --- Utils. ---*/ + +static const char *tsi_fake_handshake_message_strings[] = { + "CLIENT_INIT", "SERVER_INIT", "CLIENT_FINISHED", "SERVER_FINISHED"}; + +static const char *tsi_fake_handshake_message_to_string(int msg) { + if (msg < 0 || msg >= TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + gpr_log(GPR_ERROR, "Invalid message %d", msg); + return "UNKNOWN"; + } + return tsi_fake_handshake_message_strings[msg]; +} + +static tsi_result tsi_fake_handshake_message_from_string( + const char *msg_string, tsi_fake_handshake_message *msg) { + int i; + for (i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) { + if (strncmp(msg_string, tsi_fake_handshake_message_strings[i], + strlen(tsi_fake_handshake_message_strings[i])) == 0) { + *msg = (tsi_fake_handshake_message)i; + return TSI_OK; + } + } + gpr_log(GPR_ERROR, "Invalid handshake message."); + return TSI_DATA_CORRUPTED; +} + +static uint32_t load32_little_endian(const unsigned char *buf) { + return ((uint32_t)(buf[0]) | (uint32_t)(buf[1] << 8) | + (uint32_t)(buf[2] << 16) | (uint32_t)(buf[3] << 24)); +} + +static void store32_little_endian(uint32_t value, unsigned char *buf) { + buf[3] = (unsigned char)((value >> 24) & 0xFF); + buf[2] = (unsigned char)((value >> 16) & 0xFF); + buf[1] = (unsigned char)((value >> 8) & 0xFF); + buf[0] = (unsigned char)((value)&0xFF); +} + +static uint32_t read_frame_size(const grpc_slice_buffer *sb) { + GPR_ASSERT(sb != NULL && sb->length >= TSI_FAKE_FRAME_HEADER_SIZE); + uint8_t frame_size_buffer[TSI_FAKE_FRAME_HEADER_SIZE]; + uint8_t *buf = frame_size_buffer; + /* Copies the first 4 bytes to a temporary buffer. */ + size_t remaining = TSI_FAKE_FRAME_HEADER_SIZE; + for (size_t i = 0; i < sb->count; i++) { + size_t slice_length = GRPC_SLICE_LENGTH(sb->slices[i]); + if (remaining <= slice_length) { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), remaining); + remaining = 0; + break; + } else { + memcpy(buf, GRPC_SLICE_START_PTR(sb->slices[i]), slice_length); + buf += slice_length; + remaining -= slice_length; + } + } + GPR_ASSERT(remaining == 0); + return load32_little_endian(frame_size_buffer); +} + +static void tsi_fake_frame_reset(tsi_fake_frame *frame, int needs_draining) { + frame->offset = 0; + frame->needs_draining = needs_draining; + if (!needs_draining) frame->size = 0; +} + +/* Checks if the frame's allocated size is at least frame->size, and reallocs + * more memory if necessary. */ +static void tsi_fake_frame_ensure_size(tsi_fake_frame *frame) { + if (frame->data == NULL) { + frame->allocated_size = frame->size; + frame->data = (unsigned char *)gpr_malloc(frame->allocated_size); + } else if (frame->size > frame->allocated_size) { + unsigned char *new_data = + (unsigned char *)gpr_realloc(frame->data, frame->size); + frame->data = new_data; + frame->allocated_size = frame->size; + } +} + +/* Decodes the serialized fake frame contained in incoming_bytes, and fills + * frame with the contents of the decoded frame. + * This method should not be called if frame->needs_framing is not 0. */ +static tsi_result tsi_fake_frame_decode(const unsigned char *incoming_bytes, + size_t *incoming_bytes_size, + tsi_fake_frame *frame) { + size_t available_size = *incoming_bytes_size; + size_t to_read_size = 0; + const unsigned char *bytes_cursor = incoming_bytes; + + if (frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->data == NULL) { + frame->allocated_size = TSI_FAKE_FRAME_INITIAL_ALLOCATED_SIZE; + frame->data = (unsigned char *)gpr_malloc(frame->allocated_size); + } + + if (frame->offset < TSI_FAKE_FRAME_HEADER_SIZE) { + to_read_size = TSI_FAKE_FRAME_HEADER_SIZE - frame->offset; + if (to_read_size > available_size) { + /* Just fill what we can and exit. */ + memcpy(frame->data + frame->offset, bytes_cursor, available_size); + bytes_cursor += available_size; + frame->offset += available_size; + *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); + return TSI_INCOMPLETE_DATA; + } + memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); + bytes_cursor += to_read_size; + frame->offset += to_read_size; + available_size -= to_read_size; + frame->size = load32_little_endian(frame->data); + tsi_fake_frame_ensure_size(frame); + } + + to_read_size = frame->size - frame->offset; + if (to_read_size > available_size) { + memcpy(frame->data + frame->offset, bytes_cursor, available_size); + frame->offset += available_size; + bytes_cursor += available_size; + *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); + return TSI_INCOMPLETE_DATA; + } + memcpy(frame->data + frame->offset, bytes_cursor, to_read_size); + bytes_cursor += to_read_size; + *incoming_bytes_size = (size_t)(bytes_cursor - incoming_bytes); + tsi_fake_frame_reset(frame, 1 /* needs_draining */); + return TSI_OK; +} + +/* Encodes a fake frame into its wire format and places the result in + * outgoing_bytes. outgoing_bytes_size indicates the size of the encoded frame. + * This method should not be called if frame->needs_framing is 0. */ +static tsi_result tsi_fake_frame_encode(unsigned char *outgoing_bytes, + size_t *outgoing_bytes_size, + tsi_fake_frame *frame) { + size_t to_write_size = frame->size - frame->offset; + if (!frame->needs_draining) return TSI_INTERNAL_ERROR; + if (*outgoing_bytes_size < to_write_size) { + memcpy(outgoing_bytes, frame->data + frame->offset, *outgoing_bytes_size); + frame->offset += *outgoing_bytes_size; + return TSI_INCOMPLETE_DATA; + } + memcpy(outgoing_bytes, frame->data + frame->offset, to_write_size); + *outgoing_bytes_size = to_write_size; + tsi_fake_frame_reset(frame, 0 /* needs_draining */); + return TSI_OK; +} + +/* Sets the payload of a fake frame to contain the given data blob, where + * data_size indicates the size of data. */ +static tsi_result tsi_fake_frame_set_data(unsigned char *data, size_t data_size, + tsi_fake_frame *frame) { + frame->offset = 0; + frame->size = data_size + TSI_FAKE_FRAME_HEADER_SIZE; + tsi_fake_frame_ensure_size(frame); + store32_little_endian((uint32_t)frame->size, frame->data); + memcpy(frame->data + TSI_FAKE_FRAME_HEADER_SIZE, data, data_size); + tsi_fake_frame_reset(frame, 1 /* needs draining */); + return TSI_OK; +} + +/* Destroys the contents of a fake frame. */ +static void tsi_fake_frame_destruct(tsi_fake_frame *frame) { + if (frame->data != NULL) gpr_free(frame->data); +} + +/* --- tsi_frame_protector methods implementation. ---*/ + +static tsi_result fake_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size) { + tsi_result result = TSI_OK; + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; + unsigned char frame_header[TSI_FAKE_FRAME_HEADER_SIZE]; + tsi_fake_frame *frame = &impl->protect_frame; + size_t saved_output_size = *protected_output_frames_size; + size_t drained_size = 0; + size_t *num_bytes_written = protected_output_frames_size; + *num_bytes_written = 0; + + /* Try to drain first. */ + if (frame->needs_draining) { + drained_size = saved_output_size - *num_bytes_written; + result = + tsi_fake_frame_encode(protected_output_frames, &drained_size, frame); + *num_bytes_written += drained_size; + protected_output_frames += drained_size; + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) { + *unprotected_bytes_size = 0; + result = TSI_OK; + } + return result; + } + } + + /* Now process the unprotected_bytes. */ + if (frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->size == 0) { + /* New frame, create a header. */ + size_t written_in_frame_size = 0; + store32_little_endian((uint32_t)impl->max_frame_size, frame_header); + written_in_frame_size = TSI_FAKE_FRAME_HEADER_SIZE; + result = tsi_fake_frame_decode(frame_header, &written_in_frame_size, frame); + if (result != TSI_INCOMPLETE_DATA) { + gpr_log(GPR_ERROR, "tsi_fake_frame_decode returned %s", + tsi_result_to_string(result)); + return result; + } + } + result = + tsi_fake_frame_decode(unprotected_bytes, unprotected_bytes_size, frame); + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; + } + + /* Try to drain again. */ + if (!frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->offset != 0) return TSI_INTERNAL_ERROR; + drained_size = saved_output_size - *num_bytes_written; + result = tsi_fake_frame_encode(protected_output_frames, &drained_size, frame); + *num_bytes_written += drained_size; + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; +} + +static tsi_result fake_protector_protect_flush( + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size) { + tsi_result result = TSI_OK; + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; + tsi_fake_frame *frame = &impl->protect_frame; + if (!frame->needs_draining) { + /* Create a short frame. */ + frame->size = frame->offset; + frame->offset = 0; + frame->needs_draining = 1; + store32_little_endian((uint32_t)frame->size, + frame->data); /* Overwrite header. */ + } + result = tsi_fake_frame_encode(protected_output_frames, + protected_output_frames_size, frame); + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + *still_pending_size = frame->size - frame->offset; + return result; +} + +static tsi_result fake_protector_unprotect( + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { + tsi_result result = TSI_OK; + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; + tsi_fake_frame *frame = &impl->unprotect_frame; + size_t saved_output_size = *unprotected_bytes_size; + size_t drained_size = 0; + size_t *num_bytes_written = unprotected_bytes_size; + *num_bytes_written = 0; + + /* Try to drain first. */ + if (frame->needs_draining) { + /* Go past the header if needed. */ + if (frame->offset == 0) frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; + drained_size = saved_output_size - *num_bytes_written; + result = tsi_fake_frame_encode(unprotected_bytes, &drained_size, frame); + unprotected_bytes += drained_size; + *num_bytes_written += drained_size; + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) { + *protected_frames_bytes_size = 0; + result = TSI_OK; + } + return result; + } + } + + /* Now process the protected_bytes. */ + if (frame->needs_draining) return TSI_INTERNAL_ERROR; + result = tsi_fake_frame_decode(protected_frames_bytes, + protected_frames_bytes_size, frame); + if (result != TSI_OK) { + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; + } + + /* Try to drain again. */ + if (!frame->needs_draining) return TSI_INTERNAL_ERROR; + if (frame->offset != 0) return TSI_INTERNAL_ERROR; + frame->offset = TSI_FAKE_FRAME_HEADER_SIZE; /* Go past the header. */ + drained_size = saved_output_size - *num_bytes_written; + result = tsi_fake_frame_encode(unprotected_bytes, &drained_size, frame); + *num_bytes_written += drained_size; + if (result == TSI_INCOMPLETE_DATA) result = TSI_OK; + return result; +} + +static void fake_protector_destroy(tsi_frame_protector *self) { + tsi_fake_frame_protector *impl = (tsi_fake_frame_protector *)self; + tsi_fake_frame_destruct(&impl->protect_frame); + tsi_fake_frame_destruct(&impl->unprotect_frame); + gpr_free(self); +} + +static const tsi_frame_protector_vtable frame_protector_vtable = { + fake_protector_protect, fake_protector_protect_flush, + fake_protector_unprotect, fake_protector_destroy, +}; + +/* --- tsi_zero_copy_grpc_protector methods implementation. ---*/ + +static tsi_result fake_zero_copy_grpc_protector_protect( + grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, + grpc_slice_buffer *unprotected_slices, + grpc_slice_buffer *protected_slices) { + if (self == NULL || unprotected_slices == NULL || protected_slices == NULL) { + return TSI_INVALID_ARGUMENT; + } + tsi_fake_zero_copy_grpc_protector *impl = + (tsi_fake_zero_copy_grpc_protector *)self; + /* Protects each frame. */ + while (unprotected_slices->length > 0) { + size_t frame_length = + GPR_MIN(impl->max_frame_size, + unprotected_slices->length + TSI_FAKE_FRAME_HEADER_SIZE); + grpc_slice slice = GRPC_SLICE_MALLOC(TSI_FAKE_FRAME_HEADER_SIZE); + store32_little_endian((uint32_t)frame_length, GRPC_SLICE_START_PTR(slice)); + grpc_slice_buffer_add(protected_slices, slice); + size_t data_length = frame_length - TSI_FAKE_FRAME_HEADER_SIZE; + grpc_slice_buffer_move_first(unprotected_slices, data_length, + protected_slices); + } + return TSI_OK; +} + +static tsi_result fake_zero_copy_grpc_protector_unprotect( + grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, + grpc_slice_buffer *protected_slices, + grpc_slice_buffer *unprotected_slices) { + if (self == NULL || unprotected_slices == NULL || protected_slices == NULL) { + return TSI_INVALID_ARGUMENT; + } + tsi_fake_zero_copy_grpc_protector *impl = + (tsi_fake_zero_copy_grpc_protector *)self; + grpc_slice_buffer_move_into(protected_slices, &impl->protected_sb); + /* Unprotect each frame, if we get a full frame. */ + while (impl->protected_sb.length >= TSI_FAKE_FRAME_HEADER_SIZE) { + if (impl->parsed_frame_size == 0) { + impl->parsed_frame_size = read_frame_size(&impl->protected_sb); + if (impl->parsed_frame_size <= 4) { + gpr_log(GPR_ERROR, "Invalid frame size."); + return TSI_DATA_CORRUPTED; + } + } + /* If we do not have a full frame, return with OK status. */ + if (impl->protected_sb.length < impl->parsed_frame_size) break; + /* Strips header bytes. */ + grpc_slice_buffer_move_first(&impl->protected_sb, + TSI_FAKE_FRAME_HEADER_SIZE, &impl->header_sb); + /* Moves data to unprotected slices. */ + grpc_slice_buffer_move_first( + &impl->protected_sb, + impl->parsed_frame_size - TSI_FAKE_FRAME_HEADER_SIZE, + unprotected_slices); + impl->parsed_frame_size = 0; + grpc_slice_buffer_reset_and_unref_internal(exec_ctx, &impl->header_sb); + } + return TSI_OK; +} + +static void fake_zero_copy_grpc_protector_destroy( + grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self) { + if (self == NULL) return; + tsi_fake_zero_copy_grpc_protector *impl = + (tsi_fake_zero_copy_grpc_protector *)self; + grpc_slice_buffer_destroy_internal(exec_ctx, &impl->header_sb); + grpc_slice_buffer_destroy_internal(exec_ctx, &impl->protected_sb); + gpr_free(impl); +} + +static const tsi_zero_copy_grpc_protector_vtable + zero_copy_grpc_protector_vtable = { + fake_zero_copy_grpc_protector_protect, + fake_zero_copy_grpc_protector_unprotect, + fake_zero_copy_grpc_protector_destroy, +}; + +/* --- tsi_handshaker_result methods implementation. ---*/ + +typedef struct { + tsi_handshaker_result base; + unsigned char *unused_bytes; + size_t unused_bytes_size; +} fake_handshaker_result; + +static tsi_result fake_handshaker_result_extract_peer( + const tsi_handshaker_result *self, tsi_peer *peer) { + /* Construct a tsi_peer with 1 property: certificate type. */ + tsi_result result = tsi_construct_peer(1, peer); + if (result != TSI_OK) return result; + result = tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_FAKE_CERTIFICATE_TYPE, + &peer->properties[0]); + if (result != TSI_OK) tsi_peer_destruct(peer); + return result; +} + +static tsi_result fake_handshaker_result_create_zero_copy_grpc_protector( + void *exec_ctx, const tsi_handshaker_result *self, + size_t *max_output_protected_frame_size, + tsi_zero_copy_grpc_protector **protector) { + *protector = + tsi_create_fake_zero_copy_grpc_protector(max_output_protected_frame_size); + return TSI_OK; +} + +static tsi_result fake_handshaker_result_create_frame_protector( + const tsi_handshaker_result *self, size_t *max_output_protected_frame_size, + tsi_frame_protector **protector) { + *protector = tsi_create_fake_frame_protector(max_output_protected_frame_size); + return TSI_OK; +} + +static tsi_result fake_handshaker_result_get_unused_bytes( + const tsi_handshaker_result *self, const unsigned char **bytes, + size_t *bytes_size) { + fake_handshaker_result *result = (fake_handshaker_result *)self; + *bytes_size = result->unused_bytes_size; + *bytes = result->unused_bytes; + return TSI_OK; +} + +static void fake_handshaker_result_destroy(tsi_handshaker_result *self) { + fake_handshaker_result *result = (fake_handshaker_result *)self; + gpr_free(result->unused_bytes); + gpr_free(self); +} + +static const tsi_handshaker_result_vtable handshaker_result_vtable = { + fake_handshaker_result_extract_peer, + fake_handshaker_result_create_zero_copy_grpc_protector, + fake_handshaker_result_create_frame_protector, + fake_handshaker_result_get_unused_bytes, + fake_handshaker_result_destroy, +}; + +static tsi_result fake_handshaker_result_create( + const unsigned char *unused_bytes, size_t unused_bytes_size, + tsi_handshaker_result **handshaker_result) { + if ((unused_bytes_size > 0 && unused_bytes == NULL) || + handshaker_result == NULL) { + return TSI_INVALID_ARGUMENT; + } + fake_handshaker_result *result = + (fake_handshaker_result *)gpr_zalloc(sizeof(*result)); + result->base.vtable = &handshaker_result_vtable; + if (unused_bytes_size > 0) { + result->unused_bytes = (unsigned char *)gpr_malloc(unused_bytes_size); + memcpy(result->unused_bytes, unused_bytes, unused_bytes_size); + } + result->unused_bytes_size = unused_bytes_size; + *handshaker_result = &result->base; + return TSI_OK; +} + +/* --- tsi_handshaker methods implementation. ---*/ + +static tsi_result fake_handshaker_get_bytes_to_send_to_peer( + tsi_handshaker *self, unsigned char *bytes, size_t *bytes_size) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; + tsi_result result = TSI_OK; + if (impl->needs_incoming_message || impl->result == TSI_OK) { + *bytes_size = 0; + return TSI_OK; + } + if (!impl->outgoing_frame.needs_draining) { + tsi_fake_handshake_message next_message_to_send = + (tsi_fake_handshake_message)(impl->next_message_to_send + 2); + const char *msg_string = + tsi_fake_handshake_message_to_string(impl->next_message_to_send); + result = tsi_fake_frame_set_data((unsigned char *)msg_string, + strlen(msg_string), &impl->outgoing_frame); + if (result != TSI_OK) return result; + if (next_message_to_send > TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + next_message_to_send = TSI_FAKE_HANDSHAKE_MESSAGE_MAX; + } + if (GRPC_TRACER_ON(tsi_tracing_enabled)) { + gpr_log(GPR_INFO, "%s prepared %s.", + impl->is_client ? "Client" : "Server", + tsi_fake_handshake_message_to_string(impl->next_message_to_send)); + } + impl->next_message_to_send = next_message_to_send; + } + result = tsi_fake_frame_encode(bytes, bytes_size, &impl->outgoing_frame); + if (result != TSI_OK) return result; + if (!impl->is_client && + impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + /* We're done. */ + if (GRPC_TRACER_ON(tsi_tracing_enabled)) { + gpr_log(GPR_INFO, "Server is done."); + } + impl->result = TSI_OK; + } else { + impl->needs_incoming_message = 1; + } + return TSI_OK; +} + +static tsi_result fake_handshaker_process_bytes_from_peer( + tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { + tsi_result result = TSI_OK; + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; + tsi_fake_handshake_message expected_msg = + (tsi_fake_handshake_message)(impl->next_message_to_send - 1); + tsi_fake_handshake_message received_msg; + + if (!impl->needs_incoming_message || impl->result == TSI_OK) { + *bytes_size = 0; + return TSI_OK; + } + result = tsi_fake_frame_decode(bytes, bytes_size, &impl->incoming_frame); + if (result != TSI_OK) return result; + + /* We now have a complete frame. */ + result = tsi_fake_handshake_message_from_string( + (const char *)impl->incoming_frame.data + TSI_FAKE_FRAME_HEADER_SIZE, + &received_msg); + if (result != TSI_OK) { + impl->result = result; + return result; + } + if (received_msg != expected_msg) { + gpr_log(GPR_ERROR, "Invalid received message (%s instead of %s)", + tsi_fake_handshake_message_to_string(received_msg), + tsi_fake_handshake_message_to_string(expected_msg)); + } + if (GRPC_TRACER_ON(tsi_tracing_enabled)) { + gpr_log(GPR_INFO, "%s received %s.", impl->is_client ? "Client" : "Server", + tsi_fake_handshake_message_to_string(received_msg)); + } + tsi_fake_frame_reset(&impl->incoming_frame, 0 /* needs_draining */); + impl->needs_incoming_message = 0; + if (impl->next_message_to_send == TSI_FAKE_HANDSHAKE_MESSAGE_MAX) { + /* We're done. */ + if (GRPC_TRACER_ON(tsi_tracing_enabled)) { + gpr_log(GPR_INFO, "%s is done.", impl->is_client ? "Client" : "Server"); + } + impl->result = TSI_OK; + } + return TSI_OK; +} + +static tsi_result fake_handshaker_get_result(tsi_handshaker *self) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; + return impl->result; +} + +static void fake_handshaker_destroy(tsi_handshaker *self) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)self; + tsi_fake_frame_destruct(&impl->incoming_frame); + tsi_fake_frame_destruct(&impl->outgoing_frame); + gpr_free(impl->outgoing_bytes_buffer); + gpr_free(self); +} + +static tsi_result fake_handshaker_next( + tsi_handshaker *self, const unsigned char *received_bytes, + size_t received_bytes_size, const unsigned char **bytes_to_send, + size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result, + tsi_handshaker_on_next_done_cb cb, void *user_data) { + /* Sanity check the arguments. */ + if ((received_bytes_size > 0 && received_bytes == NULL) || + bytes_to_send == NULL || bytes_to_send_size == NULL || + handshaker_result == NULL) { + return TSI_INVALID_ARGUMENT; + } + tsi_fake_handshaker *handshaker = (tsi_fake_handshaker *)self; + tsi_result result = TSI_OK; + + /* Decode and process a handshake frame from the peer. */ + size_t consumed_bytes_size = received_bytes_size; + if (received_bytes_size > 0) { + result = fake_handshaker_process_bytes_from_peer(self, received_bytes, + &consumed_bytes_size); + if (result != TSI_OK) return result; + } + + /* Create a handshake message to send to the peer and encode it as a fake + * frame. */ + size_t offset = 0; + do { + size_t sent_bytes_size = handshaker->outgoing_bytes_buffer_size - offset; + result = fake_handshaker_get_bytes_to_send_to_peer( + self, handshaker->outgoing_bytes_buffer + offset, &sent_bytes_size); + offset += sent_bytes_size; + if (result == TSI_INCOMPLETE_DATA) { + handshaker->outgoing_bytes_buffer_size *= 2; + handshaker->outgoing_bytes_buffer = + (unsigned char *)gpr_realloc(handshaker->outgoing_bytes_buffer, + handshaker->outgoing_bytes_buffer_size); + } + } while (result == TSI_INCOMPLETE_DATA); + if (result != TSI_OK) return result; + *bytes_to_send = handshaker->outgoing_bytes_buffer; + *bytes_to_send_size = offset; + + /* Check if the handshake was completed. */ + if (fake_handshaker_get_result(self) == TSI_HANDSHAKE_IN_PROGRESS) { + *handshaker_result = NULL; + } else { + /* Calculate the unused bytes. */ + const unsigned char *unused_bytes = NULL; + size_t unused_bytes_size = received_bytes_size - consumed_bytes_size; + if (unused_bytes_size > 0) { + unused_bytes = received_bytes + consumed_bytes_size; + } + + /* Create a handshaker_result containing the unused bytes. */ + result = fake_handshaker_result_create(unused_bytes, unused_bytes_size, + handshaker_result); + if (result == TSI_OK) { + /* Indicate that the handshake has completed and that a handshaker_result + * has been created. */ + self->handshaker_result_created = true; + } + } + return result; +} + +static const tsi_handshaker_vtable handshaker_vtable = { + NULL, /* get_bytes_to_send_to_peer -- deprecated */ + NULL, /* process_bytes_from_peer -- deprecated */ + NULL, /* get_result -- deprecated */ + NULL, /* extract_peer -- deprecated */ + NULL, /* create_frame_protector -- deprecated */ + fake_handshaker_destroy, + fake_handshaker_next, +}; + +tsi_handshaker *tsi_create_fake_handshaker(int is_client) { + tsi_fake_handshaker *impl = (tsi_fake_handshaker *)gpr_zalloc(sizeof(*impl)); + impl->base.vtable = &handshaker_vtable; + impl->is_client = is_client; + impl->result = TSI_HANDSHAKE_IN_PROGRESS; + impl->outgoing_bytes_buffer_size = + TSI_FAKE_HANDSHAKER_OUTGOING_BUFFER_INITIAL_SIZE; + impl->outgoing_bytes_buffer = + (unsigned char *)gpr_malloc(impl->outgoing_bytes_buffer_size); + if (is_client) { + impl->needs_incoming_message = 0; + impl->next_message_to_send = TSI_FAKE_CLIENT_INIT; + } else { + impl->needs_incoming_message = 1; + impl->next_message_to_send = TSI_FAKE_SERVER_INIT; + } + return &impl->base; +} + +tsi_frame_protector *tsi_create_fake_frame_protector( + size_t *max_protected_frame_size) { + tsi_fake_frame_protector *impl = + (tsi_fake_frame_protector *)gpr_zalloc(sizeof(*impl)); + impl->max_frame_size = (max_protected_frame_size == NULL) + ? TSI_FAKE_DEFAULT_FRAME_SIZE + : *max_protected_frame_size; + impl->base.vtable = &frame_protector_vtable; + return &impl->base; +} + +tsi_zero_copy_grpc_protector *tsi_create_fake_zero_copy_grpc_protector( + size_t *max_protected_frame_size) { + tsi_fake_zero_copy_grpc_protector *impl = + (tsi_fake_zero_copy_grpc_protector *)gpr_zalloc(sizeof(*impl)); + grpc_slice_buffer_init(&impl->header_sb); + grpc_slice_buffer_init(&impl->protected_sb); + impl->max_frame_size = (max_protected_frame_size == NULL) + ? TSI_FAKE_DEFAULT_FRAME_SIZE + : *max_protected_frame_size; + impl->parsed_frame_size = 0; + impl->base.vtable = &zero_copy_grpc_protector_vtable; + return &impl->base; +} diff --git a/src/core/tsi/gts_transport_security.c b/src/core/tsi/gts_transport_security.c deleted file mode 100644 index e2ac685e44..0000000000 --- a/src/core/tsi/gts_transport_security.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/tsi/gts_transport_security.h" - -#include - -static gts_shared_resource g_gts_resource; - -gts_shared_resource *gts_get_shared_resource(void) { return &g_gts_resource; } - -void grpc_tsi_gts_init() { - memset(&g_gts_resource, 0, sizeof(gts_shared_resource)); - gpr_mu_init(&g_gts_resource.mu); -} - -void grpc_tsi_gts_shutdown() { - gpr_mu_destroy(&g_gts_resource.mu); - if (g_gts_resource.cq == NULL) { - return; - } - grpc_completion_queue_destroy(g_gts_resource.cq); - grpc_channel_destroy(g_gts_resource.channel); - gpr_thd_join(g_gts_resource.thread_id); -} diff --git a/src/core/tsi/gts_transport_security.cc b/src/core/tsi/gts_transport_security.cc new file mode 100644 index 0000000000..d37f3bf8f6 --- /dev/null +++ b/src/core/tsi/gts_transport_security.cc @@ -0,0 +1,40 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/tsi/gts_transport_security.h" + +#include + +static gts_shared_resource g_gts_resource; + +gts_shared_resource *gts_get_shared_resource(void) { return &g_gts_resource; } + +extern "C" void grpc_tsi_gts_init() { + memset(&g_gts_resource, 0, sizeof(gts_shared_resource)); + gpr_mu_init(&g_gts_resource.mu); +} + +extern "C" void grpc_tsi_gts_shutdown() { + gpr_mu_destroy(&g_gts_resource.mu); + if (g_gts_resource.cq == NULL) { + return; + } + grpc_completion_queue_destroy(g_gts_resource.cq); + grpc_channel_destroy(g_gts_resource.channel); + gpr_thd_join(g_gts_resource.thread_id); +} diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c deleted file mode 100644 index dd59bccdae..0000000000 --- a/src/core/tsi/ssl_transport_security.c +++ /dev/null @@ -1,1604 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/tsi/ssl_transport_security.h" - -#include - -#include -#include - -/* TODO(jboeuf): refactor inet_ntop into a portability header. */ -/* Note: for whomever reads this and tries to refactor this, this - can't be in grpc, it has to be in gpr. */ -#ifdef GPR_WINDOWS -#include -#else -#include -#include -#endif - -#include -#include -#include -#include -#include - -#include -#include /* For OPENSSL_free */ -#include -#include -#include -#include - -#include "src/core/tsi/ssl_types.h" -#include "src/core/tsi/transport_security.h" - -/* --- Constants. ---*/ - -#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384 -#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024 - -/* Putting a macro like this and littering the source file with #if is really - bad practice. - TODO(jboeuf): refactor all the #if / #endif in a separate module. */ -#ifndef TSI_OPENSSL_ALPN_SUPPORT -#define TSI_OPENSSL_ALPN_SUPPORT 1 -#endif - -/* TODO(jboeuf): I have not found a way to get this number dynamically from the - SSL structure. This is what we would ultimately want though... */ -#define TSI_SSL_MAX_PROTECTION_OVERHEAD 100 - -/* --- Structure definitions. ---*/ - -struct tsi_ssl_handshaker_factory { - const tsi_ssl_handshaker_factory_vtable *vtable; - gpr_refcount refcount; -}; - -struct tsi_ssl_client_handshaker_factory { - tsi_ssl_handshaker_factory base; - SSL_CTX *ssl_context; - unsigned char *alpn_protocol_list; - size_t alpn_protocol_list_length; -}; - -struct tsi_ssl_server_handshaker_factory { - /* Several contexts to support SNI. - The tsi_peer array contains the subject names of the server certificates - associated with the contexts at the same index. */ - tsi_ssl_handshaker_factory base; - SSL_CTX **ssl_contexts; - tsi_peer *ssl_context_x509_subject_names; - size_t ssl_context_count; - unsigned char *alpn_protocol_list; - size_t alpn_protocol_list_length; -}; - -typedef struct { - tsi_handshaker base; - SSL *ssl; - BIO *into_ssl; - BIO *from_ssl; - tsi_result result; - tsi_ssl_handshaker_factory *factory_ref; -} tsi_ssl_handshaker; - -typedef struct { - tsi_frame_protector base; - SSL *ssl; - BIO *into_ssl; - BIO *from_ssl; - unsigned char *buffer; - size_t buffer_size; - size_t buffer_offset; -} tsi_ssl_frame_protector; - -/* --- Library Initialization. ---*/ - -static gpr_once init_openssl_once = GPR_ONCE_INIT; -static gpr_mu *openssl_mutexes = NULL; - -static void openssl_locking_cb(int mode, int type, const char *file, int line) { - if (mode & CRYPTO_LOCK) { - gpr_mu_lock(&openssl_mutexes[type]); - } else { - gpr_mu_unlock(&openssl_mutexes[type]); - } -} - -static unsigned long openssl_thread_id_cb(void) { - return (unsigned long)gpr_thd_currentid(); -} - -static void init_openssl(void) { - int i; - int num_locks; - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - num_locks = CRYPTO_num_locks(); - GPR_ASSERT(num_locks > 0); - openssl_mutexes = (gpr_mu *)gpr_malloc((size_t)num_locks * sizeof(gpr_mu)); - for (i = 0; i < CRYPTO_num_locks(); i++) { - gpr_mu_init(&openssl_mutexes[i]); - } - CRYPTO_set_locking_callback(openssl_locking_cb); - CRYPTO_set_id_callback(openssl_thread_id_cb); -} - -/* --- Ssl utils. ---*/ - -static const char *ssl_error_string(int error) { - switch (error) { - case SSL_ERROR_NONE: - return "SSL_ERROR_NONE"; - case SSL_ERROR_ZERO_RETURN: - return "SSL_ERROR_ZERO_RETURN"; - case SSL_ERROR_WANT_READ: - return "SSL_ERROR_WANT_READ"; - case SSL_ERROR_WANT_WRITE: - return "SSL_ERROR_WANT_WRITE"; - case SSL_ERROR_WANT_CONNECT: - return "SSL_ERROR_WANT_CONNECT"; - case SSL_ERROR_WANT_ACCEPT: - return "SSL_ERROR_WANT_ACCEPT"; - case SSL_ERROR_WANT_X509_LOOKUP: - return "SSL_ERROR_WANT_X509_LOOKUP"; - case SSL_ERROR_SYSCALL: - return "SSL_ERROR_SYSCALL"; - case SSL_ERROR_SSL: - return "SSL_ERROR_SSL"; - default: - return "Unknown error"; - } -} - -/* TODO(jboeuf): Remove when we are past the debugging phase with this code. */ -static void ssl_log_where_info(const SSL *ssl, int where, int flag, - const char *msg) { - if ((where & flag) && GRPC_TRACER_ON(tsi_tracing_enabled)) { - gpr_log(GPR_INFO, "%20.20s - %30.30s - %5.10s", msg, - SSL_state_string_long(ssl), SSL_state_string(ssl)); - } -} - -/* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */ -static void ssl_info_callback(const SSL *ssl, int where, int ret) { - if (ret == 0) { - gpr_log(GPR_ERROR, "ssl_info_callback: error occured.\n"); - return; - } - - ssl_log_where_info(ssl, where, SSL_CB_LOOP, "LOOP"); - ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START"); - ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE"); -} - -/* Returns 1 if name looks like an IP address, 0 otherwise. - This is a very rough heuristic, and only handles IPv6 in hexadecimal form. */ -static int looks_like_ip_address(const char *name) { - size_t i; - size_t dot_count = 0; - size_t num_size = 0; - for (i = 0; i < strlen(name); i++) { - if (name[i] == ':') { - /* IPv6 Address in hexadecimal form, : is not allowed in DNS names. */ - return 1; - } - if (name[i] >= '0' && name[i] <= '9') { - if (num_size > 3) return 0; - num_size++; - } else if (name[i] == '.') { - if (dot_count > 3 || num_size == 0) return 0; - dot_count++; - num_size = 0; - } else { - return 0; - } - } - if (dot_count < 3 || num_size == 0) return 0; - return 1; -} - -/* Gets the subject CN from an X509 cert. */ -static tsi_result ssl_get_x509_common_name(X509 *cert, unsigned char **utf8, - size_t *utf8_size) { - int common_name_index = -1; - X509_NAME_ENTRY *common_name_entry = NULL; - ASN1_STRING *common_name_asn1 = NULL; - X509_NAME *subject_name = X509_get_subject_name(cert); - int utf8_returned_size = 0; - if (subject_name == NULL) { - gpr_log(GPR_ERROR, "Could not get subject name from certificate."); - return TSI_NOT_FOUND; - } - common_name_index = - X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1); - if (common_name_index == -1) { - gpr_log(GPR_ERROR, - "Could not get common name of subject from certificate."); - return TSI_NOT_FOUND; - } - common_name_entry = X509_NAME_get_entry(subject_name, common_name_index); - if (common_name_entry == NULL) { - gpr_log(GPR_ERROR, "Could not get common name entry from certificate."); - return TSI_INTERNAL_ERROR; - } - common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); - if (common_name_asn1 == NULL) { - gpr_log(GPR_ERROR, - "Could not get common name entry asn1 from certificate."); - return TSI_INTERNAL_ERROR; - } - utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1); - if (utf8_returned_size < 0) { - gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string."); - return TSI_OUT_OF_RESOURCES; - } - *utf8_size = (size_t)utf8_returned_size; - return TSI_OK; -} - -/* Gets the subject CN of an X509 cert as a tsi_peer_property. */ -static tsi_result peer_property_from_x509_common_name( - X509 *cert, tsi_peer_property *property) { - unsigned char *common_name; - size_t common_name_size; - tsi_result result = - ssl_get_x509_common_name(cert, &common_name, &common_name_size); - if (result != TSI_OK) { - if (result == TSI_NOT_FOUND) { - common_name = NULL; - common_name_size = 0; - } else { - return result; - } - } - result = tsi_construct_string_peer_property( - TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, - common_name == NULL ? "" : (const char *)common_name, common_name_size, - property); - OPENSSL_free(common_name); - return result; -} - -/* Gets the X509 cert in PEM format as a tsi_peer_property. */ -static tsi_result add_pem_certificate(X509 *cert, tsi_peer_property *property) { - BIO *bio = BIO_new(BIO_s_mem()); - if (!PEM_write_bio_X509(bio, cert)) { - BIO_free(bio); - return TSI_INTERNAL_ERROR; - } - char *contents; - long len = BIO_get_mem_data(bio, &contents); - if (len <= 0) { - BIO_free(bio); - return TSI_INTERNAL_ERROR; - } - tsi_result result = tsi_construct_string_peer_property( - TSI_X509_PEM_CERT_PROPERTY, (const char *)contents, (size_t)len, - property); - BIO_free(bio); - return result; -} - -/* Gets the subject SANs from an X509 cert as a tsi_peer_property. */ -static tsi_result add_subject_alt_names_properties_to_peer( - tsi_peer *peer, GENERAL_NAMES *subject_alt_names, - size_t subject_alt_name_count) { - size_t i; - tsi_result result = TSI_OK; - - /* Reset for DNS entries filtering. */ - peer->property_count -= subject_alt_name_count; - - for (i = 0; i < subject_alt_name_count; i++) { - GENERAL_NAME *subject_alt_name = - sk_GENERAL_NAME_value(subject_alt_names, TSI_SIZE_AS_SIZE(i)); - /* Filter out the non-dns entries names. */ - if (subject_alt_name->type == GEN_DNS) { - unsigned char *name = NULL; - int name_size; - name_size = ASN1_STRING_to_UTF8(&name, subject_alt_name->d.dNSName); - if (name_size < 0) { - gpr_log(GPR_ERROR, "Could not get utf8 from asn1 string."); - result = TSI_INTERNAL_ERROR; - break; - } - result = tsi_construct_string_peer_property( - TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, (const char *)name, - (size_t)name_size, &peer->properties[peer->property_count++]); - OPENSSL_free(name); - } else if (subject_alt_name->type == GEN_IPADD) { - char ntop_buf[INET6_ADDRSTRLEN]; - int af; - - if (subject_alt_name->d.iPAddress->length == 4) { - af = AF_INET; - } else if (subject_alt_name->d.iPAddress->length == 16) { - af = AF_INET6; - } else { - gpr_log(GPR_ERROR, "SAN IP Address contained invalid IP"); - result = TSI_INTERNAL_ERROR; - break; - } - const char *name = inet_ntop(af, subject_alt_name->d.iPAddress->data, - ntop_buf, INET6_ADDRSTRLEN); - if (name == NULL) { - gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet."); - result = TSI_INTERNAL_ERROR; - break; - } - - result = tsi_construct_string_peer_property_from_cstring( - TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, name, - &peer->properties[peer->property_count++]); - } - if (result != TSI_OK) break; - } - return result; -} - -/* Gets information about the peer's X509 cert as a tsi_peer object. */ -static tsi_result peer_from_x509(X509 *cert, int include_certificate_type, - tsi_peer *peer) { - /* TODO(jboeuf): Maybe add more properties. */ - GENERAL_NAMES *subject_alt_names = - (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); - int subject_alt_name_count = (subject_alt_names != NULL) - ? (int)sk_GENERAL_NAME_num(subject_alt_names) - : 0; - size_t property_count; - tsi_result result; - GPR_ASSERT(subject_alt_name_count >= 0); - property_count = (include_certificate_type ? (size_t)1 : 0) + - 2 /* common name, certificate */ + - (size_t)subject_alt_name_count; - result = tsi_construct_peer(property_count, peer); - if (result != TSI_OK) return result; - do { - if (include_certificate_type) { - result = tsi_construct_string_peer_property_from_cstring( - TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, - &peer->properties[0]); - if (result != TSI_OK) break; - } - result = peer_property_from_x509_common_name( - cert, &peer->properties[include_certificate_type ? 1 : 0]); - if (result != TSI_OK) break; - - result = add_pem_certificate( - cert, &peer->properties[include_certificate_type ? 2 : 1]); - if (result != TSI_OK) break; - - if (subject_alt_name_count != 0) { - result = add_subject_alt_names_properties_to_peer( - peer, subject_alt_names, (size_t)subject_alt_name_count); - if (result != TSI_OK) break; - } - } while (0); - - if (subject_alt_names != NULL) { - sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); - } - if (result != TSI_OK) tsi_peer_destruct(peer); - return result; -} - -/* Logs the SSL error stack. */ -static void log_ssl_error_stack(void) { - unsigned long err; - while ((err = ERR_get_error()) != 0) { - char details[256]; - ERR_error_string_n((uint32_t)err, details, sizeof(details)); - gpr_log(GPR_ERROR, "%s", details); - } -} - -/* Performs an SSL_read and handle errors. */ -static tsi_result do_ssl_read(SSL *ssl, unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size) { - int read_from_ssl; - GPR_ASSERT(*unprotected_bytes_size <= INT_MAX); - read_from_ssl = - SSL_read(ssl, unprotected_bytes, (int)*unprotected_bytes_size); - if (read_from_ssl <= 0) { - read_from_ssl = SSL_get_error(ssl, read_from_ssl); - switch (read_from_ssl) { - case SSL_ERROR_ZERO_RETURN: /* Received a close_notify alert. */ - case SSL_ERROR_WANT_READ: /* We need more data to finish the frame. */ - *unprotected_bytes_size = 0; - return TSI_OK; - case SSL_ERROR_WANT_WRITE: - gpr_log( - GPR_ERROR, - "Peer tried to renegotiate SSL connection. This is unsupported."); - return TSI_UNIMPLEMENTED; - case SSL_ERROR_SSL: - gpr_log(GPR_ERROR, "Corruption detected."); - log_ssl_error_stack(); - return TSI_DATA_CORRUPTED; - default: - gpr_log(GPR_ERROR, "SSL_read failed with error %s.", - ssl_error_string(read_from_ssl)); - return TSI_PROTOCOL_FAILURE; - } - } - *unprotected_bytes_size = (size_t)read_from_ssl; - return TSI_OK; -} - -/* Performs an SSL_write and handle errors. */ -static tsi_result do_ssl_write(SSL *ssl, unsigned char *unprotected_bytes, - size_t unprotected_bytes_size) { - int ssl_write_result; - GPR_ASSERT(unprotected_bytes_size <= INT_MAX); - ssl_write_result = - SSL_write(ssl, unprotected_bytes, (int)unprotected_bytes_size); - if (ssl_write_result < 0) { - ssl_write_result = SSL_get_error(ssl, ssl_write_result); - if (ssl_write_result == SSL_ERROR_WANT_READ) { - gpr_log(GPR_ERROR, - "Peer tried to renegotiate SSL connection. This is unsupported."); - return TSI_UNIMPLEMENTED; - } else { - gpr_log(GPR_ERROR, "SSL_write failed with error %s.", - ssl_error_string(ssl_write_result)); - return TSI_INTERNAL_ERROR; - } - } - return TSI_OK; -} - -/* Loads an in-memory PEM certificate chain into the SSL context. */ -static tsi_result ssl_ctx_use_certificate_chain(SSL_CTX *context, - const char *pem_cert_chain, - size_t pem_cert_chain_size) { - tsi_result result = TSI_OK; - X509 *certificate = NULL; - BIO *pem; - GPR_ASSERT(pem_cert_chain_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_cert_chain, (int)pem_cert_chain_size); - if (pem == NULL) return TSI_OUT_OF_RESOURCES; - - do { - certificate = PEM_read_bio_X509_AUX(pem, NULL, NULL, (void *)""); - if (certificate == NULL) { - result = TSI_INVALID_ARGUMENT; - break; - } - if (!SSL_CTX_use_certificate(context, certificate)) { - result = TSI_INVALID_ARGUMENT; - break; - } - while (1) { - X509 *certificate_authority = - PEM_read_bio_X509(pem, NULL, NULL, (void *)""); - if (certificate_authority == NULL) { - ERR_clear_error(); - break; /* Done reading. */ - } - if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) { - X509_free(certificate_authority); - result = TSI_INVALID_ARGUMENT; - break; - } - /* We don't need to free certificate_authority as its ownership has been - transfered to the context. That is not the case for certificate though. - */ - } - } while (0); - - if (certificate != NULL) X509_free(certificate); - BIO_free(pem); - return result; -} - -/* Loads an in-memory PEM private key into the SSL context. */ -static tsi_result ssl_ctx_use_private_key(SSL_CTX *context, const char *pem_key, - size_t pem_key_size) { - tsi_result result = TSI_OK; - EVP_PKEY *private_key = NULL; - BIO *pem; - GPR_ASSERT(pem_key_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_key, (int)pem_key_size); - if (pem == NULL) return TSI_OUT_OF_RESOURCES; - do { - private_key = PEM_read_bio_PrivateKey(pem, NULL, NULL, (void *)""); - if (private_key == NULL) { - result = TSI_INVALID_ARGUMENT; - break; - } - if (!SSL_CTX_use_PrivateKey(context, private_key)) { - result = TSI_INVALID_ARGUMENT; - break; - } - } while (0); - if (private_key != NULL) EVP_PKEY_free(private_key); - BIO_free(pem); - return result; -} - -/* Loads in-memory PEM verification certs into the SSL context and optionally - returns the verification cert names (root_names can be NULL). */ -static tsi_result ssl_ctx_load_verification_certs(SSL_CTX *context, - const char *pem_roots, - size_t pem_roots_size, - STACK_OF(X509_NAME) * - *root_names) { - tsi_result result = TSI_OK; - size_t num_roots = 0; - X509 *root = NULL; - X509_NAME *root_name = NULL; - BIO *pem; - X509_STORE *root_store; - GPR_ASSERT(pem_roots_size <= INT_MAX); - pem = BIO_new_mem_buf((void *)pem_roots, (int)pem_roots_size); - root_store = SSL_CTX_get_cert_store(context); - if (root_store == NULL) return TSI_INVALID_ARGUMENT; - if (pem == NULL) return TSI_OUT_OF_RESOURCES; - if (root_names != NULL) { - *root_names = sk_X509_NAME_new_null(); - if (*root_names == NULL) return TSI_OUT_OF_RESOURCES; - } - - while (1) { - root = PEM_read_bio_X509_AUX(pem, NULL, NULL, (void *)""); - if (root == NULL) { - ERR_clear_error(); - break; /* We're at the end of stream. */ - } - if (root_names != NULL) { - root_name = X509_get_subject_name(root); - if (root_name == NULL) { - gpr_log(GPR_ERROR, "Could not get name from root certificate."); - result = TSI_INVALID_ARGUMENT; - break; - } - root_name = X509_NAME_dup(root_name); - if (root_name == NULL) { - result = TSI_OUT_OF_RESOURCES; - break; - } - sk_X509_NAME_push(*root_names, root_name); - root_name = NULL; - } - if (!X509_STORE_add_cert(root_store, root)) { - gpr_log(GPR_ERROR, "Could not add root certificate to ssl context."); - result = TSI_INTERNAL_ERROR; - break; - } - X509_free(root); - num_roots++; - } - - if (num_roots == 0) { - gpr_log(GPR_ERROR, "Could not load any root certificate."); - result = TSI_INVALID_ARGUMENT; - } - - if (result != TSI_OK) { - if (root != NULL) X509_free(root); - if (root_names != NULL) { - sk_X509_NAME_pop_free(*root_names, X509_NAME_free); - *root_names = NULL; - if (root_name != NULL) X509_NAME_free(root_name); - } - } - BIO_free(pem); - return result; -} - -/* Populates the SSL context with a private key and a cert chain, and sets the - cipher list and the ephemeral ECDH key. */ -static tsi_result populate_ssl_context( - SSL_CTX *context, const tsi_ssl_pem_key_cert_pair *key_cert_pair, - const char *cipher_list) { - tsi_result result = TSI_OK; - if (key_cert_pair != NULL) { - if (key_cert_pair->cert_chain != NULL) { - result = ssl_ctx_use_certificate_chain(context, key_cert_pair->cert_chain, - strlen(key_cert_pair->cert_chain)); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Invalid cert chain file."); - return result; - } - } - if (key_cert_pair->private_key != NULL) { - result = ssl_ctx_use_private_key(context, key_cert_pair->private_key, - strlen(key_cert_pair->private_key)); - if (result != TSI_OK || !SSL_CTX_check_private_key(context)) { - gpr_log(GPR_ERROR, "Invalid private key."); - return result != TSI_OK ? result : TSI_INVALID_ARGUMENT; - } - } - } - if ((cipher_list != NULL) && !SSL_CTX_set_cipher_list(context, cipher_list)) { - gpr_log(GPR_ERROR, "Invalid cipher list: %s.", cipher_list); - return TSI_INVALID_ARGUMENT; - } - { - EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); - if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) { - gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key."); - EC_KEY_free(ecdh); - return TSI_INTERNAL_ERROR; - } - SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); - EC_KEY_free(ecdh); - } - return TSI_OK; -} - -/* Extracts the CN and the SANs from an X509 cert as a peer object. */ -static tsi_result extract_x509_subject_names_from_pem_cert(const char *pem_cert, - tsi_peer *peer) { - tsi_result result = TSI_OK; - X509 *cert = NULL; - BIO *pem; - pem = BIO_new_mem_buf((void *)pem_cert, (int)strlen(pem_cert)); - if (pem == NULL) return TSI_OUT_OF_RESOURCES; - - cert = PEM_read_bio_X509(pem, NULL, NULL, (void *)""); - if (cert == NULL) { - gpr_log(GPR_ERROR, "Invalid certificate"); - result = TSI_INVALID_ARGUMENT; - } else { - result = peer_from_x509(cert, 0, peer); - } - if (cert != NULL) X509_free(cert); - BIO_free(pem); - return result; -} - -/* Builds the alpn protocol name list according to rfc 7301. */ -static tsi_result build_alpn_protocol_name_list( - const char **alpn_protocols, uint16_t num_alpn_protocols, - unsigned char **protocol_name_list, size_t *protocol_name_list_length) { - uint16_t i; - unsigned char *current; - *protocol_name_list = NULL; - *protocol_name_list_length = 0; - if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; - for (i = 0; i < num_alpn_protocols; i++) { - size_t length = alpn_protocols[i] == NULL ? 0 : strlen(alpn_protocols[i]); - if (length == 0 || length > 255) { - gpr_log(GPR_ERROR, "Invalid protocol name length: %d.", (int)length); - return TSI_INVALID_ARGUMENT; - } - *protocol_name_list_length += length + 1; - } - *protocol_name_list = (unsigned char *)gpr_malloc(*protocol_name_list_length); - if (*protocol_name_list == NULL) return TSI_OUT_OF_RESOURCES; - current = *protocol_name_list; - for (i = 0; i < num_alpn_protocols; i++) { - size_t length = strlen(alpn_protocols[i]); - *(current++) = (uint8_t)length; /* max checked above. */ - memcpy(current, alpn_protocols[i], length); - current += length; - } - /* Safety check. */ - if ((current < *protocol_name_list) || - ((uintptr_t)(current - *protocol_name_list) != - *protocol_name_list_length)) { - return TSI_INTERNAL_ERROR; - } - return TSI_OK; -} - -// The verification callback is used for clients that don't really care about -// the server's certificate, but we need to pull it anyway, in case a higher -// layer wants to look at it. In this case the verification may fail, but -// we don't really care. -static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX *ctx) { - return 1; -} - -/* --- tsi_frame_protector methods implementation. ---*/ - -static tsi_result ssl_protector_protect(tsi_frame_protector *self, - const unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size, - unsigned char *protected_output_frames, - size_t *protected_output_frames_size) { - tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; - int read_from_ssl; - size_t available; - tsi_result result = TSI_OK; - - /* First see if we have some pending data in the SSL BIO. */ - int pending_in_ssl = (int)BIO_pending(impl->from_ssl); - if (pending_in_ssl > 0) { - *unprotected_bytes_size = 0; - GPR_ASSERT(*protected_output_frames_size <= INT_MAX); - read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - (int)*protected_output_frames_size); - if (read_from_ssl < 0) { - gpr_log(GPR_ERROR, - "Could not read from BIO even though some data is pending"); - return TSI_INTERNAL_ERROR; - } - *protected_output_frames_size = (size_t)read_from_ssl; - return TSI_OK; - } - - /* Now see if we can send a complete frame. */ - available = impl->buffer_size - impl->buffer_offset; - if (available > *unprotected_bytes_size) { - /* If we cannot, just copy the data in our internal buffer. */ - memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, - *unprotected_bytes_size); - impl->buffer_offset += *unprotected_bytes_size; - *protected_output_frames_size = 0; - return TSI_OK; - } - - /* If we can, prepare the buffer, send it to SSL_write and read. */ - memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, available); - result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size); - if (result != TSI_OK) return result; - - GPR_ASSERT(*protected_output_frames_size <= INT_MAX); - read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - (int)*protected_output_frames_size); - if (read_from_ssl < 0) { - gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); - return TSI_INTERNAL_ERROR; - } - *protected_output_frames_size = (size_t)read_from_ssl; - *unprotected_bytes_size = available; - impl->buffer_offset = 0; - return TSI_OK; -} - -static tsi_result ssl_protector_protect_flush( - tsi_frame_protector *self, unsigned char *protected_output_frames, - size_t *protected_output_frames_size, size_t *still_pending_size) { - tsi_result result = TSI_OK; - tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; - int read_from_ssl = 0; - int pending; - - if (impl->buffer_offset != 0) { - result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset); - if (result != TSI_OK) return result; - impl->buffer_offset = 0; - } - - pending = (int)BIO_pending(impl->from_ssl); - GPR_ASSERT(pending >= 0); - *still_pending_size = (size_t)pending; - if (*still_pending_size == 0) return TSI_OK; - - GPR_ASSERT(*protected_output_frames_size <= INT_MAX); - read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, - (int)*protected_output_frames_size); - if (read_from_ssl <= 0) { - gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); - return TSI_INTERNAL_ERROR; - } - *protected_output_frames_size = (size_t)read_from_ssl; - pending = (int)BIO_pending(impl->from_ssl); - GPR_ASSERT(pending >= 0); - *still_pending_size = (size_t)pending; - return TSI_OK; -} - -static tsi_result ssl_protector_unprotect( - tsi_frame_protector *self, const unsigned char *protected_frames_bytes, - size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size) { - tsi_result result = TSI_OK; - int written_into_ssl = 0; - size_t output_bytes_size = *unprotected_bytes_size; - size_t output_bytes_offset = 0; - tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; - - /* First, try to read remaining data from ssl. */ - result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); - if (result != TSI_OK) return result; - if (*unprotected_bytes_size == output_bytes_size) { - /* We have read everything we could and cannot process any more input. */ - *protected_frames_bytes_size = 0; - return TSI_OK; - } - output_bytes_offset = *unprotected_bytes_size; - unprotected_bytes += output_bytes_offset; - *unprotected_bytes_size = output_bytes_size - output_bytes_offset; - - /* Then, try to write some data to ssl. */ - GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX); - written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes, - (int)*protected_frames_bytes_size); - if (written_into_ssl < 0) { - gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d", - written_into_ssl); - return TSI_INTERNAL_ERROR; - } - *protected_frames_bytes_size = (size_t)written_into_ssl; - - /* Now try to read some data again. */ - result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); - if (result == TSI_OK) { - /* Don't forget to output the total number of bytes read. */ - *unprotected_bytes_size += output_bytes_offset; - } - return result; -} - -static void ssl_protector_destroy(tsi_frame_protector *self) { - tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; - if (impl->buffer != NULL) gpr_free(impl->buffer); - if (impl->ssl != NULL) SSL_free(impl->ssl); - gpr_free(self); -} - -static const tsi_frame_protector_vtable frame_protector_vtable = { - ssl_protector_protect, ssl_protector_protect_flush, ssl_protector_unprotect, - ssl_protector_destroy, -}; - -/* --- tsi_server_handshaker_factory methods implementation. --- */ - -static void tsi_ssl_handshaker_factory_destroy( - tsi_ssl_handshaker_factory *self) { - if (self == NULL) return; - - if (self->vtable != NULL && self->vtable->destroy != NULL) { - self->vtable->destroy(self); - } - /* Note, we don't free(self) here because this object is always directly - * embedded in another object. If tsi_ssl_handshaker_factory_init allocates - * any memory, it should be free'd here. */ -} - -static tsi_ssl_handshaker_factory *tsi_ssl_handshaker_factory_ref( - tsi_ssl_handshaker_factory *self) { - if (self == NULL) return NULL; - gpr_refn(&self->refcount, 1); - return self; -} - -static void tsi_ssl_handshaker_factory_unref(tsi_ssl_handshaker_factory *self) { - if (self == NULL) return; - - if (gpr_unref(&self->refcount)) { - tsi_ssl_handshaker_factory_destroy(self); - } -} - -static tsi_ssl_handshaker_factory_vtable handshaker_factory_vtable = {NULL}; - -/* Initializes a tsi_ssl_handshaker_factory object. Caller is responsible for - * allocating memory for the factory. */ -static void tsi_ssl_handshaker_factory_init( - tsi_ssl_handshaker_factory *factory) { - GPR_ASSERT(factory != NULL); - - factory->vtable = &handshaker_factory_vtable; - gpr_ref_init(&factory->refcount, 1); -} - -/* --- tsi_handshaker methods implementation. ---*/ - -static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, - unsigned char *bytes, - size_t *bytes_size) { - tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; - int bytes_read_from_ssl = 0; - if (bytes == NULL || bytes_size == NULL || *bytes_size == 0 || - *bytes_size > INT_MAX) { - return TSI_INVALID_ARGUMENT; - } - GPR_ASSERT(*bytes_size <= INT_MAX); - bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, (int)*bytes_size); - if (bytes_read_from_ssl < 0) { - *bytes_size = 0; - if (!BIO_should_retry(impl->from_ssl)) { - impl->result = TSI_INTERNAL_ERROR; - return impl->result; - } else { - return TSI_OK; - } - } - *bytes_size = (size_t)bytes_read_from_ssl; - return BIO_pending(impl->from_ssl) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA; -} - -static tsi_result ssl_handshaker_get_result(tsi_handshaker *self) { - tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; - if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) && - SSL_is_init_finished(impl->ssl)) { - impl->result = TSI_OK; - } - return impl->result; -} - -static tsi_result ssl_handshaker_process_bytes_from_peer( - tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { - tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; - int bytes_written_into_ssl_size = 0; - if (bytes == NULL || bytes_size == 0 || *bytes_size > INT_MAX) { - return TSI_INVALID_ARGUMENT; - } - GPR_ASSERT(*bytes_size <= INT_MAX); - bytes_written_into_ssl_size = - BIO_write(impl->into_ssl, bytes, (int)*bytes_size); - if (bytes_written_into_ssl_size < 0) { - gpr_log(GPR_ERROR, "Could not write to memory BIO."); - impl->result = TSI_INTERNAL_ERROR; - return impl->result; - } - *bytes_size = (size_t)bytes_written_into_ssl_size; - - if (!tsi_handshaker_is_in_progress(self)) { - impl->result = TSI_OK; - return impl->result; - } else { - /* Get ready to get some bytes from SSL. */ - int ssl_result = SSL_do_handshake(impl->ssl); - ssl_result = SSL_get_error(impl->ssl, ssl_result); - switch (ssl_result) { - case SSL_ERROR_WANT_READ: - if (BIO_pending(impl->from_ssl) == 0) { - /* We need more data. */ - return TSI_INCOMPLETE_DATA; - } else { - return TSI_OK; - } - case SSL_ERROR_NONE: - return TSI_OK; - default: { - char err_str[256]; - ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); - gpr_log(GPR_ERROR, "Handshake failed with fatal error %s: %s.", - ssl_error_string(ssl_result), err_str); - impl->result = TSI_PROTOCOL_FAILURE; - return impl->result; - } - } - } -} - -static tsi_result ssl_handshaker_extract_peer(tsi_handshaker *self, - tsi_peer *peer) { - tsi_result result = TSI_OK; - const unsigned char *alpn_selected = NULL; - unsigned int alpn_selected_len; - tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; - X509 *peer_cert = SSL_get_peer_certificate(impl->ssl); - if (peer_cert != NULL) { - result = peer_from_x509(peer_cert, 1, peer); - X509_free(peer_cert); - if (result != TSI_OK) return result; - } -#if TSI_OPENSSL_ALPN_SUPPORT - SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len); -#endif /* TSI_OPENSSL_ALPN_SUPPORT */ - if (alpn_selected == NULL) { - /* Try npn. */ - SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected, - &alpn_selected_len); - } - if (alpn_selected != NULL) { - size_t i; - tsi_peer_property *new_properties = (tsi_peer_property *)gpr_zalloc( - sizeof(*new_properties) * (peer->property_count + 1)); - for (i = 0; i < peer->property_count; i++) { - new_properties[i] = peer->properties[i]; - } - result = tsi_construct_string_peer_property( - TSI_SSL_ALPN_SELECTED_PROTOCOL, (const char *)alpn_selected, - alpn_selected_len, &new_properties[peer->property_count]); - if (result != TSI_OK) { - gpr_free(new_properties); - return result; - } - if (peer->properties != NULL) gpr_free(peer->properties); - peer->property_count++; - peer->properties = new_properties; - } - return result; -} - -static tsi_result ssl_handshaker_create_frame_protector( - tsi_handshaker *self, size_t *max_output_protected_frame_size, - tsi_frame_protector **protector) { - size_t actual_max_output_protected_frame_size = - TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; - tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; - tsi_ssl_frame_protector *protector_impl = - (tsi_ssl_frame_protector *)gpr_zalloc(sizeof(*protector_impl)); - - if (max_output_protected_frame_size != NULL) { - if (*max_output_protected_frame_size > - TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND) { - *max_output_protected_frame_size = - TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; - } else if (*max_output_protected_frame_size < - TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND) { - *max_output_protected_frame_size = - TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND; - } - actual_max_output_protected_frame_size = *max_output_protected_frame_size; - } - protector_impl->buffer_size = - actual_max_output_protected_frame_size - TSI_SSL_MAX_PROTECTION_OVERHEAD; - protector_impl->buffer = - (unsigned char *)gpr_malloc(protector_impl->buffer_size); - if (protector_impl->buffer == NULL) { - gpr_log(GPR_ERROR, - "Could not allocated buffer for tsi_ssl_frame_protector."); - gpr_free(protector_impl); - return TSI_INTERNAL_ERROR; - } - - /* Transfer ownership of ssl to the frame protector. It is OK as the caller - * cannot call anything else but destroy on the handshaker after this call. */ - protector_impl->ssl = impl->ssl; - impl->ssl = NULL; - protector_impl->into_ssl = impl->into_ssl; - protector_impl->from_ssl = impl->from_ssl; - - protector_impl->base.vtable = &frame_protector_vtable; - *protector = &protector_impl->base; - return TSI_OK; -} - -static void ssl_handshaker_destroy(tsi_handshaker *self) { - tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; - SSL_free(impl->ssl); /* The BIO objects are owned by ssl */ - tsi_ssl_handshaker_factory_unref(impl->factory_ref); - gpr_free(impl); -} - -static const tsi_handshaker_vtable handshaker_vtable = { - ssl_handshaker_get_bytes_to_send_to_peer, - ssl_handshaker_process_bytes_from_peer, - ssl_handshaker_get_result, - ssl_handshaker_extract_peer, - ssl_handshaker_create_frame_protector, - ssl_handshaker_destroy, - NULL, -}; - -/* --- tsi_ssl_handshaker_factory common methods. --- */ - -static tsi_result create_tsi_ssl_handshaker(SSL_CTX *ctx, int is_client, - const char *server_name_indication, - tsi_ssl_handshaker_factory *factory, - tsi_handshaker **handshaker) { - SSL *ssl = SSL_new(ctx); - BIO *into_ssl = NULL; - BIO *from_ssl = NULL; - tsi_ssl_handshaker *impl = NULL; - *handshaker = NULL; - if (ctx == NULL) { - gpr_log(GPR_ERROR, "SSL Context is null. Should never happen."); - return TSI_INTERNAL_ERROR; - } - if (ssl == NULL) { - return TSI_OUT_OF_RESOURCES; - } - SSL_set_info_callback(ssl, ssl_info_callback); - - into_ssl = BIO_new(BIO_s_mem()); - from_ssl = BIO_new(BIO_s_mem()); - if (into_ssl == NULL || from_ssl == NULL) { - gpr_log(GPR_ERROR, "BIO_new failed."); - SSL_free(ssl); - if (into_ssl != NULL) BIO_free(into_ssl); - if (from_ssl != NULL) BIO_free(into_ssl); - return TSI_OUT_OF_RESOURCES; - } - SSL_set_bio(ssl, into_ssl, from_ssl); - if (is_client) { - int ssl_result; - SSL_set_connect_state(ssl); - if (server_name_indication != NULL) { - if (!SSL_set_tlsext_host_name(ssl, server_name_indication)) { - gpr_log(GPR_ERROR, "Invalid server name indication %s.", - server_name_indication); - SSL_free(ssl); - return TSI_INTERNAL_ERROR; - } - } - ssl_result = SSL_do_handshake(ssl); - ssl_result = SSL_get_error(ssl, ssl_result); - if (ssl_result != SSL_ERROR_WANT_READ) { - gpr_log(GPR_ERROR, - "Unexpected error received from first SSL_do_handshake call: %s", - ssl_error_string(ssl_result)); - SSL_free(ssl); - return TSI_INTERNAL_ERROR; - } - } else { - SSL_set_accept_state(ssl); - } - - impl = (tsi_ssl_handshaker *)gpr_zalloc(sizeof(*impl)); - impl->ssl = ssl; - impl->into_ssl = into_ssl; - impl->from_ssl = from_ssl; - impl->result = TSI_HANDSHAKE_IN_PROGRESS; - impl->base.vtable = &handshaker_vtable; - impl->factory_ref = tsi_ssl_handshaker_factory_ref(factory); - - *handshaker = &impl->base; - return TSI_OK; -} - -static int select_protocol_list(const unsigned char **out, - unsigned char *outlen, - const unsigned char *client_list, - size_t client_list_len, - const unsigned char *server_list, - size_t server_list_len) { - const unsigned char *client_current = client_list; - while ((unsigned int)(client_current - client_list) < client_list_len) { - unsigned char client_current_len = *(client_current++); - const unsigned char *server_current = server_list; - while ((server_current >= server_list) && - (uintptr_t)(server_current - server_list) < server_list_len) { - unsigned char server_current_len = *(server_current++); - if ((client_current_len == server_current_len) && - !memcmp(client_current, server_current, server_current_len)) { - *out = server_current; - *outlen = server_current_len; - return SSL_TLSEXT_ERR_OK; - } - server_current += server_current_len; - } - client_current += client_current_len; - } - return SSL_TLSEXT_ERR_NOACK; -} - -/* --- tsi_ssl_client_handshaker_factory methods implementation. --- */ - -tsi_result tsi_ssl_client_handshaker_factory_create_handshaker( - tsi_ssl_client_handshaker_factory *self, const char *server_name_indication, - tsi_handshaker **handshaker) { - return create_tsi_ssl_handshaker(self->ssl_context, 1, server_name_indication, - &self->base, handshaker); -} - -void tsi_ssl_client_handshaker_factory_unref( - tsi_ssl_client_handshaker_factory *self) { - if (self == NULL) return; - tsi_ssl_handshaker_factory_unref(&self->base); -} - -static void tsi_ssl_client_handshaker_factory_destroy( - tsi_ssl_handshaker_factory *factory) { - if (factory == NULL) return; - tsi_ssl_client_handshaker_factory *self = - (tsi_ssl_client_handshaker_factory *)factory; - if (self->ssl_context != NULL) SSL_CTX_free(self->ssl_context); - if (self->alpn_protocol_list != NULL) gpr_free(self->alpn_protocol_list); - gpr_free(self); -} - -static int client_handshaker_factory_npn_callback(SSL *ssl, unsigned char **out, - unsigned char *outlen, - const unsigned char *in, - unsigned int inlen, - void *arg) { - tsi_ssl_client_handshaker_factory *factory = - (tsi_ssl_client_handshaker_factory *)arg; - return select_protocol_list((const unsigned char **)out, outlen, - factory->alpn_protocol_list, - factory->alpn_protocol_list_length, in, inlen); -} - -/* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ - -tsi_result tsi_ssl_server_handshaker_factory_create_handshaker( - tsi_ssl_server_handshaker_factory *self, tsi_handshaker **handshaker) { - if (self->ssl_context_count == 0) return TSI_INVALID_ARGUMENT; - /* Create the handshaker with the first context. We will switch if needed - because of SNI in ssl_server_handshaker_factory_servername_callback. */ - return create_tsi_ssl_handshaker(self->ssl_contexts[0], 0, NULL, &self->base, - handshaker); -} - -void tsi_ssl_server_handshaker_factory_unref( - tsi_ssl_server_handshaker_factory *self) { - if (self == NULL) return; - tsi_ssl_handshaker_factory_unref(&self->base); -} - -static void tsi_ssl_server_handshaker_factory_destroy( - tsi_ssl_handshaker_factory *factory) { - if (factory == NULL) return; - tsi_ssl_server_handshaker_factory *self = - (tsi_ssl_server_handshaker_factory *)factory; - size_t i; - for (i = 0; i < self->ssl_context_count; i++) { - if (self->ssl_contexts[i] != NULL) { - SSL_CTX_free(self->ssl_contexts[i]); - tsi_peer_destruct(&self->ssl_context_x509_subject_names[i]); - } - } - if (self->ssl_contexts != NULL) gpr_free(self->ssl_contexts); - if (self->ssl_context_x509_subject_names != NULL) { - gpr_free(self->ssl_context_x509_subject_names); - } - if (self->alpn_protocol_list != NULL) gpr_free(self->alpn_protocol_list); - gpr_free(self); -} - -static int does_entry_match_name(const char *entry, size_t entry_length, - const char *name) { - const char *dot; - const char *name_subdomain = NULL; - size_t name_length = strlen(name); - size_t name_subdomain_length; - if (entry_length == 0) return 0; - - /* Take care of '.' terminations. */ - if (name[name_length - 1] == '.') { - name_length--; - } - if (entry[entry_length - 1] == '.') { - entry_length--; - if (entry_length == 0) return 0; - } - - if ((name_length == entry_length) && - strncmp(name, entry, entry_length) == 0) { - return 1; /* Perfect match. */ - } - if (entry[0] != '*') return 0; - - /* Wildchar subdomain matching. */ - if (entry_length < 3 || entry[1] != '.') { /* At least *.x */ - gpr_log(GPR_ERROR, "Invalid wildchar entry."); - return 0; - } - name_subdomain = strchr(name, '.'); - if (name_subdomain == NULL) return 0; - name_subdomain_length = strlen(name_subdomain); - if (name_subdomain_length < 2) return 0; - name_subdomain++; /* Starts after the dot. */ - name_subdomain_length--; - entry += 2; /* Remove *. */ - entry_length -= 2; - dot = strchr(name_subdomain, '.'); - if ((dot == NULL) || (dot == &name_subdomain[name_subdomain_length - 1])) { - gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain); - return 0; - } - if (name_subdomain[name_subdomain_length - 1] == '.') { - name_subdomain_length--; - } - return ((entry_length > 0) && (name_subdomain_length == entry_length) && - strncmp(entry, name_subdomain, entry_length) == 0); -} - -static int ssl_server_handshaker_factory_servername_callback(SSL *ssl, int *ap, - void *arg) { - tsi_ssl_server_handshaker_factory *impl = - (tsi_ssl_server_handshaker_factory *)arg; - size_t i = 0; - const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - if (servername == NULL || strlen(servername) == 0) { - return SSL_TLSEXT_ERR_NOACK; - } - - for (i = 0; i < impl->ssl_context_count; i++) { - if (tsi_ssl_peer_matches_name(&impl->ssl_context_x509_subject_names[i], - servername)) { - SSL_set_SSL_CTX(ssl, impl->ssl_contexts[i]); - return SSL_TLSEXT_ERR_OK; - } - } - gpr_log(GPR_ERROR, "No match found for server name: %s.", servername); - return SSL_TLSEXT_ERR_ALERT_WARNING; -} - -#if TSI_OPENSSL_ALPN_SUPPORT -static int server_handshaker_factory_alpn_callback( - SSL *ssl, const unsigned char **out, unsigned char *outlen, - const unsigned char *in, unsigned int inlen, void *arg) { - tsi_ssl_server_handshaker_factory *factory = - (tsi_ssl_server_handshaker_factory *)arg; - return select_protocol_list(out, outlen, in, inlen, - factory->alpn_protocol_list, - factory->alpn_protocol_list_length); -} -#endif /* TSI_OPENSSL_ALPN_SUPPORT */ - -static int server_handshaker_factory_npn_advertised_callback( - SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) { - tsi_ssl_server_handshaker_factory *factory = - (tsi_ssl_server_handshaker_factory *)arg; - *out = factory->alpn_protocol_list; - GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX); - *outlen = (unsigned int)factory->alpn_protocol_list_length; - return SSL_TLSEXT_ERR_OK; -} - -/* --- tsi_ssl_handshaker_factory constructors. --- */ - -static tsi_ssl_handshaker_factory_vtable client_handshaker_factory_vtable = { - tsi_ssl_client_handshaker_factory_destroy}; - -tsi_result tsi_create_ssl_client_handshaker_factory( - const tsi_ssl_pem_key_cert_pair *pem_key_cert_pair, - const char *pem_root_certs, const char *cipher_suites, - const char **alpn_protocols, uint16_t num_alpn_protocols, - tsi_ssl_client_handshaker_factory **factory) { - SSL_CTX *ssl_context = NULL; - tsi_ssl_client_handshaker_factory *impl = NULL; - tsi_result result = TSI_OK; - - gpr_once_init(&init_openssl_once, init_openssl); - - if (factory == NULL) return TSI_INVALID_ARGUMENT; - *factory = NULL; - if (pem_root_certs == NULL) return TSI_INVALID_ARGUMENT; - - ssl_context = SSL_CTX_new(TLSv1_2_method()); - if (ssl_context == NULL) { - gpr_log(GPR_ERROR, "Could not create ssl context."); - return TSI_INVALID_ARGUMENT; - } - - impl = (tsi_ssl_client_handshaker_factory *)gpr_zalloc(sizeof(*impl)); - tsi_ssl_handshaker_factory_init(&impl->base); - impl->base.vtable = &client_handshaker_factory_vtable; - - impl->ssl_context = ssl_context; - - do { - result = - populate_ssl_context(ssl_context, pem_key_cert_pair, cipher_suites); - if (result != TSI_OK) break; - result = ssl_ctx_load_verification_certs(ssl_context, pem_root_certs, - strlen(pem_root_certs), NULL); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Cannot load server root certificates."); - break; - } - - if (num_alpn_protocols != 0) { - result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, - &impl->alpn_protocol_list, - &impl->alpn_protocol_list_length); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Building alpn list failed with error %s.", - tsi_result_to_string(result)); - break; - } -#if TSI_OPENSSL_ALPN_SUPPORT - GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX); - if (SSL_CTX_set_alpn_protos( - ssl_context, impl->alpn_protocol_list, - (unsigned int)impl->alpn_protocol_list_length)) { - gpr_log(GPR_ERROR, "Could not set alpn protocol list to context."); - result = TSI_INVALID_ARGUMENT; - break; - } -#endif /* TSI_OPENSSL_ALPN_SUPPORT */ - SSL_CTX_set_next_proto_select_cb( - ssl_context, client_handshaker_factory_npn_callback, impl); - } - } while (0); - if (result != TSI_OK) { - tsi_ssl_handshaker_factory_unref(&impl->base); - return result; - } - SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL); - /* TODO(jboeuf): Add revocation verification. */ - - *factory = impl; - return TSI_OK; -} - -static tsi_ssl_handshaker_factory_vtable server_handshaker_factory_vtable = { - tsi_ssl_server_handshaker_factory_destroy}; - -tsi_result tsi_create_ssl_server_handshaker_factory( - const tsi_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, const char *pem_client_root_certs, - int force_client_auth, const char *cipher_suites, - const char **alpn_protocols, uint16_t num_alpn_protocols, - tsi_ssl_server_handshaker_factory **factory) { - return tsi_create_ssl_server_handshaker_factory_ex( - pem_key_cert_pairs, num_key_cert_pairs, pem_client_root_certs, - force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY - : TSI_DONT_REQUEST_CLIENT_CERTIFICATE, - cipher_suites, alpn_protocols, num_alpn_protocols, factory); -} - -tsi_result tsi_create_ssl_server_handshaker_factory_ex( - const tsi_ssl_pem_key_cert_pair *pem_key_cert_pairs, - size_t num_key_cert_pairs, const char *pem_client_root_certs, - tsi_client_certificate_request_type client_certificate_request, - const char *cipher_suites, const char **alpn_protocols, - uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory **factory) { - tsi_ssl_server_handshaker_factory *impl = NULL; - tsi_result result = TSI_OK; - size_t i = 0; - - gpr_once_init(&init_openssl_once, init_openssl); - - if (factory == NULL) return TSI_INVALID_ARGUMENT; - *factory = NULL; - if (num_key_cert_pairs == 0 || pem_key_cert_pairs == NULL) { - return TSI_INVALID_ARGUMENT; - } - - impl = (tsi_ssl_server_handshaker_factory *)gpr_zalloc(sizeof(*impl)); - tsi_ssl_handshaker_factory_init(&impl->base); - impl->base.vtable = &server_handshaker_factory_vtable; - - impl->ssl_contexts = - (SSL_CTX **)gpr_zalloc(num_key_cert_pairs * sizeof(SSL_CTX *)); - impl->ssl_context_x509_subject_names = - (tsi_peer *)gpr_zalloc(num_key_cert_pairs * sizeof(tsi_peer)); - if (impl->ssl_contexts == NULL || - impl->ssl_context_x509_subject_names == NULL) { - tsi_ssl_handshaker_factory_unref(&impl->base); - return TSI_OUT_OF_RESOURCES; - } - impl->ssl_context_count = num_key_cert_pairs; - - if (num_alpn_protocols > 0) { - result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, - &impl->alpn_protocol_list, - &impl->alpn_protocol_list_length); - if (result != TSI_OK) { - tsi_ssl_handshaker_factory_unref(&impl->base); - return result; - } - } - - for (i = 0; i < num_key_cert_pairs; i++) { - do { - impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); - if (impl->ssl_contexts[i] == NULL) { - gpr_log(GPR_ERROR, "Could not create ssl context."); - result = TSI_OUT_OF_RESOURCES; - break; - } - result = populate_ssl_context(impl->ssl_contexts[i], - &pem_key_cert_pairs[i], cipher_suites); - if (result != TSI_OK) break; - - if (pem_client_root_certs != NULL) { - STACK_OF(X509_NAME) *root_names = NULL; - result = ssl_ctx_load_verification_certs( - impl->ssl_contexts[i], pem_client_root_certs, - strlen(pem_client_root_certs), &root_names); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Invalid verification certs."); - break; - } - SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names); - switch (client_certificate_request) { - case TSI_DONT_REQUEST_CLIENT_CERTIFICATE: - SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, NULL); - break; - case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: - SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, - NullVerifyCallback); - break; - case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: - SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, NULL); - break; - case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: - SSL_CTX_set_verify( - impl->ssl_contexts[i], - SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - NullVerifyCallback); - break; - case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: - SSL_CTX_set_verify( - impl->ssl_contexts[i], - SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); - break; - } - /* TODO(jboeuf): Add revocation verification. */ - } - - result = extract_x509_subject_names_from_pem_cert( - pem_key_cert_pairs[i].cert_chain, - &impl->ssl_context_x509_subject_names[i]); - if (result != TSI_OK) break; - - SSL_CTX_set_tlsext_servername_callback( - impl->ssl_contexts[i], - ssl_server_handshaker_factory_servername_callback); - SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl); -#if TSI_OPENSSL_ALPN_SUPPORT - SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i], - server_handshaker_factory_alpn_callback, impl); -#endif /* TSI_OPENSSL_ALPN_SUPPORT */ - SSL_CTX_set_next_protos_advertised_cb( - impl->ssl_contexts[i], - server_handshaker_factory_npn_advertised_callback, impl); - } while (0); - - if (result != TSI_OK) { - tsi_ssl_handshaker_factory_unref(&impl->base); - return result; - } - } - - *factory = impl; - return TSI_OK; -} - -/* --- tsi_ssl utils. --- */ - -int tsi_ssl_peer_matches_name(const tsi_peer *peer, const char *name) { - size_t i = 0; - size_t san_count = 0; - const tsi_peer_property *cn_property = NULL; - int like_ip = looks_like_ip_address(name); - - /* Check the SAN first. */ - for (i = 0; i < peer->property_count; i++) { - const tsi_peer_property *property = &peer->properties[i]; - if (property->name == NULL) continue; - if (strcmp(property->name, - TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { - san_count++; - - if (!like_ip && does_entry_match_name(property->value.data, - property->value.length, name)) { - return 1; - } else if (like_ip && - strncmp(name, property->value.data, property->value.length) == - 0 && - strlen(name) == property->value.length) { - /* IP Addresses are exact matches only. */ - return 1; - } - } else if (strcmp(property->name, - TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { - cn_property = property; - } - } - - /* If there's no SAN, try the CN, but only if its not like an IP Address */ - if (san_count == 0 && cn_property != NULL && !like_ip) { - if (does_entry_match_name(cn_property->value.data, - cn_property->value.length, name)) { - return 1; - } - } - - return 0; /* Not found. */ -} - -/* --- Testing support. --- */ -const tsi_ssl_handshaker_factory_vtable *tsi_ssl_handshaker_factory_swap_vtable( - tsi_ssl_handshaker_factory *factory, - tsi_ssl_handshaker_factory_vtable *new_vtable) { - GPR_ASSERT(factory != NULL); - GPR_ASSERT(factory->vtable != NULL); - - const tsi_ssl_handshaker_factory_vtable *orig_vtable = factory->vtable; - factory->vtable = new_vtable; - return orig_vtable; -} diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc new file mode 100644 index 0000000000..dd59bccdae --- /dev/null +++ b/src/core/tsi/ssl_transport_security.cc @@ -0,0 +1,1604 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/tsi/ssl_transport_security.h" + +#include + +#include +#include + +/* TODO(jboeuf): refactor inet_ntop into a portability header. */ +/* Note: for whomever reads this and tries to refactor this, this + can't be in grpc, it has to be in gpr. */ +#ifdef GPR_WINDOWS +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include /* For OPENSSL_free */ +#include +#include +#include +#include + +#include "src/core/tsi/ssl_types.h" +#include "src/core/tsi/transport_security.h" + +/* --- Constants. ---*/ + +#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384 +#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024 + +/* Putting a macro like this and littering the source file with #if is really + bad practice. + TODO(jboeuf): refactor all the #if / #endif in a separate module. */ +#ifndef TSI_OPENSSL_ALPN_SUPPORT +#define TSI_OPENSSL_ALPN_SUPPORT 1 +#endif + +/* TODO(jboeuf): I have not found a way to get this number dynamically from the + SSL structure. This is what we would ultimately want though... */ +#define TSI_SSL_MAX_PROTECTION_OVERHEAD 100 + +/* --- Structure definitions. ---*/ + +struct tsi_ssl_handshaker_factory { + const tsi_ssl_handshaker_factory_vtable *vtable; + gpr_refcount refcount; +}; + +struct tsi_ssl_client_handshaker_factory { + tsi_ssl_handshaker_factory base; + SSL_CTX *ssl_context; + unsigned char *alpn_protocol_list; + size_t alpn_protocol_list_length; +}; + +struct tsi_ssl_server_handshaker_factory { + /* Several contexts to support SNI. + The tsi_peer array contains the subject names of the server certificates + associated with the contexts at the same index. */ + tsi_ssl_handshaker_factory base; + SSL_CTX **ssl_contexts; + tsi_peer *ssl_context_x509_subject_names; + size_t ssl_context_count; + unsigned char *alpn_protocol_list; + size_t alpn_protocol_list_length; +}; + +typedef struct { + tsi_handshaker base; + SSL *ssl; + BIO *into_ssl; + BIO *from_ssl; + tsi_result result; + tsi_ssl_handshaker_factory *factory_ref; +} tsi_ssl_handshaker; + +typedef struct { + tsi_frame_protector base; + SSL *ssl; + BIO *into_ssl; + BIO *from_ssl; + unsigned char *buffer; + size_t buffer_size; + size_t buffer_offset; +} tsi_ssl_frame_protector; + +/* --- Library Initialization. ---*/ + +static gpr_once init_openssl_once = GPR_ONCE_INIT; +static gpr_mu *openssl_mutexes = NULL; + +static void openssl_locking_cb(int mode, int type, const char *file, int line) { + if (mode & CRYPTO_LOCK) { + gpr_mu_lock(&openssl_mutexes[type]); + } else { + gpr_mu_unlock(&openssl_mutexes[type]); + } +} + +static unsigned long openssl_thread_id_cb(void) { + return (unsigned long)gpr_thd_currentid(); +} + +static void init_openssl(void) { + int i; + int num_locks; + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + num_locks = CRYPTO_num_locks(); + GPR_ASSERT(num_locks > 0); + openssl_mutexes = (gpr_mu *)gpr_malloc((size_t)num_locks * sizeof(gpr_mu)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + gpr_mu_init(&openssl_mutexes[i]); + } + CRYPTO_set_locking_callback(openssl_locking_cb); + CRYPTO_set_id_callback(openssl_thread_id_cb); +} + +/* --- Ssl utils. ---*/ + +static const char *ssl_error_string(int error) { + switch (error) { + case SSL_ERROR_NONE: + return "SSL_ERROR_NONE"; + case SSL_ERROR_ZERO_RETURN: + return "SSL_ERROR_ZERO_RETURN"; + case SSL_ERROR_WANT_READ: + return "SSL_ERROR_WANT_READ"; + case SSL_ERROR_WANT_WRITE: + return "SSL_ERROR_WANT_WRITE"; + case SSL_ERROR_WANT_CONNECT: + return "SSL_ERROR_WANT_CONNECT"; + case SSL_ERROR_WANT_ACCEPT: + return "SSL_ERROR_WANT_ACCEPT"; + case SSL_ERROR_WANT_X509_LOOKUP: + return "SSL_ERROR_WANT_X509_LOOKUP"; + case SSL_ERROR_SYSCALL: + return "SSL_ERROR_SYSCALL"; + case SSL_ERROR_SSL: + return "SSL_ERROR_SSL"; + default: + return "Unknown error"; + } +} + +/* TODO(jboeuf): Remove when we are past the debugging phase with this code. */ +static void ssl_log_where_info(const SSL *ssl, int where, int flag, + const char *msg) { + if ((where & flag) && GRPC_TRACER_ON(tsi_tracing_enabled)) { + gpr_log(GPR_INFO, "%20.20s - %30.30s - %5.10s", msg, + SSL_state_string_long(ssl), SSL_state_string(ssl)); + } +} + +/* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */ +static void ssl_info_callback(const SSL *ssl, int where, int ret) { + if (ret == 0) { + gpr_log(GPR_ERROR, "ssl_info_callback: error occured.\n"); + return; + } + + ssl_log_where_info(ssl, where, SSL_CB_LOOP, "LOOP"); + ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START"); + ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE"); +} + +/* Returns 1 if name looks like an IP address, 0 otherwise. + This is a very rough heuristic, and only handles IPv6 in hexadecimal form. */ +static int looks_like_ip_address(const char *name) { + size_t i; + size_t dot_count = 0; + size_t num_size = 0; + for (i = 0; i < strlen(name); i++) { + if (name[i] == ':') { + /* IPv6 Address in hexadecimal form, : is not allowed in DNS names. */ + return 1; + } + if (name[i] >= '0' && name[i] <= '9') { + if (num_size > 3) return 0; + num_size++; + } else if (name[i] == '.') { + if (dot_count > 3 || num_size == 0) return 0; + dot_count++; + num_size = 0; + } else { + return 0; + } + } + if (dot_count < 3 || num_size == 0) return 0; + return 1; +} + +/* Gets the subject CN from an X509 cert. */ +static tsi_result ssl_get_x509_common_name(X509 *cert, unsigned char **utf8, + size_t *utf8_size) { + int common_name_index = -1; + X509_NAME_ENTRY *common_name_entry = NULL; + ASN1_STRING *common_name_asn1 = NULL; + X509_NAME *subject_name = X509_get_subject_name(cert); + int utf8_returned_size = 0; + if (subject_name == NULL) { + gpr_log(GPR_ERROR, "Could not get subject name from certificate."); + return TSI_NOT_FOUND; + } + common_name_index = + X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1); + if (common_name_index == -1) { + gpr_log(GPR_ERROR, + "Could not get common name of subject from certificate."); + return TSI_NOT_FOUND; + } + common_name_entry = X509_NAME_get_entry(subject_name, common_name_index); + if (common_name_entry == NULL) { + gpr_log(GPR_ERROR, "Could not get common name entry from certificate."); + return TSI_INTERNAL_ERROR; + } + common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == NULL) { + gpr_log(GPR_ERROR, + "Could not get common name entry asn1 from certificate."); + return TSI_INTERNAL_ERROR; + } + utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1); + if (utf8_returned_size < 0) { + gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string."); + return TSI_OUT_OF_RESOURCES; + } + *utf8_size = (size_t)utf8_returned_size; + return TSI_OK; +} + +/* Gets the subject CN of an X509 cert as a tsi_peer_property. */ +static tsi_result peer_property_from_x509_common_name( + X509 *cert, tsi_peer_property *property) { + unsigned char *common_name; + size_t common_name_size; + tsi_result result = + ssl_get_x509_common_name(cert, &common_name, &common_name_size); + if (result != TSI_OK) { + if (result == TSI_NOT_FOUND) { + common_name = NULL; + common_name_size = 0; + } else { + return result; + } + } + result = tsi_construct_string_peer_property( + TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, + common_name == NULL ? "" : (const char *)common_name, common_name_size, + property); + OPENSSL_free(common_name); + return result; +} + +/* Gets the X509 cert in PEM format as a tsi_peer_property. */ +static tsi_result add_pem_certificate(X509 *cert, tsi_peer_property *property) { + BIO *bio = BIO_new(BIO_s_mem()); + if (!PEM_write_bio_X509(bio, cert)) { + BIO_free(bio); + return TSI_INTERNAL_ERROR; + } + char *contents; + long len = BIO_get_mem_data(bio, &contents); + if (len <= 0) { + BIO_free(bio); + return TSI_INTERNAL_ERROR; + } + tsi_result result = tsi_construct_string_peer_property( + TSI_X509_PEM_CERT_PROPERTY, (const char *)contents, (size_t)len, + property); + BIO_free(bio); + return result; +} + +/* Gets the subject SANs from an X509 cert as a tsi_peer_property. */ +static tsi_result add_subject_alt_names_properties_to_peer( + tsi_peer *peer, GENERAL_NAMES *subject_alt_names, + size_t subject_alt_name_count) { + size_t i; + tsi_result result = TSI_OK; + + /* Reset for DNS entries filtering. */ + peer->property_count -= subject_alt_name_count; + + for (i = 0; i < subject_alt_name_count; i++) { + GENERAL_NAME *subject_alt_name = + sk_GENERAL_NAME_value(subject_alt_names, TSI_SIZE_AS_SIZE(i)); + /* Filter out the non-dns entries names. */ + if (subject_alt_name->type == GEN_DNS) { + unsigned char *name = NULL; + int name_size; + name_size = ASN1_STRING_to_UTF8(&name, subject_alt_name->d.dNSName); + if (name_size < 0) { + gpr_log(GPR_ERROR, "Could not get utf8 from asn1 string."); + result = TSI_INTERNAL_ERROR; + break; + } + result = tsi_construct_string_peer_property( + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, (const char *)name, + (size_t)name_size, &peer->properties[peer->property_count++]); + OPENSSL_free(name); + } else if (subject_alt_name->type == GEN_IPADD) { + char ntop_buf[INET6_ADDRSTRLEN]; + int af; + + if (subject_alt_name->d.iPAddress->length == 4) { + af = AF_INET; + } else if (subject_alt_name->d.iPAddress->length == 16) { + af = AF_INET6; + } else { + gpr_log(GPR_ERROR, "SAN IP Address contained invalid IP"); + result = TSI_INTERNAL_ERROR; + break; + } + const char *name = inet_ntop(af, subject_alt_name->d.iPAddress->data, + ntop_buf, INET6_ADDRSTRLEN); + if (name == NULL) { + gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet."); + result = TSI_INTERNAL_ERROR; + break; + } + + result = tsi_construct_string_peer_property_from_cstring( + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, name, + &peer->properties[peer->property_count++]); + } + if (result != TSI_OK) break; + } + return result; +} + +/* Gets information about the peer's X509 cert as a tsi_peer object. */ +static tsi_result peer_from_x509(X509 *cert, int include_certificate_type, + tsi_peer *peer) { + /* TODO(jboeuf): Maybe add more properties. */ + GENERAL_NAMES *subject_alt_names = + (GENERAL_NAMES *)X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); + int subject_alt_name_count = (subject_alt_names != NULL) + ? (int)sk_GENERAL_NAME_num(subject_alt_names) + : 0; + size_t property_count; + tsi_result result; + GPR_ASSERT(subject_alt_name_count >= 0); + property_count = (include_certificate_type ? (size_t)1 : 0) + + 2 /* common name, certificate */ + + (size_t)subject_alt_name_count; + result = tsi_construct_peer(property_count, peer); + if (result != TSI_OK) return result; + do { + if (include_certificate_type) { + result = tsi_construct_string_peer_property_from_cstring( + TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, + &peer->properties[0]); + if (result != TSI_OK) break; + } + result = peer_property_from_x509_common_name( + cert, &peer->properties[include_certificate_type ? 1 : 0]); + if (result != TSI_OK) break; + + result = add_pem_certificate( + cert, &peer->properties[include_certificate_type ? 2 : 1]); + if (result != TSI_OK) break; + + if (subject_alt_name_count != 0) { + result = add_subject_alt_names_properties_to_peer( + peer, subject_alt_names, (size_t)subject_alt_name_count); + if (result != TSI_OK) break; + } + } while (0); + + if (subject_alt_names != NULL) { + sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); + } + if (result != TSI_OK) tsi_peer_destruct(peer); + return result; +} + +/* Logs the SSL error stack. */ +static void log_ssl_error_stack(void) { + unsigned long err; + while ((err = ERR_get_error()) != 0) { + char details[256]; + ERR_error_string_n((uint32_t)err, details, sizeof(details)); + gpr_log(GPR_ERROR, "%s", details); + } +} + +/* Performs an SSL_read and handle errors. */ +static tsi_result do_ssl_read(SSL *ssl, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { + int read_from_ssl; + GPR_ASSERT(*unprotected_bytes_size <= INT_MAX); + read_from_ssl = + SSL_read(ssl, unprotected_bytes, (int)*unprotected_bytes_size); + if (read_from_ssl <= 0) { + read_from_ssl = SSL_get_error(ssl, read_from_ssl); + switch (read_from_ssl) { + case SSL_ERROR_ZERO_RETURN: /* Received a close_notify alert. */ + case SSL_ERROR_WANT_READ: /* We need more data to finish the frame. */ + *unprotected_bytes_size = 0; + return TSI_OK; + case SSL_ERROR_WANT_WRITE: + gpr_log( + GPR_ERROR, + "Peer tried to renegotiate SSL connection. This is unsupported."); + return TSI_UNIMPLEMENTED; + case SSL_ERROR_SSL: + gpr_log(GPR_ERROR, "Corruption detected."); + log_ssl_error_stack(); + return TSI_DATA_CORRUPTED; + default: + gpr_log(GPR_ERROR, "SSL_read failed with error %s.", + ssl_error_string(read_from_ssl)); + return TSI_PROTOCOL_FAILURE; + } + } + *unprotected_bytes_size = (size_t)read_from_ssl; + return TSI_OK; +} + +/* Performs an SSL_write and handle errors. */ +static tsi_result do_ssl_write(SSL *ssl, unsigned char *unprotected_bytes, + size_t unprotected_bytes_size) { + int ssl_write_result; + GPR_ASSERT(unprotected_bytes_size <= INT_MAX); + ssl_write_result = + SSL_write(ssl, unprotected_bytes, (int)unprotected_bytes_size); + if (ssl_write_result < 0) { + ssl_write_result = SSL_get_error(ssl, ssl_write_result); + if (ssl_write_result == SSL_ERROR_WANT_READ) { + gpr_log(GPR_ERROR, + "Peer tried to renegotiate SSL connection. This is unsupported."); + return TSI_UNIMPLEMENTED; + } else { + gpr_log(GPR_ERROR, "SSL_write failed with error %s.", + ssl_error_string(ssl_write_result)); + return TSI_INTERNAL_ERROR; + } + } + return TSI_OK; +} + +/* Loads an in-memory PEM certificate chain into the SSL context. */ +static tsi_result ssl_ctx_use_certificate_chain(SSL_CTX *context, + const char *pem_cert_chain, + size_t pem_cert_chain_size) { + tsi_result result = TSI_OK; + X509 *certificate = NULL; + BIO *pem; + GPR_ASSERT(pem_cert_chain_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_cert_chain, (int)pem_cert_chain_size); + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + + do { + certificate = PEM_read_bio_X509_AUX(pem, NULL, NULL, (void *)""); + if (certificate == NULL) { + result = TSI_INVALID_ARGUMENT; + break; + } + if (!SSL_CTX_use_certificate(context, certificate)) { + result = TSI_INVALID_ARGUMENT; + break; + } + while (1) { + X509 *certificate_authority = + PEM_read_bio_X509(pem, NULL, NULL, (void *)""); + if (certificate_authority == NULL) { + ERR_clear_error(); + break; /* Done reading. */ + } + if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) { + X509_free(certificate_authority); + result = TSI_INVALID_ARGUMENT; + break; + } + /* We don't need to free certificate_authority as its ownership has been + transfered to the context. That is not the case for certificate though. + */ + } + } while (0); + + if (certificate != NULL) X509_free(certificate); + BIO_free(pem); + return result; +} + +/* Loads an in-memory PEM private key into the SSL context. */ +static tsi_result ssl_ctx_use_private_key(SSL_CTX *context, const char *pem_key, + size_t pem_key_size) { + tsi_result result = TSI_OK; + EVP_PKEY *private_key = NULL; + BIO *pem; + GPR_ASSERT(pem_key_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_key, (int)pem_key_size); + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + do { + private_key = PEM_read_bio_PrivateKey(pem, NULL, NULL, (void *)""); + if (private_key == NULL) { + result = TSI_INVALID_ARGUMENT; + break; + } + if (!SSL_CTX_use_PrivateKey(context, private_key)) { + result = TSI_INVALID_ARGUMENT; + break; + } + } while (0); + if (private_key != NULL) EVP_PKEY_free(private_key); + BIO_free(pem); + return result; +} + +/* Loads in-memory PEM verification certs into the SSL context and optionally + returns the verification cert names (root_names can be NULL). */ +static tsi_result ssl_ctx_load_verification_certs(SSL_CTX *context, + const char *pem_roots, + size_t pem_roots_size, + STACK_OF(X509_NAME) * + *root_names) { + tsi_result result = TSI_OK; + size_t num_roots = 0; + X509 *root = NULL; + X509_NAME *root_name = NULL; + BIO *pem; + X509_STORE *root_store; + GPR_ASSERT(pem_roots_size <= INT_MAX); + pem = BIO_new_mem_buf((void *)pem_roots, (int)pem_roots_size); + root_store = SSL_CTX_get_cert_store(context); + if (root_store == NULL) return TSI_INVALID_ARGUMENT; + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + if (root_names != NULL) { + *root_names = sk_X509_NAME_new_null(); + if (*root_names == NULL) return TSI_OUT_OF_RESOURCES; + } + + while (1) { + root = PEM_read_bio_X509_AUX(pem, NULL, NULL, (void *)""); + if (root == NULL) { + ERR_clear_error(); + break; /* We're at the end of stream. */ + } + if (root_names != NULL) { + root_name = X509_get_subject_name(root); + if (root_name == NULL) { + gpr_log(GPR_ERROR, "Could not get name from root certificate."); + result = TSI_INVALID_ARGUMENT; + break; + } + root_name = X509_NAME_dup(root_name); + if (root_name == NULL) { + result = TSI_OUT_OF_RESOURCES; + break; + } + sk_X509_NAME_push(*root_names, root_name); + root_name = NULL; + } + if (!X509_STORE_add_cert(root_store, root)) { + gpr_log(GPR_ERROR, "Could not add root certificate to ssl context."); + result = TSI_INTERNAL_ERROR; + break; + } + X509_free(root); + num_roots++; + } + + if (num_roots == 0) { + gpr_log(GPR_ERROR, "Could not load any root certificate."); + result = TSI_INVALID_ARGUMENT; + } + + if (result != TSI_OK) { + if (root != NULL) X509_free(root); + if (root_names != NULL) { + sk_X509_NAME_pop_free(*root_names, X509_NAME_free); + *root_names = NULL; + if (root_name != NULL) X509_NAME_free(root_name); + } + } + BIO_free(pem); + return result; +} + +/* Populates the SSL context with a private key and a cert chain, and sets the + cipher list and the ephemeral ECDH key. */ +static tsi_result populate_ssl_context( + SSL_CTX *context, const tsi_ssl_pem_key_cert_pair *key_cert_pair, + const char *cipher_list) { + tsi_result result = TSI_OK; + if (key_cert_pair != NULL) { + if (key_cert_pair->cert_chain != NULL) { + result = ssl_ctx_use_certificate_chain(context, key_cert_pair->cert_chain, + strlen(key_cert_pair->cert_chain)); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Invalid cert chain file."); + return result; + } + } + if (key_cert_pair->private_key != NULL) { + result = ssl_ctx_use_private_key(context, key_cert_pair->private_key, + strlen(key_cert_pair->private_key)); + if (result != TSI_OK || !SSL_CTX_check_private_key(context)) { + gpr_log(GPR_ERROR, "Invalid private key."); + return result != TSI_OK ? result : TSI_INVALID_ARGUMENT; + } + } + } + if ((cipher_list != NULL) && !SSL_CTX_set_cipher_list(context, cipher_list)) { + gpr_log(GPR_ERROR, "Invalid cipher list: %s.", cipher_list); + return TSI_INVALID_ARGUMENT; + } + { + EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) { + gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key."); + EC_KEY_free(ecdh); + return TSI_INTERNAL_ERROR; + } + SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); + EC_KEY_free(ecdh); + } + return TSI_OK; +} + +/* Extracts the CN and the SANs from an X509 cert as a peer object. */ +static tsi_result extract_x509_subject_names_from_pem_cert(const char *pem_cert, + tsi_peer *peer) { + tsi_result result = TSI_OK; + X509 *cert = NULL; + BIO *pem; + pem = BIO_new_mem_buf((void *)pem_cert, (int)strlen(pem_cert)); + if (pem == NULL) return TSI_OUT_OF_RESOURCES; + + cert = PEM_read_bio_X509(pem, NULL, NULL, (void *)""); + if (cert == NULL) { + gpr_log(GPR_ERROR, "Invalid certificate"); + result = TSI_INVALID_ARGUMENT; + } else { + result = peer_from_x509(cert, 0, peer); + } + if (cert != NULL) X509_free(cert); + BIO_free(pem); + return result; +} + +/* Builds the alpn protocol name list according to rfc 7301. */ +static tsi_result build_alpn_protocol_name_list( + const char **alpn_protocols, uint16_t num_alpn_protocols, + unsigned char **protocol_name_list, size_t *protocol_name_list_length) { + uint16_t i; + unsigned char *current; + *protocol_name_list = NULL; + *protocol_name_list_length = 0; + if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; + for (i = 0; i < num_alpn_protocols; i++) { + size_t length = alpn_protocols[i] == NULL ? 0 : strlen(alpn_protocols[i]); + if (length == 0 || length > 255) { + gpr_log(GPR_ERROR, "Invalid protocol name length: %d.", (int)length); + return TSI_INVALID_ARGUMENT; + } + *protocol_name_list_length += length + 1; + } + *protocol_name_list = (unsigned char *)gpr_malloc(*protocol_name_list_length); + if (*protocol_name_list == NULL) return TSI_OUT_OF_RESOURCES; + current = *protocol_name_list; + for (i = 0; i < num_alpn_protocols; i++) { + size_t length = strlen(alpn_protocols[i]); + *(current++) = (uint8_t)length; /* max checked above. */ + memcpy(current, alpn_protocols[i], length); + current += length; + } + /* Safety check. */ + if ((current < *protocol_name_list) || + ((uintptr_t)(current - *protocol_name_list) != + *protocol_name_list_length)) { + return TSI_INTERNAL_ERROR; + } + return TSI_OK; +} + +// The verification callback is used for clients that don't really care about +// the server's certificate, but we need to pull it anyway, in case a higher +// layer wants to look at it. In this case the verification may fail, but +// we don't really care. +static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX *ctx) { + return 1; +} + +/* --- tsi_frame_protector methods implementation. ---*/ + +static tsi_result ssl_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size) { + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; + int read_from_ssl; + size_t available; + tsi_result result = TSI_OK; + + /* First see if we have some pending data in the SSL BIO. */ + int pending_in_ssl = (int)BIO_pending(impl->from_ssl); + if (pending_in_ssl > 0) { + *unprotected_bytes_size = 0; + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); + read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, + (int)*protected_output_frames_size); + if (read_from_ssl < 0) { + gpr_log(GPR_ERROR, + "Could not read from BIO even though some data is pending"); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = (size_t)read_from_ssl; + return TSI_OK; + } + + /* Now see if we can send a complete frame. */ + available = impl->buffer_size - impl->buffer_offset; + if (available > *unprotected_bytes_size) { + /* If we cannot, just copy the data in our internal buffer. */ + memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, + *unprotected_bytes_size); + impl->buffer_offset += *unprotected_bytes_size; + *protected_output_frames_size = 0; + return TSI_OK; + } + + /* If we can, prepare the buffer, send it to SSL_write and read. */ + memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, available); + result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size); + if (result != TSI_OK) return result; + + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); + read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, + (int)*protected_output_frames_size); + if (read_from_ssl < 0) { + gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = (size_t)read_from_ssl; + *unprotected_bytes_size = available; + impl->buffer_offset = 0; + return TSI_OK; +} + +static tsi_result ssl_protector_protect_flush( + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size) { + tsi_result result = TSI_OK; + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; + int read_from_ssl = 0; + int pending; + + if (impl->buffer_offset != 0) { + result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset); + if (result != TSI_OK) return result; + impl->buffer_offset = 0; + } + + pending = (int)BIO_pending(impl->from_ssl); + GPR_ASSERT(pending >= 0); + *still_pending_size = (size_t)pending; + if (*still_pending_size == 0) return TSI_OK; + + GPR_ASSERT(*protected_output_frames_size <= INT_MAX); + read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames, + (int)*protected_output_frames_size); + if (read_from_ssl <= 0) { + gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); + return TSI_INTERNAL_ERROR; + } + *protected_output_frames_size = (size_t)read_from_ssl; + pending = (int)BIO_pending(impl->from_ssl); + GPR_ASSERT(pending >= 0); + *still_pending_size = (size_t)pending; + return TSI_OK; +} + +static tsi_result ssl_protector_unprotect( + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { + tsi_result result = TSI_OK; + int written_into_ssl = 0; + size_t output_bytes_size = *unprotected_bytes_size; + size_t output_bytes_offset = 0; + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; + + /* First, try to read remaining data from ssl. */ + result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); + if (result != TSI_OK) return result; + if (*unprotected_bytes_size == output_bytes_size) { + /* We have read everything we could and cannot process any more input. */ + *protected_frames_bytes_size = 0; + return TSI_OK; + } + output_bytes_offset = *unprotected_bytes_size; + unprotected_bytes += output_bytes_offset; + *unprotected_bytes_size = output_bytes_size - output_bytes_offset; + + /* Then, try to write some data to ssl. */ + GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX); + written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes, + (int)*protected_frames_bytes_size); + if (written_into_ssl < 0) { + gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d", + written_into_ssl); + return TSI_INTERNAL_ERROR; + } + *protected_frames_bytes_size = (size_t)written_into_ssl; + + /* Now try to read some data again. */ + result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); + if (result == TSI_OK) { + /* Don't forget to output the total number of bytes read. */ + *unprotected_bytes_size += output_bytes_offset; + } + return result; +} + +static void ssl_protector_destroy(tsi_frame_protector *self) { + tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self; + if (impl->buffer != NULL) gpr_free(impl->buffer); + if (impl->ssl != NULL) SSL_free(impl->ssl); + gpr_free(self); +} + +static const tsi_frame_protector_vtable frame_protector_vtable = { + ssl_protector_protect, ssl_protector_protect_flush, ssl_protector_unprotect, + ssl_protector_destroy, +}; + +/* --- tsi_server_handshaker_factory methods implementation. --- */ + +static void tsi_ssl_handshaker_factory_destroy( + tsi_ssl_handshaker_factory *self) { + if (self == NULL) return; + + if (self->vtable != NULL && self->vtable->destroy != NULL) { + self->vtable->destroy(self); + } + /* Note, we don't free(self) here because this object is always directly + * embedded in another object. If tsi_ssl_handshaker_factory_init allocates + * any memory, it should be free'd here. */ +} + +static tsi_ssl_handshaker_factory *tsi_ssl_handshaker_factory_ref( + tsi_ssl_handshaker_factory *self) { + if (self == NULL) return NULL; + gpr_refn(&self->refcount, 1); + return self; +} + +static void tsi_ssl_handshaker_factory_unref(tsi_ssl_handshaker_factory *self) { + if (self == NULL) return; + + if (gpr_unref(&self->refcount)) { + tsi_ssl_handshaker_factory_destroy(self); + } +} + +static tsi_ssl_handshaker_factory_vtable handshaker_factory_vtable = {NULL}; + +/* Initializes a tsi_ssl_handshaker_factory object. Caller is responsible for + * allocating memory for the factory. */ +static void tsi_ssl_handshaker_factory_init( + tsi_ssl_handshaker_factory *factory) { + GPR_ASSERT(factory != NULL); + + factory->vtable = &handshaker_factory_vtable; + gpr_ref_init(&factory->refcount, 1); +} + +/* --- tsi_handshaker methods implementation. ---*/ + +static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + int bytes_read_from_ssl = 0; + if (bytes == NULL || bytes_size == NULL || *bytes_size == 0 || + *bytes_size > INT_MAX) { + return TSI_INVALID_ARGUMENT; + } + GPR_ASSERT(*bytes_size <= INT_MAX); + bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, (int)*bytes_size); + if (bytes_read_from_ssl < 0) { + *bytes_size = 0; + if (!BIO_should_retry(impl->from_ssl)) { + impl->result = TSI_INTERNAL_ERROR; + return impl->result; + } else { + return TSI_OK; + } + } + *bytes_size = (size_t)bytes_read_from_ssl; + return BIO_pending(impl->from_ssl) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA; +} + +static tsi_result ssl_handshaker_get_result(tsi_handshaker *self) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) && + SSL_is_init_finished(impl->ssl)) { + impl->result = TSI_OK; + } + return impl->result; +} + +static tsi_result ssl_handshaker_process_bytes_from_peer( + tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + int bytes_written_into_ssl_size = 0; + if (bytes == NULL || bytes_size == 0 || *bytes_size > INT_MAX) { + return TSI_INVALID_ARGUMENT; + } + GPR_ASSERT(*bytes_size <= INT_MAX); + bytes_written_into_ssl_size = + BIO_write(impl->into_ssl, bytes, (int)*bytes_size); + if (bytes_written_into_ssl_size < 0) { + gpr_log(GPR_ERROR, "Could not write to memory BIO."); + impl->result = TSI_INTERNAL_ERROR; + return impl->result; + } + *bytes_size = (size_t)bytes_written_into_ssl_size; + + if (!tsi_handshaker_is_in_progress(self)) { + impl->result = TSI_OK; + return impl->result; + } else { + /* Get ready to get some bytes from SSL. */ + int ssl_result = SSL_do_handshake(impl->ssl); + ssl_result = SSL_get_error(impl->ssl, ssl_result); + switch (ssl_result) { + case SSL_ERROR_WANT_READ: + if (BIO_pending(impl->from_ssl) == 0) { + /* We need more data. */ + return TSI_INCOMPLETE_DATA; + } else { + return TSI_OK; + } + case SSL_ERROR_NONE: + return TSI_OK; + default: { + char err_str[256]; + ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); + gpr_log(GPR_ERROR, "Handshake failed with fatal error %s: %s.", + ssl_error_string(ssl_result), err_str); + impl->result = TSI_PROTOCOL_FAILURE; + return impl->result; + } + } + } +} + +static tsi_result ssl_handshaker_extract_peer(tsi_handshaker *self, + tsi_peer *peer) { + tsi_result result = TSI_OK; + const unsigned char *alpn_selected = NULL; + unsigned int alpn_selected_len; + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + X509 *peer_cert = SSL_get_peer_certificate(impl->ssl); + if (peer_cert != NULL) { + result = peer_from_x509(peer_cert, 1, peer); + X509_free(peer_cert); + if (result != TSI_OK) return result; + } +#if TSI_OPENSSL_ALPN_SUPPORT + SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len); +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + if (alpn_selected == NULL) { + /* Try npn. */ + SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected, + &alpn_selected_len); + } + if (alpn_selected != NULL) { + size_t i; + tsi_peer_property *new_properties = (tsi_peer_property *)gpr_zalloc( + sizeof(*new_properties) * (peer->property_count + 1)); + for (i = 0; i < peer->property_count; i++) { + new_properties[i] = peer->properties[i]; + } + result = tsi_construct_string_peer_property( + TSI_SSL_ALPN_SELECTED_PROTOCOL, (const char *)alpn_selected, + alpn_selected_len, &new_properties[peer->property_count]); + if (result != TSI_OK) { + gpr_free(new_properties); + return result; + } + if (peer->properties != NULL) gpr_free(peer->properties); + peer->property_count++; + peer->properties = new_properties; + } + return result; +} + +static tsi_result ssl_handshaker_create_frame_protector( + tsi_handshaker *self, size_t *max_output_protected_frame_size, + tsi_frame_protector **protector) { + size_t actual_max_output_protected_frame_size = + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + tsi_ssl_frame_protector *protector_impl = + (tsi_ssl_frame_protector *)gpr_zalloc(sizeof(*protector_impl)); + + if (max_output_protected_frame_size != NULL) { + if (*max_output_protected_frame_size > + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND) { + *max_output_protected_frame_size = + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; + } else if (*max_output_protected_frame_size < + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND) { + *max_output_protected_frame_size = + TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND; + } + actual_max_output_protected_frame_size = *max_output_protected_frame_size; + } + protector_impl->buffer_size = + actual_max_output_protected_frame_size - TSI_SSL_MAX_PROTECTION_OVERHEAD; + protector_impl->buffer = + (unsigned char *)gpr_malloc(protector_impl->buffer_size); + if (protector_impl->buffer == NULL) { + gpr_log(GPR_ERROR, + "Could not allocated buffer for tsi_ssl_frame_protector."); + gpr_free(protector_impl); + return TSI_INTERNAL_ERROR; + } + + /* Transfer ownership of ssl to the frame protector. It is OK as the caller + * cannot call anything else but destroy on the handshaker after this call. */ + protector_impl->ssl = impl->ssl; + impl->ssl = NULL; + protector_impl->into_ssl = impl->into_ssl; + protector_impl->from_ssl = impl->from_ssl; + + protector_impl->base.vtable = &frame_protector_vtable; + *protector = &protector_impl->base; + return TSI_OK; +} + +static void ssl_handshaker_destroy(tsi_handshaker *self) { + tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self; + SSL_free(impl->ssl); /* The BIO objects are owned by ssl */ + tsi_ssl_handshaker_factory_unref(impl->factory_ref); + gpr_free(impl); +} + +static const tsi_handshaker_vtable handshaker_vtable = { + ssl_handshaker_get_bytes_to_send_to_peer, + ssl_handshaker_process_bytes_from_peer, + ssl_handshaker_get_result, + ssl_handshaker_extract_peer, + ssl_handshaker_create_frame_protector, + ssl_handshaker_destroy, + NULL, +}; + +/* --- tsi_ssl_handshaker_factory common methods. --- */ + +static tsi_result create_tsi_ssl_handshaker(SSL_CTX *ctx, int is_client, + const char *server_name_indication, + tsi_ssl_handshaker_factory *factory, + tsi_handshaker **handshaker) { + SSL *ssl = SSL_new(ctx); + BIO *into_ssl = NULL; + BIO *from_ssl = NULL; + tsi_ssl_handshaker *impl = NULL; + *handshaker = NULL; + if (ctx == NULL) { + gpr_log(GPR_ERROR, "SSL Context is null. Should never happen."); + return TSI_INTERNAL_ERROR; + } + if (ssl == NULL) { + return TSI_OUT_OF_RESOURCES; + } + SSL_set_info_callback(ssl, ssl_info_callback); + + into_ssl = BIO_new(BIO_s_mem()); + from_ssl = BIO_new(BIO_s_mem()); + if (into_ssl == NULL || from_ssl == NULL) { + gpr_log(GPR_ERROR, "BIO_new failed."); + SSL_free(ssl); + if (into_ssl != NULL) BIO_free(into_ssl); + if (from_ssl != NULL) BIO_free(into_ssl); + return TSI_OUT_OF_RESOURCES; + } + SSL_set_bio(ssl, into_ssl, from_ssl); + if (is_client) { + int ssl_result; + SSL_set_connect_state(ssl); + if (server_name_indication != NULL) { + if (!SSL_set_tlsext_host_name(ssl, server_name_indication)) { + gpr_log(GPR_ERROR, "Invalid server name indication %s.", + server_name_indication); + SSL_free(ssl); + return TSI_INTERNAL_ERROR; + } + } + ssl_result = SSL_do_handshake(ssl); + ssl_result = SSL_get_error(ssl, ssl_result); + if (ssl_result != SSL_ERROR_WANT_READ) { + gpr_log(GPR_ERROR, + "Unexpected error received from first SSL_do_handshake call: %s", + ssl_error_string(ssl_result)); + SSL_free(ssl); + return TSI_INTERNAL_ERROR; + } + } else { + SSL_set_accept_state(ssl); + } + + impl = (tsi_ssl_handshaker *)gpr_zalloc(sizeof(*impl)); + impl->ssl = ssl; + impl->into_ssl = into_ssl; + impl->from_ssl = from_ssl; + impl->result = TSI_HANDSHAKE_IN_PROGRESS; + impl->base.vtable = &handshaker_vtable; + impl->factory_ref = tsi_ssl_handshaker_factory_ref(factory); + + *handshaker = &impl->base; + return TSI_OK; +} + +static int select_protocol_list(const unsigned char **out, + unsigned char *outlen, + const unsigned char *client_list, + size_t client_list_len, + const unsigned char *server_list, + size_t server_list_len) { + const unsigned char *client_current = client_list; + while ((unsigned int)(client_current - client_list) < client_list_len) { + unsigned char client_current_len = *(client_current++); + const unsigned char *server_current = server_list; + while ((server_current >= server_list) && + (uintptr_t)(server_current - server_list) < server_list_len) { + unsigned char server_current_len = *(server_current++); + if ((client_current_len == server_current_len) && + !memcmp(client_current, server_current, server_current_len)) { + *out = server_current; + *outlen = server_current_len; + return SSL_TLSEXT_ERR_OK; + } + server_current += server_current_len; + } + client_current += client_current_len; + } + return SSL_TLSEXT_ERR_NOACK; +} + +/* --- tsi_ssl_client_handshaker_factory methods implementation. --- */ + +tsi_result tsi_ssl_client_handshaker_factory_create_handshaker( + tsi_ssl_client_handshaker_factory *self, const char *server_name_indication, + tsi_handshaker **handshaker) { + return create_tsi_ssl_handshaker(self->ssl_context, 1, server_name_indication, + &self->base, handshaker); +} + +void tsi_ssl_client_handshaker_factory_unref( + tsi_ssl_client_handshaker_factory *self) { + if (self == NULL) return; + tsi_ssl_handshaker_factory_unref(&self->base); +} + +static void tsi_ssl_client_handshaker_factory_destroy( + tsi_ssl_handshaker_factory *factory) { + if (factory == NULL) return; + tsi_ssl_client_handshaker_factory *self = + (tsi_ssl_client_handshaker_factory *)factory; + if (self->ssl_context != NULL) SSL_CTX_free(self->ssl_context); + if (self->alpn_protocol_list != NULL) gpr_free(self->alpn_protocol_list); + gpr_free(self); +} + +static int client_handshaker_factory_npn_callback(SSL *ssl, unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg) { + tsi_ssl_client_handshaker_factory *factory = + (tsi_ssl_client_handshaker_factory *)arg; + return select_protocol_list((const unsigned char **)out, outlen, + factory->alpn_protocol_list, + factory->alpn_protocol_list_length, in, inlen); +} + +/* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ + +tsi_result tsi_ssl_server_handshaker_factory_create_handshaker( + tsi_ssl_server_handshaker_factory *self, tsi_handshaker **handshaker) { + if (self->ssl_context_count == 0) return TSI_INVALID_ARGUMENT; + /* Create the handshaker with the first context. We will switch if needed + because of SNI in ssl_server_handshaker_factory_servername_callback. */ + return create_tsi_ssl_handshaker(self->ssl_contexts[0], 0, NULL, &self->base, + handshaker); +} + +void tsi_ssl_server_handshaker_factory_unref( + tsi_ssl_server_handshaker_factory *self) { + if (self == NULL) return; + tsi_ssl_handshaker_factory_unref(&self->base); +} + +static void tsi_ssl_server_handshaker_factory_destroy( + tsi_ssl_handshaker_factory *factory) { + if (factory == NULL) return; + tsi_ssl_server_handshaker_factory *self = + (tsi_ssl_server_handshaker_factory *)factory; + size_t i; + for (i = 0; i < self->ssl_context_count; i++) { + if (self->ssl_contexts[i] != NULL) { + SSL_CTX_free(self->ssl_contexts[i]); + tsi_peer_destruct(&self->ssl_context_x509_subject_names[i]); + } + } + if (self->ssl_contexts != NULL) gpr_free(self->ssl_contexts); + if (self->ssl_context_x509_subject_names != NULL) { + gpr_free(self->ssl_context_x509_subject_names); + } + if (self->alpn_protocol_list != NULL) gpr_free(self->alpn_protocol_list); + gpr_free(self); +} + +static int does_entry_match_name(const char *entry, size_t entry_length, + const char *name) { + const char *dot; + const char *name_subdomain = NULL; + size_t name_length = strlen(name); + size_t name_subdomain_length; + if (entry_length == 0) return 0; + + /* Take care of '.' terminations. */ + if (name[name_length - 1] == '.') { + name_length--; + } + if (entry[entry_length - 1] == '.') { + entry_length--; + if (entry_length == 0) return 0; + } + + if ((name_length == entry_length) && + strncmp(name, entry, entry_length) == 0) { + return 1; /* Perfect match. */ + } + if (entry[0] != '*') return 0; + + /* Wildchar subdomain matching. */ + if (entry_length < 3 || entry[1] != '.') { /* At least *.x */ + gpr_log(GPR_ERROR, "Invalid wildchar entry."); + return 0; + } + name_subdomain = strchr(name, '.'); + if (name_subdomain == NULL) return 0; + name_subdomain_length = strlen(name_subdomain); + if (name_subdomain_length < 2) return 0; + name_subdomain++; /* Starts after the dot. */ + name_subdomain_length--; + entry += 2; /* Remove *. */ + entry_length -= 2; + dot = strchr(name_subdomain, '.'); + if ((dot == NULL) || (dot == &name_subdomain[name_subdomain_length - 1])) { + gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain); + return 0; + } + if (name_subdomain[name_subdomain_length - 1] == '.') { + name_subdomain_length--; + } + return ((entry_length > 0) && (name_subdomain_length == entry_length) && + strncmp(entry, name_subdomain, entry_length) == 0); +} + +static int ssl_server_handshaker_factory_servername_callback(SSL *ssl, int *ap, + void *arg) { + tsi_ssl_server_handshaker_factory *impl = + (tsi_ssl_server_handshaker_factory *)arg; + size_t i = 0; + const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (servername == NULL || strlen(servername) == 0) { + return SSL_TLSEXT_ERR_NOACK; + } + + for (i = 0; i < impl->ssl_context_count; i++) { + if (tsi_ssl_peer_matches_name(&impl->ssl_context_x509_subject_names[i], + servername)) { + SSL_set_SSL_CTX(ssl, impl->ssl_contexts[i]); + return SSL_TLSEXT_ERR_OK; + } + } + gpr_log(GPR_ERROR, "No match found for server name: %s.", servername); + return SSL_TLSEXT_ERR_ALERT_WARNING; +} + +#if TSI_OPENSSL_ALPN_SUPPORT +static int server_handshaker_factory_alpn_callback( + SSL *ssl, const unsigned char **out, unsigned char *outlen, + const unsigned char *in, unsigned int inlen, void *arg) { + tsi_ssl_server_handshaker_factory *factory = + (tsi_ssl_server_handshaker_factory *)arg; + return select_protocol_list(out, outlen, in, inlen, + factory->alpn_protocol_list, + factory->alpn_protocol_list_length); +} +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + +static int server_handshaker_factory_npn_advertised_callback( + SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) { + tsi_ssl_server_handshaker_factory *factory = + (tsi_ssl_server_handshaker_factory *)arg; + *out = factory->alpn_protocol_list; + GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX); + *outlen = (unsigned int)factory->alpn_protocol_list_length; + return SSL_TLSEXT_ERR_OK; +} + +/* --- tsi_ssl_handshaker_factory constructors. --- */ + +static tsi_ssl_handshaker_factory_vtable client_handshaker_factory_vtable = { + tsi_ssl_client_handshaker_factory_destroy}; + +tsi_result tsi_create_ssl_client_handshaker_factory( + const tsi_ssl_pem_key_cert_pair *pem_key_cert_pair, + const char *pem_root_certs, const char *cipher_suites, + const char **alpn_protocols, uint16_t num_alpn_protocols, + tsi_ssl_client_handshaker_factory **factory) { + SSL_CTX *ssl_context = NULL; + tsi_ssl_client_handshaker_factory *impl = NULL; + tsi_result result = TSI_OK; + + gpr_once_init(&init_openssl_once, init_openssl); + + if (factory == NULL) return TSI_INVALID_ARGUMENT; + *factory = NULL; + if (pem_root_certs == NULL) return TSI_INVALID_ARGUMENT; + + ssl_context = SSL_CTX_new(TLSv1_2_method()); + if (ssl_context == NULL) { + gpr_log(GPR_ERROR, "Could not create ssl context."); + return TSI_INVALID_ARGUMENT; + } + + impl = (tsi_ssl_client_handshaker_factory *)gpr_zalloc(sizeof(*impl)); + tsi_ssl_handshaker_factory_init(&impl->base); + impl->base.vtable = &client_handshaker_factory_vtable; + + impl->ssl_context = ssl_context; + + do { + result = + populate_ssl_context(ssl_context, pem_key_cert_pair, cipher_suites); + if (result != TSI_OK) break; + result = ssl_ctx_load_verification_certs(ssl_context, pem_root_certs, + strlen(pem_root_certs), NULL); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Cannot load server root certificates."); + break; + } + + if (num_alpn_protocols != 0) { + result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, + &impl->alpn_protocol_list, + &impl->alpn_protocol_list_length); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Building alpn list failed with error %s.", + tsi_result_to_string(result)); + break; + } +#if TSI_OPENSSL_ALPN_SUPPORT + GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX); + if (SSL_CTX_set_alpn_protos( + ssl_context, impl->alpn_protocol_list, + (unsigned int)impl->alpn_protocol_list_length)) { + gpr_log(GPR_ERROR, "Could not set alpn protocol list to context."); + result = TSI_INVALID_ARGUMENT; + break; + } +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + SSL_CTX_set_next_proto_select_cb( + ssl_context, client_handshaker_factory_npn_callback, impl); + } + } while (0); + if (result != TSI_OK) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return result; + } + SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL); + /* TODO(jboeuf): Add revocation verification. */ + + *factory = impl; + return TSI_OK; +} + +static tsi_ssl_handshaker_factory_vtable server_handshaker_factory_vtable = { + tsi_ssl_server_handshaker_factory_destroy}; + +tsi_result tsi_create_ssl_server_handshaker_factory( + const tsi_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, const char *pem_client_root_certs, + int force_client_auth, const char *cipher_suites, + const char **alpn_protocols, uint16_t num_alpn_protocols, + tsi_ssl_server_handshaker_factory **factory) { + return tsi_create_ssl_server_handshaker_factory_ex( + pem_key_cert_pairs, num_key_cert_pairs, pem_client_root_certs, + force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY + : TSI_DONT_REQUEST_CLIENT_CERTIFICATE, + cipher_suites, alpn_protocols, num_alpn_protocols, factory); +} + +tsi_result tsi_create_ssl_server_handshaker_factory_ex( + const tsi_ssl_pem_key_cert_pair *pem_key_cert_pairs, + size_t num_key_cert_pairs, const char *pem_client_root_certs, + tsi_client_certificate_request_type client_certificate_request, + const char *cipher_suites, const char **alpn_protocols, + uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory **factory) { + tsi_ssl_server_handshaker_factory *impl = NULL; + tsi_result result = TSI_OK; + size_t i = 0; + + gpr_once_init(&init_openssl_once, init_openssl); + + if (factory == NULL) return TSI_INVALID_ARGUMENT; + *factory = NULL; + if (num_key_cert_pairs == 0 || pem_key_cert_pairs == NULL) { + return TSI_INVALID_ARGUMENT; + } + + impl = (tsi_ssl_server_handshaker_factory *)gpr_zalloc(sizeof(*impl)); + tsi_ssl_handshaker_factory_init(&impl->base); + impl->base.vtable = &server_handshaker_factory_vtable; + + impl->ssl_contexts = + (SSL_CTX **)gpr_zalloc(num_key_cert_pairs * sizeof(SSL_CTX *)); + impl->ssl_context_x509_subject_names = + (tsi_peer *)gpr_zalloc(num_key_cert_pairs * sizeof(tsi_peer)); + if (impl->ssl_contexts == NULL || + impl->ssl_context_x509_subject_names == NULL) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return TSI_OUT_OF_RESOURCES; + } + impl->ssl_context_count = num_key_cert_pairs; + + if (num_alpn_protocols > 0) { + result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, + &impl->alpn_protocol_list, + &impl->alpn_protocol_list_length); + if (result != TSI_OK) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return result; + } + } + + for (i = 0; i < num_key_cert_pairs; i++) { + do { + impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); + if (impl->ssl_contexts[i] == NULL) { + gpr_log(GPR_ERROR, "Could not create ssl context."); + result = TSI_OUT_OF_RESOURCES; + break; + } + result = populate_ssl_context(impl->ssl_contexts[i], + &pem_key_cert_pairs[i], cipher_suites); + if (result != TSI_OK) break; + + if (pem_client_root_certs != NULL) { + STACK_OF(X509_NAME) *root_names = NULL; + result = ssl_ctx_load_verification_certs( + impl->ssl_contexts[i], pem_client_root_certs, + strlen(pem_client_root_certs), &root_names); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Invalid verification certs."); + break; + } + SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names); + switch (client_certificate_request) { + case TSI_DONT_REQUEST_CLIENT_CERTIFICATE: + SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, NULL); + break; + case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, + NullVerifyCallback); + break; + case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: + SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, NULL); + break; + case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: + SSL_CTX_set_verify( + impl->ssl_contexts[i], + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + NullVerifyCallback); + break; + case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: + SSL_CTX_set_verify( + impl->ssl_contexts[i], + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + break; + } + /* TODO(jboeuf): Add revocation verification. */ + } + + result = extract_x509_subject_names_from_pem_cert( + pem_key_cert_pairs[i].cert_chain, + &impl->ssl_context_x509_subject_names[i]); + if (result != TSI_OK) break; + + SSL_CTX_set_tlsext_servername_callback( + impl->ssl_contexts[i], + ssl_server_handshaker_factory_servername_callback); + SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl); +#if TSI_OPENSSL_ALPN_SUPPORT + SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i], + server_handshaker_factory_alpn_callback, impl); +#endif /* TSI_OPENSSL_ALPN_SUPPORT */ + SSL_CTX_set_next_protos_advertised_cb( + impl->ssl_contexts[i], + server_handshaker_factory_npn_advertised_callback, impl); + } while (0); + + if (result != TSI_OK) { + tsi_ssl_handshaker_factory_unref(&impl->base); + return result; + } + } + + *factory = impl; + return TSI_OK; +} + +/* --- tsi_ssl utils. --- */ + +int tsi_ssl_peer_matches_name(const tsi_peer *peer, const char *name) { + size_t i = 0; + size_t san_count = 0; + const tsi_peer_property *cn_property = NULL; + int like_ip = looks_like_ip_address(name); + + /* Check the SAN first. */ + for (i = 0; i < peer->property_count; i++) { + const tsi_peer_property *property = &peer->properties[i]; + if (property->name == NULL) continue; + if (strcmp(property->name, + TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { + san_count++; + + if (!like_ip && does_entry_match_name(property->value.data, + property->value.length, name)) { + return 1; + } else if (like_ip && + strncmp(name, property->value.data, property->value.length) == + 0 && + strlen(name) == property->value.length) { + /* IP Addresses are exact matches only. */ + return 1; + } + } else if (strcmp(property->name, + TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { + cn_property = property; + } + } + + /* If there's no SAN, try the CN, but only if its not like an IP Address */ + if (san_count == 0 && cn_property != NULL && !like_ip) { + if (does_entry_match_name(cn_property->value.data, + cn_property->value.length, name)) { + return 1; + } + } + + return 0; /* Not found. */ +} + +/* --- Testing support. --- */ +const tsi_ssl_handshaker_factory_vtable *tsi_ssl_handshaker_factory_swap_vtable( + tsi_ssl_handshaker_factory *factory, + tsi_ssl_handshaker_factory_vtable *new_vtable) { + GPR_ASSERT(factory != NULL); + GPR_ASSERT(factory->vtable != NULL); + + const tsi_ssl_handshaker_factory_vtable *orig_vtable = factory->vtable; + factory->vtable = new_vtable; + return orig_vtable; +} diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c deleted file mode 100644 index 21bd8eba78..0000000000 --- a/src/core/tsi/transport_security.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/tsi/transport_security.h" - -#include -#include - -#include -#include - -/* --- Tracing. --- */ - -grpc_tracer_flag tsi_tracing_enabled = GRPC_TRACER_INITIALIZER(false, "tsi"); - -/* --- tsi_result common implementation. --- */ - -const char *tsi_result_to_string(tsi_result result) { - switch (result) { - case TSI_OK: - return "TSI_OK"; - case TSI_UNKNOWN_ERROR: - return "TSI_UNKNOWN_ERROR"; - case TSI_INVALID_ARGUMENT: - return "TSI_INVALID_ARGUMENT"; - case TSI_PERMISSION_DENIED: - return "TSI_PERMISSION_DENIED"; - case TSI_INCOMPLETE_DATA: - return "TSI_INCOMPLETE_DATA"; - case TSI_FAILED_PRECONDITION: - return "TSI_FAILED_PRECONDITION"; - case TSI_UNIMPLEMENTED: - return "TSI_UNIMPLEMENTED"; - case TSI_INTERNAL_ERROR: - return "TSI_INTERNAL_ERROR"; - case TSI_DATA_CORRUPTED: - return "TSI_DATA_CORRUPTED"; - case TSI_NOT_FOUND: - return "TSI_NOT_FOUND"; - case TSI_PROTOCOL_FAILURE: - return "TSI_PROTOCOL_FAILURE"; - case TSI_HANDSHAKE_IN_PROGRESS: - return "TSI_HANDSHAKE_IN_PROGRESS"; - case TSI_OUT_OF_RESOURCES: - return "TSI_OUT_OF_RESOURCES"; - case TSI_ASYNC: - return "TSI_ASYNC"; - default: - return "UNKNOWN"; - } -} - -/* --- tsi_frame_protector common implementation. --- - - Calls specific implementation after state/input validation. */ - -tsi_result tsi_frame_protector_protect(tsi_frame_protector *self, - const unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size, - unsigned char *protected_output_frames, - size_t *protected_output_frames_size) { - if (self == NULL || self->vtable == NULL || unprotected_bytes == NULL || - unprotected_bytes_size == NULL || protected_output_frames == NULL || - protected_output_frames_size == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->protect == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size, - protected_output_frames, - protected_output_frames_size); -} - -tsi_result tsi_frame_protector_protect_flush( - tsi_frame_protector *self, unsigned char *protected_output_frames, - size_t *protected_output_frames_size, size_t *still_pending_size) { - if (self == NULL || self->vtable == NULL || protected_output_frames == NULL || - protected_output_frames_size == NULL || still_pending_size == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->protect_flush == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->protect_flush(self, protected_output_frames, - protected_output_frames_size, - still_pending_size); -} - -tsi_result tsi_frame_protector_unprotect( - tsi_frame_protector *self, const unsigned char *protected_frames_bytes, - size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, - size_t *unprotected_bytes_size) { - if (self == NULL || self->vtable == NULL || protected_frames_bytes == NULL || - protected_frames_bytes_size == NULL || unprotected_bytes == NULL || - unprotected_bytes_size == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->unprotect == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->unprotect(self, protected_frames_bytes, - protected_frames_bytes_size, unprotected_bytes, - unprotected_bytes_size); -} - -void tsi_frame_protector_destroy(tsi_frame_protector *self) { - if (self == NULL) return; - self->vtable->destroy(self); -} - -/* --- tsi_handshaker common implementation. --- - - Calls specific implementation after state/input validation. */ - -tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, - unsigned char *bytes, - size_t *bytes_size) { - if (self == NULL || self->vtable == NULL || bytes == NULL || - bytes_size == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; - if (self->vtable->get_bytes_to_send_to_peer == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); -} - -tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self, - const unsigned char *bytes, - size_t *bytes_size) { - if (self == NULL || self->vtable == NULL || bytes == NULL || - bytes_size == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; - if (self->vtable->process_bytes_from_peer == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); -} - -tsi_result tsi_handshaker_get_result(tsi_handshaker *self) { - if (self == NULL || self->vtable == NULL) return TSI_INVALID_ARGUMENT; - if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; - if (self->vtable->get_result == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->get_result(self); -} - -tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer) { - if (self == NULL || self->vtable == NULL || peer == NULL) { - return TSI_INVALID_ARGUMENT; - } - memset(peer, 0, sizeof(tsi_peer)); - if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; - if (tsi_handshaker_get_result(self) != TSI_OK) { - return TSI_FAILED_PRECONDITION; - } - if (self->vtable->extract_peer == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->extract_peer(self, peer); -} - -tsi_result tsi_handshaker_create_frame_protector( - tsi_handshaker *self, size_t *max_protected_frame_size, - tsi_frame_protector **protector) { - tsi_result result; - if (self == NULL || self->vtable == NULL || protector == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; - if (tsi_handshaker_get_result(self) != TSI_OK) return TSI_FAILED_PRECONDITION; - if (self->vtable->create_frame_protector == NULL) return TSI_UNIMPLEMENTED; - result = self->vtable->create_frame_protector(self, max_protected_frame_size, - protector); - if (result == TSI_OK) { - self->frame_protector_created = true; - } - return result; -} - -tsi_result tsi_handshaker_next( - tsi_handshaker *self, const unsigned char *received_bytes, - size_t received_bytes_size, const unsigned char **bytes_to_send, - size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result, - tsi_handshaker_on_next_done_cb cb, void *user_data) { - if (self == NULL || self->vtable == NULL) return TSI_INVALID_ARGUMENT; - if (self->handshaker_result_created) return TSI_FAILED_PRECONDITION; - if (self->vtable->next == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->next(self, received_bytes, received_bytes_size, - bytes_to_send, bytes_to_send_size, - handshaker_result, cb, user_data); -} - -void tsi_handshaker_destroy(tsi_handshaker *self) { - if (self == NULL) return; - self->vtable->destroy(self); -} - -/* --- tsi_handshaker_result implementation. --- */ - -tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result *self, - tsi_peer *peer) { - if (self == NULL || self->vtable == NULL || peer == NULL) { - return TSI_INVALID_ARGUMENT; - } - memset(peer, 0, sizeof(tsi_peer)); - if (self->vtable->extract_peer == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->extract_peer(self, peer); -} - -tsi_result tsi_handshaker_result_create_frame_protector( - const tsi_handshaker_result *self, size_t *max_protected_frame_size, - tsi_frame_protector **protector) { - if (self == NULL || self->vtable == NULL || protector == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->create_frame_protector == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->create_frame_protector(self, max_protected_frame_size, - protector); -} - -tsi_result tsi_handshaker_result_get_unused_bytes( - const tsi_handshaker_result *self, const unsigned char **bytes, - size_t *bytes_size) { - if (self == NULL || self->vtable == NULL || bytes == NULL || - bytes_size == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->get_unused_bytes == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->get_unused_bytes(self, bytes, bytes_size); -} - -void tsi_handshaker_result_destroy(tsi_handshaker_result *self) { - if (self == NULL) return; - self->vtable->destroy(self); -} - -/* --- tsi_peer implementation. --- */ - -tsi_peer_property tsi_init_peer_property(void) { - tsi_peer_property property; - memset(&property, 0, sizeof(tsi_peer_property)); - return property; -} - -static void tsi_peer_destroy_list_property(tsi_peer_property *children, - size_t child_count) { - size_t i; - for (i = 0; i < child_count; i++) { - tsi_peer_property_destruct(&children[i]); - } - gpr_free(children); -} - -void tsi_peer_property_destruct(tsi_peer_property *property) { - if (property->name != NULL) { - gpr_free(property->name); - } - if (property->value.data != NULL) { - gpr_free(property->value.data); - } - *property = tsi_init_peer_property(); /* Reset everything to 0. */ -} - -void tsi_peer_destruct(tsi_peer *self) { - if (self == NULL) return; - if (self->properties != NULL) { - tsi_peer_destroy_list_property(self->properties, self->property_count); - self->properties = NULL; - } - self->property_count = 0; -} - -tsi_result tsi_construct_allocated_string_peer_property( - const char *name, size_t value_length, tsi_peer_property *property) { - *property = tsi_init_peer_property(); - if (name != NULL) property->name = gpr_strdup(name); - if (value_length > 0) { - property->value.data = (char *)gpr_zalloc(value_length); - property->value.length = value_length; - } - return TSI_OK; -} - -tsi_result tsi_construct_string_peer_property_from_cstring( - const char *name, const char *value, tsi_peer_property *property) { - return tsi_construct_string_peer_property(name, value, strlen(value), - property); -} - -tsi_result tsi_construct_string_peer_property(const char *name, - const char *value, - size_t value_length, - tsi_peer_property *property) { - tsi_result result = tsi_construct_allocated_string_peer_property( - name, value_length, property); - if (result != TSI_OK) return result; - if (value_length > 0) { - memcpy(property->value.data, value, value_length); - } - return TSI_OK; -} - -tsi_result tsi_construct_peer(size_t property_count, tsi_peer *peer) { - memset(peer, 0, sizeof(tsi_peer)); - if (property_count > 0) { - peer->properties = (tsi_peer_property *)gpr_zalloc( - property_count * sizeof(tsi_peer_property)); - peer->property_count = property_count; - } - return TSI_OK; -} diff --git a/src/core/tsi/transport_security.cc b/src/core/tsi/transport_security.cc new file mode 100644 index 0000000000..21bd8eba78 --- /dev/null +++ b/src/core/tsi/transport_security.cc @@ -0,0 +1,318 @@ +/* + * + * Copyright 2015 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/tsi/transport_security.h" + +#include +#include + +#include +#include + +/* --- Tracing. --- */ + +grpc_tracer_flag tsi_tracing_enabled = GRPC_TRACER_INITIALIZER(false, "tsi"); + +/* --- tsi_result common implementation. --- */ + +const char *tsi_result_to_string(tsi_result result) { + switch (result) { + case TSI_OK: + return "TSI_OK"; + case TSI_UNKNOWN_ERROR: + return "TSI_UNKNOWN_ERROR"; + case TSI_INVALID_ARGUMENT: + return "TSI_INVALID_ARGUMENT"; + case TSI_PERMISSION_DENIED: + return "TSI_PERMISSION_DENIED"; + case TSI_INCOMPLETE_DATA: + return "TSI_INCOMPLETE_DATA"; + case TSI_FAILED_PRECONDITION: + return "TSI_FAILED_PRECONDITION"; + case TSI_UNIMPLEMENTED: + return "TSI_UNIMPLEMENTED"; + case TSI_INTERNAL_ERROR: + return "TSI_INTERNAL_ERROR"; + case TSI_DATA_CORRUPTED: + return "TSI_DATA_CORRUPTED"; + case TSI_NOT_FOUND: + return "TSI_NOT_FOUND"; + case TSI_PROTOCOL_FAILURE: + return "TSI_PROTOCOL_FAILURE"; + case TSI_HANDSHAKE_IN_PROGRESS: + return "TSI_HANDSHAKE_IN_PROGRESS"; + case TSI_OUT_OF_RESOURCES: + return "TSI_OUT_OF_RESOURCES"; + case TSI_ASYNC: + return "TSI_ASYNC"; + default: + return "UNKNOWN"; + } +} + +/* --- tsi_frame_protector common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_frame_protector_protect(tsi_frame_protector *self, + const unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size, + unsigned char *protected_output_frames, + size_t *protected_output_frames_size) { + if (self == NULL || self->vtable == NULL || unprotected_bytes == NULL || + unprotected_bytes_size == NULL || protected_output_frames == NULL || + protected_output_frames_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size, + protected_output_frames, + protected_output_frames_size); +} + +tsi_result tsi_frame_protector_protect_flush( + tsi_frame_protector *self, unsigned char *protected_output_frames, + size_t *protected_output_frames_size, size_t *still_pending_size) { + if (self == NULL || self->vtable == NULL || protected_output_frames == NULL || + protected_output_frames_size == NULL || still_pending_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect_flush == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->protect_flush(self, protected_output_frames, + protected_output_frames_size, + still_pending_size); +} + +tsi_result tsi_frame_protector_unprotect( + tsi_frame_protector *self, const unsigned char *protected_frames_bytes, + size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes, + size_t *unprotected_bytes_size) { + if (self == NULL || self->vtable == NULL || protected_frames_bytes == NULL || + protected_frames_bytes_size == NULL || unprotected_bytes == NULL || + unprotected_bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->unprotect == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->unprotect(self, protected_frames_bytes, + protected_frames_bytes_size, unprotected_bytes, + unprotected_bytes_size); +} + +void tsi_frame_protector_destroy(tsi_frame_protector *self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + +/* --- tsi_handshaker common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size) { + if (self == NULL || self->vtable == NULL || bytes == NULL || + bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->vtable->get_bytes_to_send_to_peer == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size); +} + +tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self, + const unsigned char *bytes, + size_t *bytes_size) { + if (self == NULL || self->vtable == NULL || bytes == NULL || + bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->vtable->process_bytes_from_peer == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->process_bytes_from_peer(self, bytes, bytes_size); +} + +tsi_result tsi_handshaker_get_result(tsi_handshaker *self) { + if (self == NULL || self->vtable == NULL) return TSI_INVALID_ARGUMENT; + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (self->vtable->get_result == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->get_result(self); +} + +tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer) { + if (self == NULL || self->vtable == NULL || peer == NULL) { + return TSI_INVALID_ARGUMENT; + } + memset(peer, 0, sizeof(tsi_peer)); + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (tsi_handshaker_get_result(self) != TSI_OK) { + return TSI_FAILED_PRECONDITION; + } + if (self->vtable->extract_peer == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->extract_peer(self, peer); +} + +tsi_result tsi_handshaker_create_frame_protector( + tsi_handshaker *self, size_t *max_protected_frame_size, + tsi_frame_protector **protector) { + tsi_result result; + if (self == NULL || self->vtable == NULL || protector == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->frame_protector_created) return TSI_FAILED_PRECONDITION; + if (tsi_handshaker_get_result(self) != TSI_OK) return TSI_FAILED_PRECONDITION; + if (self->vtable->create_frame_protector == NULL) return TSI_UNIMPLEMENTED; + result = self->vtable->create_frame_protector(self, max_protected_frame_size, + protector); + if (result == TSI_OK) { + self->frame_protector_created = true; + } + return result; +} + +tsi_result tsi_handshaker_next( + tsi_handshaker *self, const unsigned char *received_bytes, + size_t received_bytes_size, const unsigned char **bytes_to_send, + size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result, + tsi_handshaker_on_next_done_cb cb, void *user_data) { + if (self == NULL || self->vtable == NULL) return TSI_INVALID_ARGUMENT; + if (self->handshaker_result_created) return TSI_FAILED_PRECONDITION; + if (self->vtable->next == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->next(self, received_bytes, received_bytes_size, + bytes_to_send, bytes_to_send_size, + handshaker_result, cb, user_data); +} + +void tsi_handshaker_destroy(tsi_handshaker *self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + +/* --- tsi_handshaker_result implementation. --- */ + +tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result *self, + tsi_peer *peer) { + if (self == NULL || self->vtable == NULL || peer == NULL) { + return TSI_INVALID_ARGUMENT; + } + memset(peer, 0, sizeof(tsi_peer)); + if (self->vtable->extract_peer == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->extract_peer(self, peer); +} + +tsi_result tsi_handshaker_result_create_frame_protector( + const tsi_handshaker_result *self, size_t *max_protected_frame_size, + tsi_frame_protector **protector) { + if (self == NULL || self->vtable == NULL || protector == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->create_frame_protector == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->create_frame_protector(self, max_protected_frame_size, + protector); +} + +tsi_result tsi_handshaker_result_get_unused_bytes( + const tsi_handshaker_result *self, const unsigned char **bytes, + size_t *bytes_size) { + if (self == NULL || self->vtable == NULL || bytes == NULL || + bytes_size == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->get_unused_bytes == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->get_unused_bytes(self, bytes, bytes_size); +} + +void tsi_handshaker_result_destroy(tsi_handshaker_result *self) { + if (self == NULL) return; + self->vtable->destroy(self); +} + +/* --- tsi_peer implementation. --- */ + +tsi_peer_property tsi_init_peer_property(void) { + tsi_peer_property property; + memset(&property, 0, sizeof(tsi_peer_property)); + return property; +} + +static void tsi_peer_destroy_list_property(tsi_peer_property *children, + size_t child_count) { + size_t i; + for (i = 0; i < child_count; i++) { + tsi_peer_property_destruct(&children[i]); + } + gpr_free(children); +} + +void tsi_peer_property_destruct(tsi_peer_property *property) { + if (property->name != NULL) { + gpr_free(property->name); + } + if (property->value.data != NULL) { + gpr_free(property->value.data); + } + *property = tsi_init_peer_property(); /* Reset everything to 0. */ +} + +void tsi_peer_destruct(tsi_peer *self) { + if (self == NULL) return; + if (self->properties != NULL) { + tsi_peer_destroy_list_property(self->properties, self->property_count); + self->properties = NULL; + } + self->property_count = 0; +} + +tsi_result tsi_construct_allocated_string_peer_property( + const char *name, size_t value_length, tsi_peer_property *property) { + *property = tsi_init_peer_property(); + if (name != NULL) property->name = gpr_strdup(name); + if (value_length > 0) { + property->value.data = (char *)gpr_zalloc(value_length); + property->value.length = value_length; + } + return TSI_OK; +} + +tsi_result tsi_construct_string_peer_property_from_cstring( + const char *name, const char *value, tsi_peer_property *property) { + return tsi_construct_string_peer_property(name, value, strlen(value), + property); +} + +tsi_result tsi_construct_string_peer_property(const char *name, + const char *value, + size_t value_length, + tsi_peer_property *property) { + tsi_result result = tsi_construct_allocated_string_peer_property( + name, value_length, property); + if (result != TSI_OK) return result; + if (value_length > 0) { + memcpy(property->value.data, value, value_length); + } + return TSI_OK; +} + +tsi_result tsi_construct_peer(size_t property_count, tsi_peer *peer) { + memset(peer, 0, sizeof(tsi_peer)); + if (property_count > 0) { + peer->properties = (tsi_peer_property *)gpr_zalloc( + property_count * sizeof(tsi_peer_property)); + peer->property_count = property_count; + } + return TSI_OK; +} diff --git a/src/core/tsi/transport_security_adapter.c b/src/core/tsi/transport_security_adapter.c deleted file mode 100644 index e399e42758..0000000000 --- a/src/core/tsi/transport_security_adapter.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/tsi/transport_security_adapter.h" - -#include - -#include -#include -#include "src/core/tsi/transport_security.h" - -#define TSI_ADAPTER_INITIAL_BUFFER_SIZE 256 - -/* --- tsi_adapter_handshaker_result implementation ---*/ - -typedef struct { - tsi_handshaker_result base; - tsi_handshaker *wrapped; - unsigned char *unused_bytes; - size_t unused_bytes_size; -} tsi_adapter_handshaker_result; - -static tsi_result adapter_result_extract_peer(const tsi_handshaker_result *self, - tsi_peer *peer) { - tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; - return tsi_handshaker_extract_peer(impl->wrapped, peer); -} - -static tsi_result adapter_result_create_frame_protector( - const tsi_handshaker_result *self, size_t *max_output_protected_frame_size, - tsi_frame_protector **protector) { - tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; - return tsi_handshaker_create_frame_protector( - impl->wrapped, max_output_protected_frame_size, protector); -} - -static tsi_result adapter_result_get_unused_bytes( - const tsi_handshaker_result *self, const unsigned char **bytes, - size_t *byte_size) { - tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; - *bytes = impl->unused_bytes; - *byte_size = impl->unused_bytes_size; - return TSI_OK; -} - -static void adapter_result_destroy(tsi_handshaker_result *self) { - tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; - tsi_handshaker_destroy(impl->wrapped); - gpr_free(impl->unused_bytes); - gpr_free(self); -} - -static const tsi_handshaker_result_vtable result_vtable = { - adapter_result_extract_peer, - NULL, /* create_zero_copy_grpc_protector */ - adapter_result_create_frame_protector, - adapter_result_get_unused_bytes, - adapter_result_destroy, -}; - -/* Ownership of wrapped tsi_handshaker is transferred to the result object. */ -static tsi_result tsi_adapter_create_handshaker_result( - tsi_handshaker *wrapped, const unsigned char *unused_bytes, - size_t unused_bytes_size, tsi_handshaker_result **handshaker_result) { - if (wrapped == NULL || (unused_bytes_size > 0 && unused_bytes == NULL)) { - return TSI_INVALID_ARGUMENT; - } - tsi_adapter_handshaker_result *impl = - (tsi_adapter_handshaker_result *)gpr_zalloc(sizeof(*impl)); - impl->base.vtable = &result_vtable; - impl->wrapped = wrapped; - impl->unused_bytes_size = unused_bytes_size; - if (unused_bytes_size > 0) { - impl->unused_bytes = (unsigned char *)gpr_malloc(unused_bytes_size); - memcpy(impl->unused_bytes, unused_bytes, unused_bytes_size); - } else { - impl->unused_bytes = NULL; - } - *handshaker_result = &impl->base; - return TSI_OK; -} - -/* --- tsi_adapter_handshaker implementation ---*/ - -typedef struct { - tsi_handshaker base; - tsi_handshaker *wrapped; - unsigned char *adapter_buffer; - size_t adapter_buffer_size; -} tsi_adapter_handshaker; - -static tsi_result adapter_get_bytes_to_send_to_peer(tsi_handshaker *self, - unsigned char *bytes, - size_t *bytes_size) { - return tsi_handshaker_get_bytes_to_send_to_peer( - tsi_adapter_handshaker_get_wrapped(self), bytes, bytes_size); -} - -static tsi_result adapter_process_bytes_from_peer(tsi_handshaker *self, - const unsigned char *bytes, - size_t *bytes_size) { - return tsi_handshaker_process_bytes_from_peer( - tsi_adapter_handshaker_get_wrapped(self), bytes, bytes_size); -} - -static tsi_result adapter_get_result(tsi_handshaker *self) { - return tsi_handshaker_get_result(tsi_adapter_handshaker_get_wrapped(self)); -} - -static tsi_result adapter_extract_peer(tsi_handshaker *self, tsi_peer *peer) { - return tsi_handshaker_extract_peer(tsi_adapter_handshaker_get_wrapped(self), - peer); -} - -static tsi_result adapter_create_frame_protector( - tsi_handshaker *self, size_t *max_protected_frame_size, - tsi_frame_protector **protector) { - return tsi_handshaker_create_frame_protector( - tsi_adapter_handshaker_get_wrapped(self), max_protected_frame_size, - protector); -} - -static void adapter_destroy(tsi_handshaker *self) { - tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)self; - tsi_handshaker_destroy(impl->wrapped); - gpr_free(impl->adapter_buffer); - gpr_free(self); -} - -static tsi_result adapter_next( - tsi_handshaker *self, const unsigned char *received_bytes, - size_t received_bytes_size, const unsigned char **bytes_to_send, - size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result, - tsi_handshaker_on_next_done_cb cb, void *user_data) { - /* Input sanity check. */ - if ((received_bytes_size > 0 && received_bytes == NULL) || - bytes_to_send == NULL || bytes_to_send_size == NULL || - handshaker_result == NULL) { - return TSI_INVALID_ARGUMENT; - } - - /* If there are received bytes, process them first. */ - tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)self; - tsi_result status = TSI_OK; - size_t bytes_consumed = received_bytes_size; - if (received_bytes_size > 0) { - status = tsi_handshaker_process_bytes_from_peer( - impl->wrapped, received_bytes, &bytes_consumed); - if (status != TSI_OK) return status; - } - - /* Get bytes to send to the peer, if available. */ - size_t offset = 0; - do { - size_t to_send_size = impl->adapter_buffer_size - offset; - status = tsi_handshaker_get_bytes_to_send_to_peer( - impl->wrapped, impl->adapter_buffer + offset, &to_send_size); - offset += to_send_size; - if (status == TSI_INCOMPLETE_DATA) { - impl->adapter_buffer_size *= 2; - impl->adapter_buffer = (unsigned char *)gpr_realloc( - impl->adapter_buffer, impl->adapter_buffer_size); - } - } while (status == TSI_INCOMPLETE_DATA); - if (status != TSI_OK) return status; - *bytes_to_send = impl->adapter_buffer; - *bytes_to_send_size = offset; - - /* If handshake completes, create tsi_handshaker_result. */ - if (tsi_handshaker_is_in_progress(impl->wrapped)) { - *handshaker_result = NULL; - } else { - size_t unused_bytes_size = received_bytes_size - bytes_consumed; - const unsigned char *unused_bytes = - unused_bytes_size == 0 ? NULL : received_bytes + bytes_consumed; - status = tsi_adapter_create_handshaker_result( - impl->wrapped, unused_bytes, unused_bytes_size, handshaker_result); - if (status == TSI_OK) { - impl->base.handshaker_result_created = true; - impl->wrapped = NULL; - } - } - return status; -} - -static const tsi_handshaker_vtable handshaker_vtable = { - adapter_get_bytes_to_send_to_peer, - adapter_process_bytes_from_peer, - adapter_get_result, - adapter_extract_peer, - adapter_create_frame_protector, - adapter_destroy, - adapter_next, -}; - -tsi_handshaker *tsi_create_adapter_handshaker(tsi_handshaker *wrapped) { - GPR_ASSERT(wrapped != NULL); - tsi_adapter_handshaker *impl = - (tsi_adapter_handshaker *)gpr_zalloc(sizeof(*impl)); - impl->base.vtable = &handshaker_vtable; - impl->wrapped = wrapped; - impl->adapter_buffer_size = TSI_ADAPTER_INITIAL_BUFFER_SIZE; - impl->adapter_buffer = (unsigned char *)gpr_malloc(impl->adapter_buffer_size); - return &impl->base; -} - -tsi_handshaker *tsi_adapter_handshaker_get_wrapped(tsi_handshaker *adapter) { - if (adapter == NULL) return NULL; - tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)adapter; - return impl->wrapped; -} diff --git a/src/core/tsi/transport_security_adapter.cc b/src/core/tsi/transport_security_adapter.cc new file mode 100644 index 0000000000..e399e42758 --- /dev/null +++ b/src/core/tsi/transport_security_adapter.cc @@ -0,0 +1,226 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/tsi/transport_security_adapter.h" + +#include + +#include +#include +#include "src/core/tsi/transport_security.h" + +#define TSI_ADAPTER_INITIAL_BUFFER_SIZE 256 + +/* --- tsi_adapter_handshaker_result implementation ---*/ + +typedef struct { + tsi_handshaker_result base; + tsi_handshaker *wrapped; + unsigned char *unused_bytes; + size_t unused_bytes_size; +} tsi_adapter_handshaker_result; + +static tsi_result adapter_result_extract_peer(const tsi_handshaker_result *self, + tsi_peer *peer) { + tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; + return tsi_handshaker_extract_peer(impl->wrapped, peer); +} + +static tsi_result adapter_result_create_frame_protector( + const tsi_handshaker_result *self, size_t *max_output_protected_frame_size, + tsi_frame_protector **protector) { + tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; + return tsi_handshaker_create_frame_protector( + impl->wrapped, max_output_protected_frame_size, protector); +} + +static tsi_result adapter_result_get_unused_bytes( + const tsi_handshaker_result *self, const unsigned char **bytes, + size_t *byte_size) { + tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; + *bytes = impl->unused_bytes; + *byte_size = impl->unused_bytes_size; + return TSI_OK; +} + +static void adapter_result_destroy(tsi_handshaker_result *self) { + tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self; + tsi_handshaker_destroy(impl->wrapped); + gpr_free(impl->unused_bytes); + gpr_free(self); +} + +static const tsi_handshaker_result_vtable result_vtable = { + adapter_result_extract_peer, + NULL, /* create_zero_copy_grpc_protector */ + adapter_result_create_frame_protector, + adapter_result_get_unused_bytes, + adapter_result_destroy, +}; + +/* Ownership of wrapped tsi_handshaker is transferred to the result object. */ +static tsi_result tsi_adapter_create_handshaker_result( + tsi_handshaker *wrapped, const unsigned char *unused_bytes, + size_t unused_bytes_size, tsi_handshaker_result **handshaker_result) { + if (wrapped == NULL || (unused_bytes_size > 0 && unused_bytes == NULL)) { + return TSI_INVALID_ARGUMENT; + } + tsi_adapter_handshaker_result *impl = + (tsi_adapter_handshaker_result *)gpr_zalloc(sizeof(*impl)); + impl->base.vtable = &result_vtable; + impl->wrapped = wrapped; + impl->unused_bytes_size = unused_bytes_size; + if (unused_bytes_size > 0) { + impl->unused_bytes = (unsigned char *)gpr_malloc(unused_bytes_size); + memcpy(impl->unused_bytes, unused_bytes, unused_bytes_size); + } else { + impl->unused_bytes = NULL; + } + *handshaker_result = &impl->base; + return TSI_OK; +} + +/* --- tsi_adapter_handshaker implementation ---*/ + +typedef struct { + tsi_handshaker base; + tsi_handshaker *wrapped; + unsigned char *adapter_buffer; + size_t adapter_buffer_size; +} tsi_adapter_handshaker; + +static tsi_result adapter_get_bytes_to_send_to_peer(tsi_handshaker *self, + unsigned char *bytes, + size_t *bytes_size) { + return tsi_handshaker_get_bytes_to_send_to_peer( + tsi_adapter_handshaker_get_wrapped(self), bytes, bytes_size); +} + +static tsi_result adapter_process_bytes_from_peer(tsi_handshaker *self, + const unsigned char *bytes, + size_t *bytes_size) { + return tsi_handshaker_process_bytes_from_peer( + tsi_adapter_handshaker_get_wrapped(self), bytes, bytes_size); +} + +static tsi_result adapter_get_result(tsi_handshaker *self) { + return tsi_handshaker_get_result(tsi_adapter_handshaker_get_wrapped(self)); +} + +static tsi_result adapter_extract_peer(tsi_handshaker *self, tsi_peer *peer) { + return tsi_handshaker_extract_peer(tsi_adapter_handshaker_get_wrapped(self), + peer); +} + +static tsi_result adapter_create_frame_protector( + tsi_handshaker *self, size_t *max_protected_frame_size, + tsi_frame_protector **protector) { + return tsi_handshaker_create_frame_protector( + tsi_adapter_handshaker_get_wrapped(self), max_protected_frame_size, + protector); +} + +static void adapter_destroy(tsi_handshaker *self) { + tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)self; + tsi_handshaker_destroy(impl->wrapped); + gpr_free(impl->adapter_buffer); + gpr_free(self); +} + +static tsi_result adapter_next( + tsi_handshaker *self, const unsigned char *received_bytes, + size_t received_bytes_size, const unsigned char **bytes_to_send, + size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result, + tsi_handshaker_on_next_done_cb cb, void *user_data) { + /* Input sanity check. */ + if ((received_bytes_size > 0 && received_bytes == NULL) || + bytes_to_send == NULL || bytes_to_send_size == NULL || + handshaker_result == NULL) { + return TSI_INVALID_ARGUMENT; + } + + /* If there are received bytes, process them first. */ + tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)self; + tsi_result status = TSI_OK; + size_t bytes_consumed = received_bytes_size; + if (received_bytes_size > 0) { + status = tsi_handshaker_process_bytes_from_peer( + impl->wrapped, received_bytes, &bytes_consumed); + if (status != TSI_OK) return status; + } + + /* Get bytes to send to the peer, if available. */ + size_t offset = 0; + do { + size_t to_send_size = impl->adapter_buffer_size - offset; + status = tsi_handshaker_get_bytes_to_send_to_peer( + impl->wrapped, impl->adapter_buffer + offset, &to_send_size); + offset += to_send_size; + if (status == TSI_INCOMPLETE_DATA) { + impl->adapter_buffer_size *= 2; + impl->adapter_buffer = (unsigned char *)gpr_realloc( + impl->adapter_buffer, impl->adapter_buffer_size); + } + } while (status == TSI_INCOMPLETE_DATA); + if (status != TSI_OK) return status; + *bytes_to_send = impl->adapter_buffer; + *bytes_to_send_size = offset; + + /* If handshake completes, create tsi_handshaker_result. */ + if (tsi_handshaker_is_in_progress(impl->wrapped)) { + *handshaker_result = NULL; + } else { + size_t unused_bytes_size = received_bytes_size - bytes_consumed; + const unsigned char *unused_bytes = + unused_bytes_size == 0 ? NULL : received_bytes + bytes_consumed; + status = tsi_adapter_create_handshaker_result( + impl->wrapped, unused_bytes, unused_bytes_size, handshaker_result); + if (status == TSI_OK) { + impl->base.handshaker_result_created = true; + impl->wrapped = NULL; + } + } + return status; +} + +static const tsi_handshaker_vtable handshaker_vtable = { + adapter_get_bytes_to_send_to_peer, + adapter_process_bytes_from_peer, + adapter_get_result, + adapter_extract_peer, + adapter_create_frame_protector, + adapter_destroy, + adapter_next, +}; + +tsi_handshaker *tsi_create_adapter_handshaker(tsi_handshaker *wrapped) { + GPR_ASSERT(wrapped != NULL); + tsi_adapter_handshaker *impl = + (tsi_adapter_handshaker *)gpr_zalloc(sizeof(*impl)); + impl->base.vtable = &handshaker_vtable; + impl->wrapped = wrapped; + impl->adapter_buffer_size = TSI_ADAPTER_INITIAL_BUFFER_SIZE; + impl->adapter_buffer = (unsigned char *)gpr_malloc(impl->adapter_buffer_size); + return &impl->base; +} + +tsi_handshaker *tsi_adapter_handshaker_get_wrapped(tsi_handshaker *adapter) { + if (adapter == NULL) return NULL; + tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)adapter; + return impl->wrapped; +} diff --git a/src/core/tsi/transport_security_grpc.c b/src/core/tsi/transport_security_grpc.c deleted file mode 100644 index affd995230..0000000000 --- a/src/core/tsi/transport_security_grpc.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include "src/core/tsi/transport_security_grpc.h" - -/* This method creates a tsi_zero_copy_grpc_protector object. */ -tsi_result tsi_handshaker_result_create_zero_copy_grpc_protector( - grpc_exec_ctx *exec_ctx, const tsi_handshaker_result *self, - size_t *max_output_protected_frame_size, - tsi_zero_copy_grpc_protector **protector) { - if (exec_ctx == NULL || self == NULL || self->vtable == NULL || - protector == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->create_zero_copy_grpc_protector == NULL) { - return TSI_UNIMPLEMENTED; - } - return self->vtable->create_zero_copy_grpc_protector( - exec_ctx, self, max_output_protected_frame_size, protector); -} - -/* --- tsi_zero_copy_grpc_protector common implementation. --- - - Calls specific implementation after state/input validation. */ - -tsi_result tsi_zero_copy_grpc_protector_protect( - grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, - grpc_slice_buffer *unprotected_slices, - grpc_slice_buffer *protected_slices) { - if (exec_ctx == NULL || self == NULL || self->vtable == NULL || - unprotected_slices == NULL || protected_slices == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->protect == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->protect(exec_ctx, self, unprotected_slices, - protected_slices); -} - -tsi_result tsi_zero_copy_grpc_protector_unprotect( - grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, - grpc_slice_buffer *protected_slices, - grpc_slice_buffer *unprotected_slices) { - if (exec_ctx == NULL || self == NULL || self->vtable == NULL || - protected_slices == NULL || unprotected_slices == NULL) { - return TSI_INVALID_ARGUMENT; - } - if (self->vtable->unprotect == NULL) return TSI_UNIMPLEMENTED; - return self->vtable->unprotect(exec_ctx, self, protected_slices, - unprotected_slices); -} - -void tsi_zero_copy_grpc_protector_destroy(grpc_exec_ctx *exec_ctx, - tsi_zero_copy_grpc_protector *self) { - if (self == NULL) return; - self->vtable->destroy(exec_ctx, self); -} diff --git a/src/core/tsi/transport_security_grpc.cc b/src/core/tsi/transport_security_grpc.cc new file mode 100644 index 0000000000..affd995230 --- /dev/null +++ b/src/core/tsi/transport_security_grpc.cc @@ -0,0 +1,71 @@ +/* + * + * Copyright 2017 gRPC authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "src/core/tsi/transport_security_grpc.h" + +/* This method creates a tsi_zero_copy_grpc_protector object. */ +tsi_result tsi_handshaker_result_create_zero_copy_grpc_protector( + grpc_exec_ctx *exec_ctx, const tsi_handshaker_result *self, + size_t *max_output_protected_frame_size, + tsi_zero_copy_grpc_protector **protector) { + if (exec_ctx == NULL || self == NULL || self->vtable == NULL || + protector == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->create_zero_copy_grpc_protector == NULL) { + return TSI_UNIMPLEMENTED; + } + return self->vtable->create_zero_copy_grpc_protector( + exec_ctx, self, max_output_protected_frame_size, protector); +} + +/* --- tsi_zero_copy_grpc_protector common implementation. --- + + Calls specific implementation after state/input validation. */ + +tsi_result tsi_zero_copy_grpc_protector_protect( + grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, + grpc_slice_buffer *unprotected_slices, + grpc_slice_buffer *protected_slices) { + if (exec_ctx == NULL || self == NULL || self->vtable == NULL || + unprotected_slices == NULL || protected_slices == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->protect == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->protect(exec_ctx, self, unprotected_slices, + protected_slices); +} + +tsi_result tsi_zero_copy_grpc_protector_unprotect( + grpc_exec_ctx *exec_ctx, tsi_zero_copy_grpc_protector *self, + grpc_slice_buffer *protected_slices, + grpc_slice_buffer *unprotected_slices) { + if (exec_ctx == NULL || self == NULL || self->vtable == NULL || + protected_slices == NULL || unprotected_slices == NULL) { + return TSI_INVALID_ARGUMENT; + } + if (self->vtable->unprotect == NULL) return TSI_UNIMPLEMENTED; + return self->vtable->unprotect(exec_ctx, self, protected_slices, + unprotected_slices); +} + +void tsi_zero_copy_grpc_protector_destroy(grpc_exec_ctx *exec_ctx, + tsi_zero_copy_grpc_protector *self) { + if (self == NULL) return; + self->vtable->destroy(exec_ctx, self); +} diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 29e210042d..5d3251c884 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -15,312 +15,310 @@ # AUTO-GENERATED FROM `$REPO_ROOT/templates/src/python/grpcio/grpc_core_dependencies.py.template`!!! CORE_SOURCE_FILES = [ - 'src/core/lib/profiling/basic_timers.c', - 'src/core/lib/profiling/stap_timers.c', - 'src/core/lib/support/alloc.c', - 'src/core/lib/support/arena.c', - 'src/core/lib/support/atm.c', - 'src/core/lib/support/avl.c', - 'src/core/lib/support/backoff.c', - 'src/core/lib/support/cmdline.c', - 'src/core/lib/support/cpu_iphone.c', - 'src/core/lib/support/cpu_linux.c', - 'src/core/lib/support/cpu_posix.c', - 'src/core/lib/support/cpu_windows.c', - 'src/core/lib/support/env_linux.c', - 'src/core/lib/support/env_posix.c', - 'src/core/lib/support/env_windows.c', - 'src/core/lib/support/histogram.c', - 'src/core/lib/support/host_port.c', - 'src/core/lib/support/log.c', - 'src/core/lib/support/log_android.c', - 'src/core/lib/support/log_linux.c', - 'src/core/lib/support/log_posix.c', - 'src/core/lib/support/log_windows.c', - 'src/core/lib/support/mpscq.c', - 'src/core/lib/support/murmur_hash.c', - 'src/core/lib/support/stack_lockfree.c', - 'src/core/lib/support/string.c', - 'src/core/lib/support/string_posix.c', - 'src/core/lib/support/string_util_windows.c', - 'src/core/lib/support/string_windows.c', - 'src/core/lib/support/subprocess_posix.c', - 'src/core/lib/support/subprocess_windows.c', - 'src/core/lib/support/sync.c', - 'src/core/lib/support/sync_posix.c', - 'src/core/lib/support/sync_windows.c', - 'src/core/lib/support/thd.c', - 'src/core/lib/support/thd_posix.c', - 'src/core/lib/support/thd_windows.c', - 'src/core/lib/support/time.c', - 'src/core/lib/support/time_posix.c', - 'src/core/lib/support/time_precise.c', - 'src/core/lib/support/time_windows.c', - 'src/core/lib/support/tls_pthread.c', - 'src/core/lib/support/tmpfile_msys.c', - 'src/core/lib/support/tmpfile_posix.c', - 'src/core/lib/support/tmpfile_windows.c', - 'src/core/lib/support/wrap_memcpy.c', - 'src/core/lib/surface/init.c', - 'src/core/lib/channel/channel_args.c', - 'src/core/lib/channel/channel_stack.c', - 'src/core/lib/channel/channel_stack_builder.c', - 'src/core/lib/channel/connected_channel.c', - 'src/core/lib/channel/handshaker.c', - 'src/core/lib/channel/handshaker_factory.c', - 'src/core/lib/channel/handshaker_registry.c', - 'src/core/lib/compression/compression.c', - 'src/core/lib/compression/message_compress.c', - 'src/core/lib/compression/stream_compression.c', - 'src/core/lib/compression/stream_compression_gzip.c', - 'src/core/lib/compression/stream_compression_identity.c', - 'src/core/lib/debug/stats.c', - 'src/core/lib/debug/stats_data.c', - 'src/core/lib/http/format_request.c', - 'src/core/lib/http/httpcli.c', - 'src/core/lib/http/parser.c', - 'src/core/lib/iomgr/call_combiner.c', - 'src/core/lib/iomgr/closure.c', - 'src/core/lib/iomgr/combiner.c', - 'src/core/lib/iomgr/endpoint.c', - 'src/core/lib/iomgr/endpoint_pair_posix.c', - 'src/core/lib/iomgr/endpoint_pair_uv.c', - 'src/core/lib/iomgr/endpoint_pair_windows.c', - 'src/core/lib/iomgr/error.c', - 'src/core/lib/iomgr/ev_epoll1_linux.c', - 'src/core/lib/iomgr/ev_epollex_linux.c', - 'src/core/lib/iomgr/ev_epollsig_linux.c', - 'src/core/lib/iomgr/ev_poll_posix.c', - 'src/core/lib/iomgr/ev_posix.c', - 'src/core/lib/iomgr/ev_windows.c', - 'src/core/lib/iomgr/exec_ctx.c', - 'src/core/lib/iomgr/executor.c', - 'src/core/lib/iomgr/gethostname_fallback.c', - 'src/core/lib/iomgr/gethostname_host_name_max.c', - 'src/core/lib/iomgr/gethostname_sysconf.c', - 'src/core/lib/iomgr/iocp_windows.c', - 'src/core/lib/iomgr/iomgr.c', - 'src/core/lib/iomgr/iomgr_posix.c', - 'src/core/lib/iomgr/iomgr_uv.c', - 'src/core/lib/iomgr/iomgr_windows.c', - 'src/core/lib/iomgr/is_epollexclusive_available.c', - 'src/core/lib/iomgr/load_file.c', - 'src/core/lib/iomgr/lockfree_event.c', - 'src/core/lib/iomgr/network_status_tracker.c', - 'src/core/lib/iomgr/polling_entity.c', - 'src/core/lib/iomgr/pollset_set_uv.c', - 'src/core/lib/iomgr/pollset_set_windows.c', - 'src/core/lib/iomgr/pollset_uv.c', - 'src/core/lib/iomgr/pollset_windows.c', - 'src/core/lib/iomgr/resolve_address_posix.c', - 'src/core/lib/iomgr/resolve_address_uv.c', - 'src/core/lib/iomgr/resolve_address_windows.c', - 'src/core/lib/iomgr/resource_quota.c', - 'src/core/lib/iomgr/sockaddr_utils.c', - 'src/core/lib/iomgr/socket_factory_posix.c', - 'src/core/lib/iomgr/socket_mutator.c', - 'src/core/lib/iomgr/socket_utils_common_posix.c', - 'src/core/lib/iomgr/socket_utils_linux.c', - 'src/core/lib/iomgr/socket_utils_posix.c', - 'src/core/lib/iomgr/socket_utils_uv.c', - 'src/core/lib/iomgr/socket_utils_windows.c', - 'src/core/lib/iomgr/socket_windows.c', - 'src/core/lib/iomgr/tcp_client_posix.c', - 'src/core/lib/iomgr/tcp_client_uv.c', - 'src/core/lib/iomgr/tcp_client_windows.c', - 'src/core/lib/iomgr/tcp_posix.c', - 'src/core/lib/iomgr/tcp_server_posix.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_common.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c', - 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c', - 'src/core/lib/iomgr/tcp_server_uv.c', - 'src/core/lib/iomgr/tcp_server_windows.c', - 'src/core/lib/iomgr/tcp_uv.c', - 'src/core/lib/iomgr/tcp_windows.c', - 'src/core/lib/iomgr/time_averaged_stats.c', - 'src/core/lib/iomgr/timer_generic.c', - 'src/core/lib/iomgr/timer_heap.c', - 'src/core/lib/iomgr/timer_manager.c', - 'src/core/lib/iomgr/timer_uv.c', - 'src/core/lib/iomgr/udp_server.c', - 'src/core/lib/iomgr/unix_sockets_posix.c', - 'src/core/lib/iomgr/unix_sockets_posix_noop.c', - 'src/core/lib/iomgr/wakeup_fd_cv.c', - 'src/core/lib/iomgr/wakeup_fd_eventfd.c', - 'src/core/lib/iomgr/wakeup_fd_nospecial.c', - 'src/core/lib/iomgr/wakeup_fd_pipe.c', - 'src/core/lib/iomgr/wakeup_fd_posix.c', - 'src/core/lib/json/json.c', - 'src/core/lib/json/json_reader.c', - 'src/core/lib/json/json_string.c', - 'src/core/lib/json/json_writer.c', - 'src/core/lib/slice/b64.c', - 'src/core/lib/slice/percent_encoding.c', - 'src/core/lib/slice/slice.c', - 'src/core/lib/slice/slice_buffer.c', - 'src/core/lib/slice/slice_hash_table.c', - 'src/core/lib/slice/slice_intern.c', - 'src/core/lib/slice/slice_string_helpers.c', - 'src/core/lib/surface/alarm.c', - 'src/core/lib/surface/api_trace.c', - 'src/core/lib/surface/byte_buffer.c', - 'src/core/lib/surface/byte_buffer_reader.c', - 'src/core/lib/surface/call.c', - 'src/core/lib/surface/call_details.c', - 'src/core/lib/surface/call_log_batch.c', - 'src/core/lib/surface/channel.c', - 'src/core/lib/surface/channel_init.c', - 'src/core/lib/surface/channel_ping.c', - 'src/core/lib/surface/channel_stack_type.c', - 'src/core/lib/surface/completion_queue.c', - 'src/core/lib/surface/completion_queue_factory.c', - 'src/core/lib/surface/event_string.c', + 'src/core/lib/profiling/basic_timers.cc', + 'src/core/lib/profiling/stap_timers.cc', + 'src/core/lib/support/alloc.cc', + 'src/core/lib/support/arena.cc', + 'src/core/lib/support/atm.cc', + 'src/core/lib/support/avl.cc', + 'src/core/lib/support/backoff.cc', + 'src/core/lib/support/cmdline.cc', + 'src/core/lib/support/cpu_iphone.cc', + 'src/core/lib/support/cpu_linux.cc', + 'src/core/lib/support/cpu_posix.cc', + 'src/core/lib/support/cpu_windows.cc', + 'src/core/lib/support/env_linux.cc', + 'src/core/lib/support/env_posix.cc', + 'src/core/lib/support/env_windows.cc', + 'src/core/lib/support/histogram.cc', + 'src/core/lib/support/host_port.cc', + 'src/core/lib/support/log.cc', + 'src/core/lib/support/log_android.cc', + 'src/core/lib/support/log_linux.cc', + 'src/core/lib/support/log_posix.cc', + 'src/core/lib/support/log_windows.cc', + 'src/core/lib/support/mpscq.cc', + 'src/core/lib/support/murmur_hash.cc', + 'src/core/lib/support/stack_lockfree.cc', + 'src/core/lib/support/string.cc', + 'src/core/lib/support/string_posix.cc', + 'src/core/lib/support/string_util_windows.cc', + 'src/core/lib/support/string_windows.cc', + 'src/core/lib/support/subprocess_posix.cc', + 'src/core/lib/support/subprocess_windows.cc', + 'src/core/lib/support/sync.cc', + 'src/core/lib/support/sync_posix.cc', + 'src/core/lib/support/sync_windows.cc', + 'src/core/lib/support/thd.cc', + 'src/core/lib/support/thd_posix.cc', + 'src/core/lib/support/thd_windows.cc', + 'src/core/lib/support/time.cc', + 'src/core/lib/support/time_posix.cc', + 'src/core/lib/support/time_precise.cc', + 'src/core/lib/support/time_windows.cc', + 'src/core/lib/support/tls_pthread.cc', + 'src/core/lib/support/tmpfile_msys.cc', + 'src/core/lib/support/tmpfile_posix.cc', + 'src/core/lib/support/tmpfile_windows.cc', + 'src/core/lib/support/wrap_memcpy.cc', + 'src/core/lib/surface/init.cc', + 'src/core/lib/channel/channel_args.cc', + 'src/core/lib/channel/channel_stack.cc', + 'src/core/lib/channel/channel_stack_builder.cc', + 'src/core/lib/channel/connected_channel.cc', + 'src/core/lib/channel/handshaker.cc', + 'src/core/lib/channel/handshaker_factory.cc', + 'src/core/lib/channel/handshaker_registry.cc', + 'src/core/lib/compression/compression.cc', + 'src/core/lib/compression/message_compress.cc', + 'src/core/lib/compression/stream_compression.cc', + 'src/core/lib/debug/stats.cc', + 'src/core/lib/debug/stats_data.cc', + 'src/core/lib/http/format_request.cc', + 'src/core/lib/http/httpcli.cc', + 'src/core/lib/http/parser.cc', + 'src/core/lib/iomgr/call_combiner.cc', + 'src/core/lib/iomgr/closure.cc', + 'src/core/lib/iomgr/combiner.cc', + 'src/core/lib/iomgr/endpoint.cc', + 'src/core/lib/iomgr/endpoint_pair_posix.cc', + 'src/core/lib/iomgr/endpoint_pair_uv.cc', + 'src/core/lib/iomgr/endpoint_pair_windows.cc', + 'src/core/lib/iomgr/error.cc', + 'src/core/lib/iomgr/ev_epoll1_linux.cc', + 'src/core/lib/iomgr/ev_epollex_linux.cc', + 'src/core/lib/iomgr/ev_epollsig_linux.cc', + 'src/core/lib/iomgr/ev_poll_posix.cc', + 'src/core/lib/iomgr/ev_posix.cc', + 'src/core/lib/iomgr/ev_windows.cc', + 'src/core/lib/iomgr/exec_ctx.cc', + 'src/core/lib/iomgr/executor.cc', + 'src/core/lib/iomgr/gethostname_fallback.cc', + 'src/core/lib/iomgr/gethostname_host_name_max.cc', + 'src/core/lib/iomgr/gethostname_sysconf.cc', + 'src/core/lib/iomgr/iocp_windows.cc', + 'src/core/lib/iomgr/iomgr.cc', + 'src/core/lib/iomgr/iomgr_posix.cc', + 'src/core/lib/iomgr/iomgr_uv.cc', + 'src/core/lib/iomgr/iomgr_windows.cc', + 'src/core/lib/iomgr/is_epollexclusive_available.cc', + 'src/core/lib/iomgr/load_file.cc', + 'src/core/lib/iomgr/lockfree_event.cc', + 'src/core/lib/iomgr/network_status_tracker.cc', + 'src/core/lib/iomgr/polling_entity.cc', + 'src/core/lib/iomgr/pollset_set_uv.cc', + 'src/core/lib/iomgr/pollset_set_windows.cc', + 'src/core/lib/iomgr/pollset_uv.cc', + 'src/core/lib/iomgr/pollset_windows.cc', + 'src/core/lib/iomgr/resolve_address_posix.cc', + 'src/core/lib/iomgr/resolve_address_uv.cc', + 'src/core/lib/iomgr/resolve_address_windows.cc', + 'src/core/lib/iomgr/resource_quota.cc', + 'src/core/lib/iomgr/sockaddr_utils.cc', + 'src/core/lib/iomgr/socket_factory_posix.cc', + 'src/core/lib/iomgr/socket_mutator.cc', + 'src/core/lib/iomgr/socket_utils_common_posix.cc', + 'src/core/lib/iomgr/socket_utils_linux.cc', + 'src/core/lib/iomgr/socket_utils_posix.cc', + 'src/core/lib/iomgr/socket_utils_uv.cc', + 'src/core/lib/iomgr/socket_utils_windows.cc', + 'src/core/lib/iomgr/socket_windows.cc', + 'src/core/lib/iomgr/tcp_client_posix.cc', + 'src/core/lib/iomgr/tcp_client_uv.cc', + 'src/core/lib/iomgr/tcp_client_windows.cc', + 'src/core/lib/iomgr/tcp_posix.cc', + 'src/core/lib/iomgr/tcp_server_posix.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_common.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc', + 'src/core/lib/iomgr/tcp_server_uv.cc', + 'src/core/lib/iomgr/tcp_server_windows.cc', + 'src/core/lib/iomgr/tcp_uv.cc', + 'src/core/lib/iomgr/tcp_windows.cc', + 'src/core/lib/iomgr/time_averaged_stats.cc', + 'src/core/lib/iomgr/timer_generic.cc', + 'src/core/lib/iomgr/timer_heap.cc', + 'src/core/lib/iomgr/timer_manager.cc', + 'src/core/lib/iomgr/timer_uv.cc', + 'src/core/lib/iomgr/udp_server.cc', + 'src/core/lib/iomgr/unix_sockets_posix.cc', + 'src/core/lib/iomgr/unix_sockets_posix_noop.cc', + 'src/core/lib/iomgr/wakeup_fd_cv.cc', + 'src/core/lib/iomgr/wakeup_fd_eventfd.cc', + 'src/core/lib/iomgr/wakeup_fd_nospecial.cc', + 'src/core/lib/iomgr/wakeup_fd_pipe.cc', + 'src/core/lib/iomgr/wakeup_fd_posix.cc', + 'src/core/lib/json/json.cc', + 'src/core/lib/json/json_reader.cc', + 'src/core/lib/json/json_string.cc', + 'src/core/lib/json/json_writer.cc', + 'src/core/lib/slice/b64.cc', + 'src/core/lib/slice/percent_encoding.cc', + 'src/core/lib/slice/slice.cc', + 'src/core/lib/slice/slice_buffer.cc', + 'src/core/lib/slice/slice_hash_table.cc', + 'src/core/lib/slice/slice_intern.cc', + 'src/core/lib/slice/slice_string_helpers.cc', + 'src/core/lib/surface/alarm.cc', + 'src/core/lib/surface/api_trace.cc', + 'src/core/lib/surface/byte_buffer.cc', + 'src/core/lib/surface/byte_buffer_reader.cc', + 'src/core/lib/surface/call.cc', + 'src/core/lib/surface/call_details.cc', + 'src/core/lib/surface/call_log_batch.cc', + 'src/core/lib/surface/channel.cc', + 'src/core/lib/surface/channel_init.cc', + 'src/core/lib/surface/channel_ping.cc', + 'src/core/lib/surface/channel_stack_type.cc', + 'src/core/lib/surface/completion_queue.cc', + 'src/core/lib/surface/completion_queue_factory.cc', + 'src/core/lib/surface/event_string.cc', 'src/core/lib/surface/lame_client.cc', - 'src/core/lib/surface/metadata_array.c', - 'src/core/lib/surface/server.c', - 'src/core/lib/surface/validate_metadata.c', - 'src/core/lib/surface/version.c', - 'src/core/lib/transport/bdp_estimator.c', - 'src/core/lib/transport/byte_stream.c', - 'src/core/lib/transport/connectivity_state.c', - 'src/core/lib/transport/error_utils.c', - 'src/core/lib/transport/metadata.c', - 'src/core/lib/transport/metadata_batch.c', - 'src/core/lib/transport/pid_controller.c', - 'src/core/lib/transport/service_config.c', - 'src/core/lib/transport/static_metadata.c', - 'src/core/lib/transport/status_conversion.c', - 'src/core/lib/transport/timeout_encoding.c', - 'src/core/lib/transport/transport.c', - 'src/core/lib/transport/transport_op_string.c', - 'src/core/lib/debug/trace.c', - 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c', - 'src/core/ext/transport/chttp2/transport/bin_decoder.c', - 'src/core/ext/transport/chttp2/transport/bin_encoder.c', - 'src/core/ext/transport/chttp2/transport/chttp2_plugin.c', - 'src/core/ext/transport/chttp2/transport/chttp2_transport.c', - 'src/core/ext/transport/chttp2/transport/flow_control.c', - 'src/core/ext/transport/chttp2/transport/frame_data.c', - 'src/core/ext/transport/chttp2/transport/frame_goaway.c', - 'src/core/ext/transport/chttp2/transport/frame_ping.c', - 'src/core/ext/transport/chttp2/transport/frame_rst_stream.c', - 'src/core/ext/transport/chttp2/transport/frame_settings.c', - 'src/core/ext/transport/chttp2/transport/frame_window_update.c', - 'src/core/ext/transport/chttp2/transport/hpack_encoder.c', - 'src/core/ext/transport/chttp2/transport/hpack_parser.c', - 'src/core/ext/transport/chttp2/transport/hpack_table.c', - 'src/core/ext/transport/chttp2/transport/http2_settings.c', - 'src/core/ext/transport/chttp2/transport/huffsyms.c', - 'src/core/ext/transport/chttp2/transport/incoming_metadata.c', - 'src/core/ext/transport/chttp2/transport/parsing.c', - 'src/core/ext/transport/chttp2/transport/stream_lists.c', - 'src/core/ext/transport/chttp2/transport/stream_map.c', - 'src/core/ext/transport/chttp2/transport/varint.c', - 'src/core/ext/transport/chttp2/transport/writing.c', - 'src/core/ext/transport/chttp2/alpn/alpn.c', - 'src/core/ext/filters/http/client/http_client_filter.c', - 'src/core/ext/filters/http/http_filters_plugin.c', - 'src/core/ext/filters/http/message_compress/message_compress_filter.c', - 'src/core/ext/filters/http/server/http_server_filter.c', - 'src/core/lib/http/httpcli_security_connector.c', - 'src/core/lib/security/context/security_context.c', - 'src/core/lib/security/credentials/composite/composite_credentials.c', - 'src/core/lib/security/credentials/credentials.c', - 'src/core/lib/security/credentials/credentials_metadata.c', - 'src/core/lib/security/credentials/fake/fake_credentials.c', - 'src/core/lib/security/credentials/google_default/credentials_generic.c', - 'src/core/lib/security/credentials/google_default/google_default_credentials.c', - 'src/core/lib/security/credentials/iam/iam_credentials.c', - 'src/core/lib/security/credentials/jwt/json_token.c', - 'src/core/lib/security/credentials/jwt/jwt_credentials.c', - 'src/core/lib/security/credentials/jwt/jwt_verifier.c', - 'src/core/lib/security/credentials/oauth2/oauth2_credentials.c', - 'src/core/lib/security/credentials/plugin/plugin_credentials.c', - 'src/core/lib/security/credentials/ssl/ssl_credentials.c', - 'src/core/lib/security/transport/client_auth_filter.c', - 'src/core/lib/security/transport/lb_targets_info.c', - 'src/core/lib/security/transport/secure_endpoint.c', - 'src/core/lib/security/transport/security_connector.c', - 'src/core/lib/security/transport/security_handshaker.c', - 'src/core/lib/security/transport/server_auth_filter.c', - 'src/core/lib/security/transport/tsi_error.c', - 'src/core/lib/security/util/json_util.c', - 'src/core/lib/surface/init_secure.c', - 'src/core/tsi/fake_transport_security.c', - 'src/core/tsi/gts_transport_security.c', - 'src/core/tsi/ssl_transport_security.c', - 'src/core/tsi/transport_security_grpc.c', - 'src/core/tsi/transport_security.c', - 'src/core/tsi/transport_security_adapter.c', - 'src/core/ext/transport/chttp2/server/chttp2_server.c', - 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.c', - 'src/core/ext/filters/client_channel/channel_connectivity.c', - 'src/core/ext/filters/client_channel/client_channel.c', - 'src/core/ext/filters/client_channel/client_channel_factory.c', - 'src/core/ext/filters/client_channel/client_channel_plugin.c', - 'src/core/ext/filters/client_channel/connector.c', - 'src/core/ext/filters/client_channel/http_connect_handshaker.c', - 'src/core/ext/filters/client_channel/http_proxy.c', - 'src/core/ext/filters/client_channel/lb_policy.c', - 'src/core/ext/filters/client_channel/lb_policy_factory.c', - 'src/core/ext/filters/client_channel/lb_policy_registry.c', - 'src/core/ext/filters/client_channel/parse_address.c', - 'src/core/ext/filters/client_channel/proxy_mapper.c', - 'src/core/ext/filters/client_channel/proxy_mapper_registry.c', - 'src/core/ext/filters/client_channel/resolver.c', - 'src/core/ext/filters/client_channel/resolver_factory.c', - 'src/core/ext/filters/client_channel/resolver_registry.c', - 'src/core/ext/filters/client_channel/retry_throttle.c', - 'src/core/ext/filters/client_channel/subchannel.c', - 'src/core/ext/filters/client_channel/subchannel_index.c', - 'src/core/ext/filters/client_channel/uri_parser.c', - 'src/core/ext/filters/deadline/deadline_filter.c', - 'src/core/ext/transport/chttp2/client/chttp2_connector.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.c', - 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create.c', - 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c', - 'src/core/ext/transport/inproc/inproc_plugin.c', - 'src/core/ext/transport/inproc/inproc_transport.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', + 'src/core/lib/surface/metadata_array.cc', + 'src/core/lib/surface/server.cc', + 'src/core/lib/surface/validate_metadata.cc', + 'src/core/lib/surface/version.cc', + 'src/core/lib/transport/bdp_estimator.cc', + 'src/core/lib/transport/byte_stream.cc', + 'src/core/lib/transport/connectivity_state.cc', + 'src/core/lib/transport/error_utils.cc', + 'src/core/lib/transport/metadata.cc', + 'src/core/lib/transport/metadata_batch.cc', + 'src/core/lib/transport/pid_controller.cc', + 'src/core/lib/transport/service_config.cc', + 'src/core/lib/transport/static_metadata.cc', + 'src/core/lib/transport/status_conversion.cc', + 'src/core/lib/transport/timeout_encoding.cc', + 'src/core/lib/transport/transport.cc', + 'src/core/lib/transport/transport_op_string.cc', + 'src/core/lib/debug/trace.cc', + 'src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc', + 'src/core/ext/transport/chttp2/transport/bin_decoder.cc', + 'src/core/ext/transport/chttp2/transport/bin_encoder.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_plugin.cc', + 'src/core/ext/transport/chttp2/transport/chttp2_transport.cc', + 'src/core/ext/transport/chttp2/transport/flow_control.cc', + 'src/core/ext/transport/chttp2/transport/frame_data.cc', + 'src/core/ext/transport/chttp2/transport/frame_goaway.cc', + 'src/core/ext/transport/chttp2/transport/frame_ping.cc', + 'src/core/ext/transport/chttp2/transport/frame_rst_stream.cc', + 'src/core/ext/transport/chttp2/transport/frame_settings.cc', + 'src/core/ext/transport/chttp2/transport/frame_window_update.cc', + 'src/core/ext/transport/chttp2/transport/hpack_encoder.cc', + 'src/core/ext/transport/chttp2/transport/hpack_parser.cc', + 'src/core/ext/transport/chttp2/transport/hpack_table.cc', + 'src/core/ext/transport/chttp2/transport/http2_settings.cc', + 'src/core/ext/transport/chttp2/transport/huffsyms.cc', + 'src/core/ext/transport/chttp2/transport/incoming_metadata.cc', + 'src/core/ext/transport/chttp2/transport/parsing.cc', + 'src/core/ext/transport/chttp2/transport/stream_lists.cc', + 'src/core/ext/transport/chttp2/transport/stream_map.cc', + 'src/core/ext/transport/chttp2/transport/varint.cc', + 'src/core/ext/transport/chttp2/transport/writing.cc', + 'src/core/ext/transport/chttp2/alpn/alpn.cc', + 'src/core/ext/filters/http/client/http_client_filter.cc', + 'src/core/ext/filters/http/http_filters_plugin.cc', + 'src/core/ext/filters/http/message_compress/message_compress_filter.cc', + 'src/core/ext/filters/http/server/http_server_filter.cc', + 'src/core/lib/http/httpcli_security_connector.cc', + 'src/core/lib/security/context/security_context.cc', + 'src/core/lib/security/credentials/composite/composite_credentials.cc', + 'src/core/lib/security/credentials/credentials.cc', + 'src/core/lib/security/credentials/credentials_metadata.cc', + 'src/core/lib/security/credentials/fake/fake_credentials.cc', + 'src/core/lib/security/credentials/google_default/credentials_generic.cc', + 'src/core/lib/security/credentials/google_default/google_default_credentials.cc', + 'src/core/lib/security/credentials/iam/iam_credentials.cc', + 'src/core/lib/security/credentials/jwt/json_token.cc', + 'src/core/lib/security/credentials/jwt/jwt_credentials.cc', + 'src/core/lib/security/credentials/jwt/jwt_verifier.cc', + 'src/core/lib/security/credentials/oauth2/oauth2_credentials.cc', + 'src/core/lib/security/credentials/plugin/plugin_credentials.cc', + 'src/core/lib/security/credentials/ssl/ssl_credentials.cc', + 'src/core/lib/security/transport/client_auth_filter.cc', + 'src/core/lib/security/transport/lb_targets_info.cc', + 'src/core/lib/security/transport/secure_endpoint.cc', + 'src/core/lib/security/transport/security_connector.cc', + 'src/core/lib/security/transport/security_handshaker.cc', + 'src/core/lib/security/transport/server_auth_filter.cc', + 'src/core/lib/security/transport/tsi_error.cc', + 'src/core/lib/security/util/json_util.cc', + 'src/core/lib/surface/init_secure.cc', + 'src/core/tsi/fake_transport_security.cc', + 'src/core/tsi/gts_transport_security.cc', + 'src/core/tsi/ssl_transport_security.cc', + 'src/core/tsi/transport_security_grpc.cc', + 'src/core/tsi/transport_security.cc', + 'src/core/tsi/transport_security_adapter.cc', + 'src/core/ext/transport/chttp2/server/chttp2_server.cc', + 'src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc', + 'src/core/ext/filters/client_channel/channel_connectivity.cc', + 'src/core/ext/filters/client_channel/client_channel.cc', + 'src/core/ext/filters/client_channel/client_channel_factory.cc', + 'src/core/ext/filters/client_channel/client_channel_plugin.cc', + 'src/core/ext/filters/client_channel/connector.cc', + 'src/core/ext/filters/client_channel/http_connect_handshaker.cc', + 'src/core/ext/filters/client_channel/http_proxy.cc', + 'src/core/ext/filters/client_channel/lb_policy.cc', + 'src/core/ext/filters/client_channel/lb_policy_factory.cc', + 'src/core/ext/filters/client_channel/lb_policy_registry.cc', + 'src/core/ext/filters/client_channel/parse_address.cc', + 'src/core/ext/filters/client_channel/proxy_mapper.cc', + 'src/core/ext/filters/client_channel/proxy_mapper_registry.cc', + 'src/core/ext/filters/client_channel/resolver.cc', + 'src/core/ext/filters/client_channel/resolver_factory.cc', + 'src/core/ext/filters/client_channel/resolver_registry.cc', + 'src/core/ext/filters/client_channel/retry_throttle.cc', + 'src/core/ext/filters/client_channel/subchannel.cc', + 'src/core/ext/filters/client_channel/subchannel_index.cc', + 'src/core/ext/filters/client_channel/uri_parser.cc', + 'src/core/ext/filters/deadline/deadline_filter.cc', + 'src/core/ext/transport/chttp2/client/chttp2_connector.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc', + 'src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create.cc', + 'src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc', + 'src/core/ext/transport/inproc/inproc_plugin.cc', + 'src/core/ext/transport/inproc/inproc_transport.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', - 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c', - 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c', - 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c', - 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c', - 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c', - 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_filter.c', - 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.c', - 'src/core/ext/census/base_resources.c', - 'src/core/ext/census/context.c', - 'src/core/ext/census/gen/census.pb.c', - 'src/core/ext/census/gen/trace_context.pb.c', - 'src/core/ext/census/grpc_context.c', - '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', - 'src/core/ext/census/resource.c', - 'src/core/ext/census/trace_context.c', - 'src/core/ext/census/tracing.c', - 'src/core/ext/filters/max_age/max_age_filter.c', - 'src/core/ext/filters/message_size/message_size_filter.c', - 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c', - 'src/core/ext/filters/workarounds/workaround_utils.c', + 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', + 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', + 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc', + 'src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc', + 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', + 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_filter.cc', + 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', + 'src/core/ext/census/base_resources.cc', + 'src/core/ext/census/context.cc', + 'src/core/ext/census/gen/census.pb.cc', + 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/grpc_context.cc', + 'src/core/ext/census/grpc_filter.cc', + 'src/core/ext/census/grpc_plugin.cc', + 'src/core/ext/census/initialize.cc', + 'src/core/ext/census/intrusive_hash_map.cc', + 'src/core/ext/census/mlog.cc', + 'src/core/ext/census/operation.cc', + 'src/core/ext/census/placeholders.cc', + 'src/core/ext/census/resource.cc', + 'src/core/ext/census/trace_context.cc', + 'src/core/ext/census/tracing.cc', + 'src/core/ext/filters/max_age/max_age_filter.cc', + 'src/core/ext/filters/message_size/message_size_filter.cc', + 'src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc', + 'src/core/ext/filters/workarounds/workaround_utils.cc', 'src/core/plugin_registry/grpc_plugin_registry.cc', 'src/boringssl/err_data.c', 'third_party/boringssl/crypto/aes/aes.c', diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 205cb2971e..b6ea3c2eb7 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -872,565 +872,563 @@ src/core/README.md \ src/core/ext/README.md \ src/core/ext/census/README.md \ src/core/ext/census/aggregation.h \ -src/core/ext/census/base_resources.c \ +src/core/ext/census/base_resources.cc \ src/core/ext/census/base_resources.h \ src/core/ext/census/census_interface.h \ src/core/ext/census/census_rpc_stats.h \ -src/core/ext/census/context.c \ +src/core/ext/census/context.cc \ src/core/ext/census/gen/README.md \ -src/core/ext/census/gen/census.pb.c \ +src/core/ext/census/gen/census.pb.cc \ src/core/ext/census/gen/census.pb.h \ -src/core/ext/census/gen/trace_context.pb.c \ +src/core/ext/census/gen/trace_context.pb.cc \ src/core/ext/census/gen/trace_context.pb.h \ -src/core/ext/census/grpc_context.c \ -src/core/ext/census/grpc_filter.c \ +src/core/ext/census/grpc_context.cc \ +src/core/ext/census/grpc_filter.cc \ 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/grpc_plugin.cc \ +src/core/ext/census/initialize.cc \ +src/core/ext/census/intrusive_hash_map.cc \ 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.cc \ src/core/ext/census/mlog.h \ -src/core/ext/census/operation.c \ -src/core/ext/census/placeholders.c \ -src/core/ext/census/resource.c \ +src/core/ext/census/operation.cc \ +src/core/ext/census/placeholders.cc \ +src/core/ext/census/resource.cc \ src/core/ext/census/resource.h \ src/core/ext/census/rpc_metric_id.h \ -src/core/ext/census/trace_context.c \ +src/core/ext/census/trace_context.cc \ src/core/ext/census/trace_context.h \ src/core/ext/census/trace_label.h \ src/core/ext/census/trace_propagation.h \ src/core/ext/census/trace_status.h \ src/core/ext/census/trace_string.h \ -src/core/ext/census/tracing.c \ +src/core/ext/census/tracing.cc \ src/core/ext/census/tracing.h \ src/core/ext/filters/client_channel/README.md \ -src/core/ext/filters/client_channel/channel_connectivity.c \ -src/core/ext/filters/client_channel/client_channel.c \ +src/core/ext/filters/client_channel/channel_connectivity.cc \ +src/core/ext/filters/client_channel/client_channel.cc \ src/core/ext/filters/client_channel/client_channel.h \ -src/core/ext/filters/client_channel/client_channel_factory.c \ +src/core/ext/filters/client_channel/client_channel_factory.cc \ src/core/ext/filters/client_channel/client_channel_factory.h \ -src/core/ext/filters/client_channel/client_channel_plugin.c \ -src/core/ext/filters/client_channel/connector.c \ +src/core/ext/filters/client_channel/client_channel_plugin.cc \ +src/core/ext/filters/client_channel/connector.cc \ src/core/ext/filters/client_channel/connector.h \ -src/core/ext/filters/client_channel/http_connect_handshaker.c \ +src/core/ext/filters/client_channel/http_connect_handshaker.cc \ src/core/ext/filters/client_channel/http_connect_handshaker.h \ -src/core/ext/filters/client_channel/http_proxy.c \ +src/core/ext/filters/client_channel/http_proxy.cc \ src/core/ext/filters/client_channel/http_proxy.h \ -src/core/ext/filters/client_channel/lb_policy.c \ +src/core/ext/filters/client_channel/lb_policy.cc \ src/core/ext/filters/client_channel/lb_policy.h \ -src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c \ +src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h \ -src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c \ +src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h \ -src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c \ -src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c \ +src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc \ +src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h \ -src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c \ +src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h \ -src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ +src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h \ -src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c \ -src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c \ -src/core/ext/filters/client_channel/lb_policy_factory.c \ +src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ +src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ +src/core/ext/filters/client_channel/lb_policy_factory.cc \ src/core/ext/filters/client_channel/lb_policy_factory.h \ -src/core/ext/filters/client_channel/lb_policy_registry.c \ +src/core/ext/filters/client_channel/lb_policy_registry.cc \ src/core/ext/filters/client_channel/lb_policy_registry.h \ -src/core/ext/filters/client_channel/parse_address.c \ +src/core/ext/filters/client_channel/parse_address.cc \ src/core/ext/filters/client_channel/parse_address.h \ -src/core/ext/filters/client_channel/proxy_mapper.c \ +src/core/ext/filters/client_channel/proxy_mapper.cc \ src/core/ext/filters/client_channel/proxy_mapper.h \ -src/core/ext/filters/client_channel/proxy_mapper_registry.c \ +src/core/ext/filters/client_channel/proxy_mapper_registry.cc \ src/core/ext/filters/client_channel/proxy_mapper_registry.h \ -src/core/ext/filters/client_channel/resolver.c \ +src/core/ext/filters/client_channel/resolver.cc \ src/core/ext/filters/client_channel/resolver.h \ src/core/ext/filters/client_channel/resolver/README.md \ -src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c \ +src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h \ -src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c \ -src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c \ +src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc \ +src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc \ src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h \ -src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c \ +src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc \ src/core/ext/filters/client_channel/resolver/dns/native/README.md \ -src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c \ -src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c \ +src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \ +src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc \ src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h \ src/core/ext/filters/client_channel/resolver/sockaddr/README.md \ -src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c \ -src/core/ext/filters/client_channel/resolver_factory.c \ +src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ +src/core/ext/filters/client_channel/resolver_factory.cc \ src/core/ext/filters/client_channel/resolver_factory.h \ -src/core/ext/filters/client_channel/resolver_registry.c \ +src/core/ext/filters/client_channel/resolver_registry.cc \ src/core/ext/filters/client_channel/resolver_registry.h \ -src/core/ext/filters/client_channel/retry_throttle.c \ +src/core/ext/filters/client_channel/retry_throttle.cc \ src/core/ext/filters/client_channel/retry_throttle.h \ -src/core/ext/filters/client_channel/subchannel.c \ +src/core/ext/filters/client_channel/subchannel.cc \ src/core/ext/filters/client_channel/subchannel.h \ -src/core/ext/filters/client_channel/subchannel_index.c \ +src/core/ext/filters/client_channel/subchannel_index.cc \ src/core/ext/filters/client_channel/subchannel_index.h \ -src/core/ext/filters/client_channel/uri_parser.c \ +src/core/ext/filters/client_channel/uri_parser.cc \ src/core/ext/filters/client_channel/uri_parser.h \ -src/core/ext/filters/deadline/deadline_filter.c \ +src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/deadline/deadline_filter.h \ -src/core/ext/filters/http/client/http_client_filter.c \ +src/core/ext/filters/http/client/http_client_filter.cc \ src/core/ext/filters/http/client/http_client_filter.h \ -src/core/ext/filters/http/http_filters_plugin.c \ -src/core/ext/filters/http/message_compress/message_compress_filter.c \ +src/core/ext/filters/http/http_filters_plugin.cc \ +src/core/ext/filters/http/message_compress/message_compress_filter.cc \ src/core/ext/filters/http/message_compress/message_compress_filter.h \ -src/core/ext/filters/http/server/http_server_filter.c \ +src/core/ext/filters/http/server/http_server_filter.cc \ src/core/ext/filters/http/server/http_server_filter.h \ -src/core/ext/filters/load_reporting/server_load_reporting_filter.c \ +src/core/ext/filters/load_reporting/server_load_reporting_filter.cc \ src/core/ext/filters/load_reporting/server_load_reporting_filter.h \ -src/core/ext/filters/load_reporting/server_load_reporting_plugin.c \ +src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ src/core/ext/filters/load_reporting/server_load_reporting_plugin.h \ -src/core/ext/filters/max_age/max_age_filter.c \ +src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/max_age/max_age_filter.h \ -src/core/ext/filters/message_size/message_size_filter.c \ +src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/message_size/message_size_filter.h \ -src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c \ +src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc \ src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h \ -src/core/ext/filters/workarounds/workaround_utils.c \ +src/core/ext/filters/workarounds/workaround_utils.cc \ src/core/ext/filters/workarounds/workaround_utils.h \ src/core/ext/transport/README.md \ src/core/ext/transport/chttp2/README.md \ -src/core/ext/transport/chttp2/alpn/alpn.c \ +src/core/ext/transport/chttp2/alpn/alpn.cc \ src/core/ext/transport/chttp2/alpn/alpn.h \ -src/core/ext/transport/chttp2/client/chttp2_connector.c \ +src/core/ext/transport/chttp2/client/chttp2_connector.cc \ src/core/ext/transport/chttp2/client/chttp2_connector.h \ src/core/ext/transport/chttp2/client/insecure/README.md \ -src/core/ext/transport/chttp2/client/insecure/channel_create.c \ -src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c \ +src/core/ext/transport/chttp2/client/insecure/channel_create.cc \ +src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc \ src/core/ext/transport/chttp2/client/secure/README.md \ -src/core/ext/transport/chttp2/client/secure/secure_channel_create.c \ -src/core/ext/transport/chttp2/server/chttp2_server.c \ +src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc \ +src/core/ext/transport/chttp2/server/chttp2_server.cc \ src/core/ext/transport/chttp2/server/chttp2_server.h \ src/core/ext/transport/chttp2/server/insecure/README.md \ -src/core/ext/transport/chttp2/server/insecure/server_chttp2.c \ -src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c \ +src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc \ +src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc \ src/core/ext/transport/chttp2/server/secure/README.md \ -src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c \ +src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc \ src/core/ext/transport/chttp2/transport/README.md \ -src/core/ext/transport/chttp2/transport/bin_decoder.c \ +src/core/ext/transport/chttp2/transport/bin_decoder.cc \ src/core/ext/transport/chttp2/transport/bin_decoder.h \ -src/core/ext/transport/chttp2/transport/bin_encoder.c \ +src/core/ext/transport/chttp2/transport/bin_encoder.cc \ src/core/ext/transport/chttp2/transport/bin_encoder.h \ -src/core/ext/transport/chttp2/transport/chttp2_plugin.c \ -src/core/ext/transport/chttp2/transport/chttp2_transport.c \ +src/core/ext/transport/chttp2/transport/chttp2_plugin.cc \ +src/core/ext/transport/chttp2/transport/chttp2_transport.cc \ src/core/ext/transport/chttp2/transport/chttp2_transport.h \ -src/core/ext/transport/chttp2/transport/flow_control.c \ +src/core/ext/transport/chttp2/transport/flow_control.cc \ src/core/ext/transport/chttp2/transport/frame.h \ -src/core/ext/transport/chttp2/transport/frame_data.c \ +src/core/ext/transport/chttp2/transport/frame_data.cc \ src/core/ext/transport/chttp2/transport/frame_data.h \ -src/core/ext/transport/chttp2/transport/frame_goaway.c \ +src/core/ext/transport/chttp2/transport/frame_goaway.cc \ src/core/ext/transport/chttp2/transport/frame_goaway.h \ -src/core/ext/transport/chttp2/transport/frame_ping.c \ +src/core/ext/transport/chttp2/transport/frame_ping.cc \ src/core/ext/transport/chttp2/transport/frame_ping.h \ -src/core/ext/transport/chttp2/transport/frame_rst_stream.c \ +src/core/ext/transport/chttp2/transport/frame_rst_stream.cc \ src/core/ext/transport/chttp2/transport/frame_rst_stream.h \ -src/core/ext/transport/chttp2/transport/frame_settings.c \ +src/core/ext/transport/chttp2/transport/frame_settings.cc \ src/core/ext/transport/chttp2/transport/frame_settings.h \ -src/core/ext/transport/chttp2/transport/frame_window_update.c \ +src/core/ext/transport/chttp2/transport/frame_window_update.cc \ src/core/ext/transport/chttp2/transport/frame_window_update.h \ -src/core/ext/transport/chttp2/transport/hpack_encoder.c \ +src/core/ext/transport/chttp2/transport/hpack_encoder.cc \ src/core/ext/transport/chttp2/transport/hpack_encoder.h \ -src/core/ext/transport/chttp2/transport/hpack_parser.c \ +src/core/ext/transport/chttp2/transport/hpack_parser.cc \ src/core/ext/transport/chttp2/transport/hpack_parser.h \ -src/core/ext/transport/chttp2/transport/hpack_table.c \ +src/core/ext/transport/chttp2/transport/hpack_table.cc \ src/core/ext/transport/chttp2/transport/hpack_table.h \ -src/core/ext/transport/chttp2/transport/http2_settings.c \ +src/core/ext/transport/chttp2/transport/http2_settings.cc \ src/core/ext/transport/chttp2/transport/http2_settings.h \ -src/core/ext/transport/chttp2/transport/huffsyms.c \ +src/core/ext/transport/chttp2/transport/huffsyms.cc \ src/core/ext/transport/chttp2/transport/huffsyms.h \ -src/core/ext/transport/chttp2/transport/incoming_metadata.c \ +src/core/ext/transport/chttp2/transport/incoming_metadata.cc \ src/core/ext/transport/chttp2/transport/incoming_metadata.h \ src/core/ext/transport/chttp2/transport/internal.h \ -src/core/ext/transport/chttp2/transport/parsing.c \ -src/core/ext/transport/chttp2/transport/stream_lists.c \ -src/core/ext/transport/chttp2/transport/stream_map.c \ +src/core/ext/transport/chttp2/transport/parsing.cc \ +src/core/ext/transport/chttp2/transport/stream_lists.cc \ +src/core/ext/transport/chttp2/transport/stream_map.cc \ src/core/ext/transport/chttp2/transport/stream_map.h \ -src/core/ext/transport/chttp2/transport/varint.c \ +src/core/ext/transport/chttp2/transport/varint.cc \ src/core/ext/transport/chttp2/transport/varint.h \ -src/core/ext/transport/chttp2/transport/writing.c \ -src/core/ext/transport/inproc/inproc_plugin.c \ -src/core/ext/transport/inproc/inproc_transport.c \ +src/core/ext/transport/chttp2/transport/writing.cc \ +src/core/ext/transport/inproc/inproc_plugin.cc \ +src/core/ext/transport/inproc/inproc_transport.cc \ src/core/ext/transport/inproc/inproc_transport.h \ src/core/lib/README.md \ src/core/lib/channel/README.md \ -src/core/lib/channel/channel_args.c \ +src/core/lib/channel/channel_args.cc \ src/core/lib/channel/channel_args.h \ -src/core/lib/channel/channel_stack.c \ +src/core/lib/channel/channel_stack.cc \ src/core/lib/channel/channel_stack.h \ -src/core/lib/channel/channel_stack_builder.c \ +src/core/lib/channel/channel_stack_builder.cc \ src/core/lib/channel/channel_stack_builder.h \ -src/core/lib/channel/connected_channel.c \ +src/core/lib/channel/connected_channel.cc \ src/core/lib/channel/connected_channel.h \ src/core/lib/channel/context.h \ -src/core/lib/channel/handshaker.c \ +src/core/lib/channel/handshaker.cc \ src/core/lib/channel/handshaker.h \ -src/core/lib/channel/handshaker_factory.c \ +src/core/lib/channel/handshaker_factory.cc \ src/core/lib/channel/handshaker_factory.h \ -src/core/lib/channel/handshaker_registry.c \ +src/core/lib/channel/handshaker_registry.cc \ src/core/lib/channel/handshaker_registry.h \ src/core/lib/compression/algorithm_metadata.h \ -src/core/lib/compression/compression.c \ -src/core/lib/compression/message_compress.c \ +src/core/lib/compression/compression.cc \ +src/core/lib/compression/message_compress.cc \ src/core/lib/compression/message_compress.h \ -src/core/lib/compression/stream_compression.c \ +src/core/lib/compression/stream_compression.cc \ src/core/lib/compression/stream_compression.h \ -src/core/lib/compression/stream_compression_gzip.c \ src/core/lib/compression/stream_compression_gzip.h \ -src/core/lib/compression/stream_compression_identity.c \ src/core/lib/compression/stream_compression_identity.h \ -src/core/lib/debug/stats.c \ +src/core/lib/debug/stats.cc \ src/core/lib/debug/stats.h \ -src/core/lib/debug/stats_data.c \ +src/core/lib/debug/stats_data.cc \ src/core/lib/debug/stats_data.h \ -src/core/lib/debug/trace.c \ +src/core/lib/debug/trace.cc \ src/core/lib/debug/trace.h \ -src/core/lib/http/format_request.c \ +src/core/lib/http/format_request.cc \ src/core/lib/http/format_request.h \ -src/core/lib/http/httpcli.c \ +src/core/lib/http/httpcli.cc \ src/core/lib/http/httpcli.h \ -src/core/lib/http/httpcli_security_connector.c \ -src/core/lib/http/parser.c \ +src/core/lib/http/httpcli_security_connector.cc \ +src/core/lib/http/parser.cc \ src/core/lib/http/parser.h \ src/core/lib/iomgr/README.md \ -src/core/lib/iomgr/call_combiner.c \ +src/core/lib/iomgr/call_combiner.cc \ src/core/lib/iomgr/call_combiner.h \ -src/core/lib/iomgr/closure.c \ +src/core/lib/iomgr/closure.cc \ src/core/lib/iomgr/closure.h \ -src/core/lib/iomgr/combiner.c \ +src/core/lib/iomgr/combiner.cc \ src/core/lib/iomgr/combiner.h \ -src/core/lib/iomgr/endpoint.c \ +src/core/lib/iomgr/endpoint.cc \ src/core/lib/iomgr/endpoint.h \ src/core/lib/iomgr/endpoint_pair.h \ -src/core/lib/iomgr/endpoint_pair_posix.c \ -src/core/lib/iomgr/endpoint_pair_uv.c \ -src/core/lib/iomgr/endpoint_pair_windows.c \ -src/core/lib/iomgr/error.c \ +src/core/lib/iomgr/endpoint_pair_posix.cc \ +src/core/lib/iomgr/endpoint_pair_uv.cc \ +src/core/lib/iomgr/endpoint_pair_windows.cc \ +src/core/lib/iomgr/error.cc \ src/core/lib/iomgr/error.h \ src/core/lib/iomgr/error_internal.h \ -src/core/lib/iomgr/ev_epoll1_linux.c \ +src/core/lib/iomgr/ev_epoll1_linux.cc \ src/core/lib/iomgr/ev_epoll1_linux.h \ -src/core/lib/iomgr/ev_epollex_linux.c \ +src/core/lib/iomgr/ev_epollex_linux.cc \ src/core/lib/iomgr/ev_epollex_linux.h \ -src/core/lib/iomgr/ev_epollsig_linux.c \ +src/core/lib/iomgr/ev_epollsig_linux.cc \ src/core/lib/iomgr/ev_epollsig_linux.h \ -src/core/lib/iomgr/ev_poll_posix.c \ +src/core/lib/iomgr/ev_poll_posix.cc \ src/core/lib/iomgr/ev_poll_posix.h \ -src/core/lib/iomgr/ev_posix.c \ +src/core/lib/iomgr/ev_posix.cc \ src/core/lib/iomgr/ev_posix.h \ -src/core/lib/iomgr/ev_windows.c \ -src/core/lib/iomgr/exec_ctx.c \ +src/core/lib/iomgr/ev_windows.cc \ +src/core/lib/iomgr/exec_ctx.cc \ src/core/lib/iomgr/exec_ctx.h \ -src/core/lib/iomgr/executor.c \ +src/core/lib/iomgr/executor.cc \ src/core/lib/iomgr/executor.h \ src/core/lib/iomgr/gethostname.h \ -src/core/lib/iomgr/gethostname_fallback.c \ -src/core/lib/iomgr/gethostname_host_name_max.c \ -src/core/lib/iomgr/gethostname_sysconf.c \ -src/core/lib/iomgr/iocp_windows.c \ +src/core/lib/iomgr/gethostname_fallback.cc \ +src/core/lib/iomgr/gethostname_host_name_max.cc \ +src/core/lib/iomgr/gethostname_sysconf.cc \ +src/core/lib/iomgr/iocp_windows.cc \ src/core/lib/iomgr/iocp_windows.h \ -src/core/lib/iomgr/iomgr.c \ +src/core/lib/iomgr/iomgr.cc \ src/core/lib/iomgr/iomgr.h \ src/core/lib/iomgr/iomgr_internal.h \ -src/core/lib/iomgr/iomgr_posix.c \ +src/core/lib/iomgr/iomgr_posix.cc \ src/core/lib/iomgr/iomgr_posix.h \ -src/core/lib/iomgr/iomgr_uv.c \ +src/core/lib/iomgr/iomgr_uv.cc \ src/core/lib/iomgr/iomgr_uv.h \ -src/core/lib/iomgr/iomgr_windows.c \ -src/core/lib/iomgr/is_epollexclusive_available.c \ +src/core/lib/iomgr/iomgr_windows.cc \ +src/core/lib/iomgr/is_epollexclusive_available.cc \ src/core/lib/iomgr/is_epollexclusive_available.h \ -src/core/lib/iomgr/load_file.c \ +src/core/lib/iomgr/load_file.cc \ src/core/lib/iomgr/load_file.h \ -src/core/lib/iomgr/lockfree_event.c \ +src/core/lib/iomgr/lockfree_event.cc \ src/core/lib/iomgr/lockfree_event.h \ src/core/lib/iomgr/nameser.h \ -src/core/lib/iomgr/network_status_tracker.c \ +src/core/lib/iomgr/network_status_tracker.cc \ src/core/lib/iomgr/network_status_tracker.h \ -src/core/lib/iomgr/polling_entity.c \ +src/core/lib/iomgr/polling_entity.cc \ src/core/lib/iomgr/polling_entity.h \ src/core/lib/iomgr/pollset.h \ src/core/lib/iomgr/pollset_set.h \ -src/core/lib/iomgr/pollset_set_uv.c \ -src/core/lib/iomgr/pollset_set_windows.c \ +src/core/lib/iomgr/pollset_set_uv.cc \ +src/core/lib/iomgr/pollset_set_windows.cc \ src/core/lib/iomgr/pollset_set_windows.h \ -src/core/lib/iomgr/pollset_uv.c \ +src/core/lib/iomgr/pollset_uv.cc \ src/core/lib/iomgr/pollset_uv.h \ -src/core/lib/iomgr/pollset_windows.c \ +src/core/lib/iomgr/pollset_windows.cc \ src/core/lib/iomgr/pollset_windows.h \ src/core/lib/iomgr/port.h \ src/core/lib/iomgr/resolve_address.h \ -src/core/lib/iomgr/resolve_address_posix.c \ -src/core/lib/iomgr/resolve_address_uv.c \ -src/core/lib/iomgr/resolve_address_windows.c \ -src/core/lib/iomgr/resource_quota.c \ +src/core/lib/iomgr/resolve_address_posix.cc \ +src/core/lib/iomgr/resolve_address_uv.cc \ +src/core/lib/iomgr/resolve_address_windows.cc \ +src/core/lib/iomgr/resource_quota.cc \ src/core/lib/iomgr/resource_quota.h \ src/core/lib/iomgr/sockaddr.h \ src/core/lib/iomgr/sockaddr_posix.h \ -src/core/lib/iomgr/sockaddr_utils.c \ +src/core/lib/iomgr/sockaddr_utils.cc \ src/core/lib/iomgr/sockaddr_utils.h \ src/core/lib/iomgr/sockaddr_windows.h \ -src/core/lib/iomgr/socket_factory_posix.c \ +src/core/lib/iomgr/socket_factory_posix.cc \ src/core/lib/iomgr/socket_factory_posix.h \ -src/core/lib/iomgr/socket_mutator.c \ +src/core/lib/iomgr/socket_mutator.cc \ src/core/lib/iomgr/socket_mutator.h \ src/core/lib/iomgr/socket_utils.h \ -src/core/lib/iomgr/socket_utils_common_posix.c \ -src/core/lib/iomgr/socket_utils_linux.c \ -src/core/lib/iomgr/socket_utils_posix.c \ +src/core/lib/iomgr/socket_utils_common_posix.cc \ +src/core/lib/iomgr/socket_utils_linux.cc \ +src/core/lib/iomgr/socket_utils_posix.cc \ src/core/lib/iomgr/socket_utils_posix.h \ -src/core/lib/iomgr/socket_utils_uv.c \ -src/core/lib/iomgr/socket_utils_windows.c \ -src/core/lib/iomgr/socket_windows.c \ +src/core/lib/iomgr/socket_utils_uv.cc \ +src/core/lib/iomgr/socket_utils_windows.cc \ +src/core/lib/iomgr/socket_windows.cc \ src/core/lib/iomgr/socket_windows.h \ src/core/lib/iomgr/sys_epoll_wrapper.h \ src/core/lib/iomgr/tcp_client.h \ -src/core/lib/iomgr/tcp_client_posix.c \ +src/core/lib/iomgr/tcp_client_posix.cc \ src/core/lib/iomgr/tcp_client_posix.h \ -src/core/lib/iomgr/tcp_client_uv.c \ -src/core/lib/iomgr/tcp_client_windows.c \ -src/core/lib/iomgr/tcp_posix.c \ +src/core/lib/iomgr/tcp_client_uv.cc \ +src/core/lib/iomgr/tcp_client_windows.cc \ +src/core/lib/iomgr/tcp_posix.cc \ src/core/lib/iomgr/tcp_posix.h \ src/core/lib/iomgr/tcp_server.h \ -src/core/lib/iomgr/tcp_server_posix.c \ +src/core/lib/iomgr/tcp_server_posix.cc \ src/core/lib/iomgr/tcp_server_utils_posix.h \ -src/core/lib/iomgr/tcp_server_utils_posix_common.c \ -src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c \ -src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c \ -src/core/lib/iomgr/tcp_server_uv.c \ -src/core/lib/iomgr/tcp_server_windows.c \ -src/core/lib/iomgr/tcp_uv.c \ +src/core/lib/iomgr/tcp_server_utils_posix_common.cc \ +src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc \ +src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc \ +src/core/lib/iomgr/tcp_server_uv.cc \ +src/core/lib/iomgr/tcp_server_windows.cc \ +src/core/lib/iomgr/tcp_uv.cc \ src/core/lib/iomgr/tcp_uv.h \ -src/core/lib/iomgr/tcp_windows.c \ +src/core/lib/iomgr/tcp_windows.cc \ src/core/lib/iomgr/tcp_windows.h \ -src/core/lib/iomgr/time_averaged_stats.c \ +src/core/lib/iomgr/time_averaged_stats.cc \ src/core/lib/iomgr/time_averaged_stats.h \ src/core/lib/iomgr/timer.h \ -src/core/lib/iomgr/timer_generic.c \ +src/core/lib/iomgr/timer_generic.cc \ src/core/lib/iomgr/timer_generic.h \ -src/core/lib/iomgr/timer_heap.c \ +src/core/lib/iomgr/timer_heap.cc \ src/core/lib/iomgr/timer_heap.h \ -src/core/lib/iomgr/timer_manager.c \ +src/core/lib/iomgr/timer_manager.cc \ src/core/lib/iomgr/timer_manager.h \ -src/core/lib/iomgr/timer_uv.c \ +src/core/lib/iomgr/timer_uv.cc \ src/core/lib/iomgr/timer_uv.h \ -src/core/lib/iomgr/udp_server.c \ +src/core/lib/iomgr/udp_server.cc \ src/core/lib/iomgr/udp_server.h \ -src/core/lib/iomgr/unix_sockets_posix.c \ +src/core/lib/iomgr/unix_sockets_posix.cc \ src/core/lib/iomgr/unix_sockets_posix.h \ -src/core/lib/iomgr/unix_sockets_posix_noop.c \ -src/core/lib/iomgr/wakeup_fd_cv.c \ +src/core/lib/iomgr/unix_sockets_posix_noop.cc \ +src/core/lib/iomgr/wakeup_fd_cv.cc \ src/core/lib/iomgr/wakeup_fd_cv.h \ -src/core/lib/iomgr/wakeup_fd_eventfd.c \ -src/core/lib/iomgr/wakeup_fd_nospecial.c \ -src/core/lib/iomgr/wakeup_fd_pipe.c \ +src/core/lib/iomgr/wakeup_fd_eventfd.cc \ +src/core/lib/iomgr/wakeup_fd_nospecial.cc \ +src/core/lib/iomgr/wakeup_fd_pipe.cc \ src/core/lib/iomgr/wakeup_fd_pipe.h \ -src/core/lib/iomgr/wakeup_fd_posix.c \ +src/core/lib/iomgr/wakeup_fd_posix.cc \ src/core/lib/iomgr/wakeup_fd_posix.h \ -src/core/lib/json/json.c \ +src/core/lib/json/json.cc \ src/core/lib/json/json.h \ src/core/lib/json/json_common.h \ -src/core/lib/json/json_reader.c \ +src/core/lib/json/json_reader.cc \ src/core/lib/json/json_reader.h \ -src/core/lib/json/json_string.c \ -src/core/lib/json/json_writer.c \ +src/core/lib/json/json_string.cc \ +src/core/lib/json/json_writer.cc \ src/core/lib/json/json_writer.h \ -src/core/lib/profiling/basic_timers.c \ -src/core/lib/profiling/stap_timers.c \ +src/core/lib/profiling/basic_timers.cc \ +src/core/lib/profiling/stap_timers.cc \ src/core/lib/profiling/timers.h \ -src/core/lib/security/context/security_context.c \ +src/core/lib/security/context/security_context.cc \ src/core/lib/security/context/security_context.h \ -src/core/lib/security/credentials/composite/composite_credentials.c \ +src/core/lib/security/credentials/composite/composite_credentials.cc \ src/core/lib/security/credentials/composite/composite_credentials.h \ -src/core/lib/security/credentials/credentials.c \ +src/core/lib/security/credentials/credentials.cc \ src/core/lib/security/credentials/credentials.h \ -src/core/lib/security/credentials/credentials_metadata.c \ -src/core/lib/security/credentials/fake/fake_credentials.c \ +src/core/lib/security/credentials/credentials_metadata.cc \ +src/core/lib/security/credentials/fake/fake_credentials.cc \ src/core/lib/security/credentials/fake/fake_credentials.h \ -src/core/lib/security/credentials/google_default/credentials_generic.c \ -src/core/lib/security/credentials/google_default/google_default_credentials.c \ +src/core/lib/security/credentials/google_default/credentials_generic.cc \ +src/core/lib/security/credentials/google_default/google_default_credentials.cc \ src/core/lib/security/credentials/google_default/google_default_credentials.h \ -src/core/lib/security/credentials/iam/iam_credentials.c \ +src/core/lib/security/credentials/iam/iam_credentials.cc \ src/core/lib/security/credentials/iam/iam_credentials.h \ -src/core/lib/security/credentials/jwt/json_token.c \ +src/core/lib/security/credentials/jwt/json_token.cc \ src/core/lib/security/credentials/jwt/json_token.h \ -src/core/lib/security/credentials/jwt/jwt_credentials.c \ +src/core/lib/security/credentials/jwt/jwt_credentials.cc \ src/core/lib/security/credentials/jwt/jwt_credentials.h \ -src/core/lib/security/credentials/jwt/jwt_verifier.c \ +src/core/lib/security/credentials/jwt/jwt_verifier.cc \ src/core/lib/security/credentials/jwt/jwt_verifier.h \ -src/core/lib/security/credentials/oauth2/oauth2_credentials.c \ +src/core/lib/security/credentials/oauth2/oauth2_credentials.cc \ src/core/lib/security/credentials/oauth2/oauth2_credentials.h \ -src/core/lib/security/credentials/plugin/plugin_credentials.c \ +src/core/lib/security/credentials/plugin/plugin_credentials.cc \ src/core/lib/security/credentials/plugin/plugin_credentials.h \ -src/core/lib/security/credentials/ssl/ssl_credentials.c \ +src/core/lib/security/credentials/ssl/ssl_credentials.cc \ src/core/lib/security/credentials/ssl/ssl_credentials.h \ src/core/lib/security/transport/auth_filters.h \ -src/core/lib/security/transport/client_auth_filter.c \ -src/core/lib/security/transport/lb_targets_info.c \ +src/core/lib/security/transport/client_auth_filter.cc \ +src/core/lib/security/transport/lb_targets_info.cc \ src/core/lib/security/transport/lb_targets_info.h \ -src/core/lib/security/transport/secure_endpoint.c \ +src/core/lib/security/transport/secure_endpoint.cc \ src/core/lib/security/transport/secure_endpoint.h \ -src/core/lib/security/transport/security_connector.c \ +src/core/lib/security/transport/security_connector.cc \ src/core/lib/security/transport/security_connector.h \ -src/core/lib/security/transport/security_handshaker.c \ +src/core/lib/security/transport/security_handshaker.cc \ src/core/lib/security/transport/security_handshaker.h \ -src/core/lib/security/transport/server_auth_filter.c \ -src/core/lib/security/transport/tsi_error.c \ +src/core/lib/security/transport/server_auth_filter.cc \ +src/core/lib/security/transport/tsi_error.cc \ src/core/lib/security/transport/tsi_error.h \ -src/core/lib/security/util/json_util.c \ +src/core/lib/security/util/json_util.cc \ src/core/lib/security/util/json_util.h \ -src/core/lib/slice/b64.c \ +src/core/lib/slice/b64.cc \ src/core/lib/slice/b64.h \ -src/core/lib/slice/percent_encoding.c \ +src/core/lib/slice/percent_encoding.cc \ src/core/lib/slice/percent_encoding.h \ -src/core/lib/slice/slice.c \ -src/core/lib/slice/slice_buffer.c \ -src/core/lib/slice/slice_hash_table.c \ +src/core/lib/slice/slice.cc \ +src/core/lib/slice/slice_buffer.cc \ +src/core/lib/slice/slice_hash_table.cc \ src/core/lib/slice/slice_hash_table.h \ -src/core/lib/slice/slice_intern.c \ +src/core/lib/slice/slice_intern.cc \ src/core/lib/slice/slice_internal.h \ -src/core/lib/slice/slice_string_helpers.c \ +src/core/lib/slice/slice_string_helpers.cc \ src/core/lib/slice/slice_string_helpers.h \ -src/core/lib/support/alloc.c \ -src/core/lib/support/arena.c \ +src/core/lib/support/alloc.cc \ +src/core/lib/support/arena.cc \ src/core/lib/support/arena.h \ -src/core/lib/support/atm.c \ +src/core/lib/support/atm.cc \ src/core/lib/support/atomic.h \ src/core/lib/support/atomic_with_atm.h \ src/core/lib/support/atomic_with_std.h \ -src/core/lib/support/avl.c \ -src/core/lib/support/backoff.c \ +src/core/lib/support/avl.cc \ +src/core/lib/support/backoff.cc \ src/core/lib/support/backoff.h \ src/core/lib/support/block_annotate.h \ -src/core/lib/support/cmdline.c \ -src/core/lib/support/cpu_iphone.c \ -src/core/lib/support/cpu_linux.c \ -src/core/lib/support/cpu_posix.c \ -src/core/lib/support/cpu_windows.c \ +src/core/lib/support/cmdline.cc \ +src/core/lib/support/cpu_iphone.cc \ +src/core/lib/support/cpu_linux.cc \ +src/core/lib/support/cpu_posix.cc \ +src/core/lib/support/cpu_windows.cc \ src/core/lib/support/env.h \ -src/core/lib/support/env_linux.c \ -src/core/lib/support/env_posix.c \ -src/core/lib/support/env_windows.c \ -src/core/lib/support/histogram.c \ -src/core/lib/support/host_port.c \ -src/core/lib/support/log.c \ -src/core/lib/support/log_android.c \ -src/core/lib/support/log_linux.c \ -src/core/lib/support/log_posix.c \ -src/core/lib/support/log_windows.c \ +src/core/lib/support/env_linux.cc \ +src/core/lib/support/env_posix.cc \ +src/core/lib/support/env_windows.cc \ +src/core/lib/support/histogram.cc \ +src/core/lib/support/host_port.cc \ +src/core/lib/support/log.cc \ +src/core/lib/support/log_android.cc \ +src/core/lib/support/log_linux.cc \ +src/core/lib/support/log_posix.cc \ +src/core/lib/support/log_windows.cc \ src/core/lib/support/memory.h \ -src/core/lib/support/mpscq.c \ +src/core/lib/support/mpscq.cc \ src/core/lib/support/mpscq.h \ -src/core/lib/support/murmur_hash.c \ +src/core/lib/support/murmur_hash.cc \ src/core/lib/support/murmur_hash.h \ src/core/lib/support/spinlock.h \ -src/core/lib/support/stack_lockfree.c \ +src/core/lib/support/stack_lockfree.cc \ src/core/lib/support/stack_lockfree.h \ -src/core/lib/support/string.c \ +src/core/lib/support/string.cc \ src/core/lib/support/string.h \ -src/core/lib/support/string_posix.c \ -src/core/lib/support/string_util_windows.c \ -src/core/lib/support/string_windows.c \ +src/core/lib/support/string_posix.cc \ +src/core/lib/support/string_util_windows.cc \ +src/core/lib/support/string_windows.cc \ src/core/lib/support/string_windows.h \ -src/core/lib/support/subprocess_posix.c \ -src/core/lib/support/subprocess_windows.c \ -src/core/lib/support/sync.c \ -src/core/lib/support/sync_posix.c \ -src/core/lib/support/sync_windows.c \ -src/core/lib/support/thd.c \ -src/core/lib/support/thd_posix.c \ -src/core/lib/support/thd_windows.c \ -src/core/lib/support/time.c \ -src/core/lib/support/time_posix.c \ -src/core/lib/support/time_precise.c \ +src/core/lib/support/subprocess_posix.cc \ +src/core/lib/support/subprocess_windows.cc \ +src/core/lib/support/sync.cc \ +src/core/lib/support/sync_posix.cc \ +src/core/lib/support/sync_windows.cc \ +src/core/lib/support/thd.cc \ +src/core/lib/support/thd_posix.cc \ +src/core/lib/support/thd_windows.cc \ +src/core/lib/support/time.cc \ +src/core/lib/support/time_posix.cc \ +src/core/lib/support/time_precise.cc \ src/core/lib/support/time_precise.h \ -src/core/lib/support/time_windows.c \ -src/core/lib/support/tls_pthread.c \ +src/core/lib/support/time_windows.cc \ +src/core/lib/support/tls_pthread.cc \ src/core/lib/support/tmpfile.h \ -src/core/lib/support/tmpfile_msys.c \ -src/core/lib/support/tmpfile_posix.c \ -src/core/lib/support/tmpfile_windows.c \ -src/core/lib/support/wrap_memcpy.c \ +src/core/lib/support/tmpfile_msys.cc \ +src/core/lib/support/tmpfile_posix.cc \ +src/core/lib/support/tmpfile_windows.cc \ +src/core/lib/support/wrap_memcpy.cc \ src/core/lib/surface/README.md \ -src/core/lib/surface/alarm.c \ +src/core/lib/surface/alarm.cc \ src/core/lib/surface/alarm_internal.h \ -src/core/lib/surface/api_trace.c \ +src/core/lib/surface/api_trace.cc \ src/core/lib/surface/api_trace.h \ -src/core/lib/surface/byte_buffer.c \ -src/core/lib/surface/byte_buffer_reader.c \ -src/core/lib/surface/call.c \ +src/core/lib/surface/byte_buffer.cc \ +src/core/lib/surface/byte_buffer_reader.cc \ +src/core/lib/surface/call.cc \ src/core/lib/surface/call.h \ -src/core/lib/surface/call_details.c \ -src/core/lib/surface/call_log_batch.c \ +src/core/lib/surface/call_details.cc \ +src/core/lib/surface/call_log_batch.cc \ src/core/lib/surface/call_test_only.h \ -src/core/lib/surface/channel.c \ +src/core/lib/surface/channel.cc \ src/core/lib/surface/channel.h \ -src/core/lib/surface/channel_init.c \ +src/core/lib/surface/channel_init.cc \ src/core/lib/surface/channel_init.h \ -src/core/lib/surface/channel_ping.c \ -src/core/lib/surface/channel_stack_type.c \ +src/core/lib/surface/channel_ping.cc \ +src/core/lib/surface/channel_stack_type.cc \ src/core/lib/surface/channel_stack_type.h \ -src/core/lib/surface/completion_queue.c \ +src/core/lib/surface/completion_queue.cc \ src/core/lib/surface/completion_queue.h \ -src/core/lib/surface/completion_queue_factory.c \ +src/core/lib/surface/completion_queue_factory.cc \ src/core/lib/surface/completion_queue_factory.h \ -src/core/lib/surface/event_string.c \ +src/core/lib/surface/event_string.cc \ src/core/lib/surface/event_string.h \ -src/core/lib/surface/init.c \ +src/core/lib/surface/init.cc \ src/core/lib/surface/init.h \ -src/core/lib/surface/init_secure.c \ +src/core/lib/surface/init_secure.cc \ src/core/lib/surface/lame_client.cc \ src/core/lib/surface/lame_client.h \ -src/core/lib/surface/metadata_array.c \ -src/core/lib/surface/server.c \ +src/core/lib/surface/metadata_array.cc \ +src/core/lib/surface/server.cc \ src/core/lib/surface/server.h \ -src/core/lib/surface/validate_metadata.c \ +src/core/lib/surface/validate_metadata.cc \ src/core/lib/surface/validate_metadata.h \ -src/core/lib/surface/version.c \ +src/core/lib/surface/version.cc \ src/core/lib/transport/README.md \ -src/core/lib/transport/bdp_estimator.c \ +src/core/lib/transport/bdp_estimator.cc \ src/core/lib/transport/bdp_estimator.h \ -src/core/lib/transport/byte_stream.c \ +src/core/lib/transport/byte_stream.cc \ src/core/lib/transport/byte_stream.h \ -src/core/lib/transport/connectivity_state.c \ +src/core/lib/transport/connectivity_state.cc \ src/core/lib/transport/connectivity_state.h \ -src/core/lib/transport/error_utils.c \ +src/core/lib/transport/error_utils.cc \ src/core/lib/transport/error_utils.h \ src/core/lib/transport/http2_errors.h \ -src/core/lib/transport/metadata.c \ +src/core/lib/transport/metadata.cc \ src/core/lib/transport/metadata.h \ -src/core/lib/transport/metadata_batch.c \ +src/core/lib/transport/metadata_batch.cc \ src/core/lib/transport/metadata_batch.h \ -src/core/lib/transport/pid_controller.c \ +src/core/lib/transport/pid_controller.cc \ src/core/lib/transport/pid_controller.h \ -src/core/lib/transport/service_config.c \ +src/core/lib/transport/service_config.cc \ src/core/lib/transport/service_config.h \ -src/core/lib/transport/static_metadata.c \ +src/core/lib/transport/static_metadata.cc \ src/core/lib/transport/static_metadata.h \ -src/core/lib/transport/status_conversion.c \ +src/core/lib/transport/status_conversion.cc \ src/core/lib/transport/status_conversion.h \ -src/core/lib/transport/timeout_encoding.c \ +src/core/lib/transport/timeout_encoding.cc \ src/core/lib/transport/timeout_encoding.h \ -src/core/lib/transport/transport.c \ +src/core/lib/transport/transport.cc \ src/core/lib/transport/transport.h \ src/core/lib/transport/transport_impl.h \ -src/core/lib/transport/transport_op_string.c \ +src/core/lib/transport/transport_op_string.cc \ src/core/plugin_registry/grpc_plugin_registry.cc \ src/core/tsi/README.md \ -src/core/tsi/fake_transport_security.c \ +src/core/tsi/fake_transport_security.cc \ src/core/tsi/fake_transport_security.h \ -src/core/tsi/gts_transport_security.c \ +src/core/tsi/gts_transport_security.cc \ src/core/tsi/gts_transport_security.h \ -src/core/tsi/ssl_transport_security.c \ +src/core/tsi/ssl_transport_security.cc \ src/core/tsi/ssl_transport_security.h \ src/core/tsi/ssl_types.h \ -src/core/tsi/transport_security.c \ +src/core/tsi/transport_security.cc \ src/core/tsi/transport_security.h \ -src/core/tsi/transport_security_adapter.c \ +src/core/tsi/transport_security_adapter.cc \ src/core/tsi/transport_security_adapter.h \ -src/core/tsi/transport_security_grpc.c \ +src/core/tsi/transport_security_grpc.cc \ src/core/tsi/transport_security_grpc.h \ src/core/tsi/transport_security_interface.h \ third_party/nanopb/pb.h \ diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index c46eb726c8..906458adac 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -6000,7 +6000,7 @@ "language": "c", "name": "grpc", "src": [ - "src/core/lib/surface/init.c" + "src/core/lib/surface/init.cc" ], "third_party": false, "type": "lib" @@ -6018,7 +6018,7 @@ "language": "c", "name": "grpc_cronet", "src": [ - "src/core/lib/surface/init.c" + "src/core/lib/surface/init.cc" ], "third_party": false, "type": "lib" @@ -6104,8 +6104,8 @@ "language": "c", "name": "grpc_unsecure", "src": [ - "src/core/lib/surface/init.c", - "src/core/lib/surface/init_unsecure.c" + "src/core/lib/surface/init.cc", + "src/core/lib/surface/init_unsecure.cc" ], "third_party": false, "type": "lib" @@ -7722,37 +7722,37 @@ "src": [ "include/grpc/census.h", "src/core/ext/census/aggregation.h", - "src/core/ext/census/base_resources.c", + "src/core/ext/census/base_resources.cc", "src/core/ext/census/base_resources.h", "src/core/ext/census/census_interface.h", "src/core/ext/census/census_rpc_stats.h", - "src/core/ext/census/context.c", - "src/core/ext/census/gen/census.pb.c", + "src/core/ext/census/context.cc", + "src/core/ext/census/gen/census.pb.cc", "src/core/ext/census/gen/census.pb.h", - "src/core/ext/census/gen/trace_context.pb.c", + "src/core/ext/census/gen/trace_context.pb.cc", "src/core/ext/census/gen/trace_context.pb.h", - "src/core/ext/census/grpc_context.c", - "src/core/ext/census/grpc_filter.c", + "src/core/ext/census/grpc_context.cc", + "src/core/ext/census/grpc_filter.cc", "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/grpc_plugin.cc", + "src/core/ext/census/initialize.cc", + "src/core/ext/census/intrusive_hash_map.cc", "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.cc", "src/core/ext/census/mlog.h", - "src/core/ext/census/operation.c", - "src/core/ext/census/placeholders.c", - "src/core/ext/census/resource.c", + "src/core/ext/census/operation.cc", + "src/core/ext/census/placeholders.cc", + "src/core/ext/census/resource.cc", "src/core/ext/census/resource.h", "src/core/ext/census/rpc_metric_id.h", - "src/core/ext/census/trace_context.c", + "src/core/ext/census/trace_context.cc", "src/core/ext/census/trace_context.h", "src/core/ext/census/trace_label.h", "src/core/ext/census/trace_propagation.h", "src/core/ext/census/trace_status.h", "src/core/ext/census/trace_string.h", - "src/core/ext/census/tracing.c", + "src/core/ext/census/tracing.cc", "src/core/ext/census/tracing.h" ], "third_party": false, @@ -7767,52 +7767,52 @@ "language": "c", "name": "gpr_base", "src": [ - "src/core/lib/profiling/basic_timers.c", - "src/core/lib/profiling/stap_timers.c", - "src/core/lib/support/alloc.c", - "src/core/lib/support/arena.c", - "src/core/lib/support/atm.c", - "src/core/lib/support/avl.c", - "src/core/lib/support/backoff.c", - "src/core/lib/support/cmdline.c", - "src/core/lib/support/cpu_iphone.c", - "src/core/lib/support/cpu_linux.c", - "src/core/lib/support/cpu_posix.c", - "src/core/lib/support/cpu_windows.c", - "src/core/lib/support/env_linux.c", - "src/core/lib/support/env_posix.c", - "src/core/lib/support/env_windows.c", - "src/core/lib/support/histogram.c", - "src/core/lib/support/host_port.c", - "src/core/lib/support/log.c", - "src/core/lib/support/log_android.c", - "src/core/lib/support/log_linux.c", - "src/core/lib/support/log_posix.c", - "src/core/lib/support/log_windows.c", - "src/core/lib/support/mpscq.c", - "src/core/lib/support/murmur_hash.c", - "src/core/lib/support/stack_lockfree.c", - "src/core/lib/support/string.c", - "src/core/lib/support/string_posix.c", - "src/core/lib/support/string_util_windows.c", - "src/core/lib/support/string_windows.c", - "src/core/lib/support/subprocess_posix.c", - "src/core/lib/support/subprocess_windows.c", - "src/core/lib/support/sync.c", - "src/core/lib/support/sync_posix.c", - "src/core/lib/support/sync_windows.c", - "src/core/lib/support/thd.c", - "src/core/lib/support/thd_posix.c", - "src/core/lib/support/thd_windows.c", - "src/core/lib/support/time.c", - "src/core/lib/support/time_posix.c", - "src/core/lib/support/time_precise.c", - "src/core/lib/support/time_windows.c", - "src/core/lib/support/tls_pthread.c", - "src/core/lib/support/tmpfile_msys.c", - "src/core/lib/support/tmpfile_posix.c", - "src/core/lib/support/tmpfile_windows.c", - "src/core/lib/support/wrap_memcpy.c" + "src/core/lib/profiling/basic_timers.cc", + "src/core/lib/profiling/stap_timers.cc", + "src/core/lib/support/alloc.cc", + "src/core/lib/support/arena.cc", + "src/core/lib/support/atm.cc", + "src/core/lib/support/avl.cc", + "src/core/lib/support/backoff.cc", + "src/core/lib/support/cmdline.cc", + "src/core/lib/support/cpu_iphone.cc", + "src/core/lib/support/cpu_linux.cc", + "src/core/lib/support/cpu_posix.cc", + "src/core/lib/support/cpu_windows.cc", + "src/core/lib/support/env_linux.cc", + "src/core/lib/support/env_posix.cc", + "src/core/lib/support/env_windows.cc", + "src/core/lib/support/histogram.cc", + "src/core/lib/support/host_port.cc", + "src/core/lib/support/log.cc", + "src/core/lib/support/log_android.cc", + "src/core/lib/support/log_linux.cc", + "src/core/lib/support/log_posix.cc", + "src/core/lib/support/log_windows.cc", + "src/core/lib/support/mpscq.cc", + "src/core/lib/support/murmur_hash.cc", + "src/core/lib/support/stack_lockfree.cc", + "src/core/lib/support/string.cc", + "src/core/lib/support/string_posix.cc", + "src/core/lib/support/string_util_windows.cc", + "src/core/lib/support/string_windows.cc", + "src/core/lib/support/subprocess_posix.cc", + "src/core/lib/support/subprocess_windows.cc", + "src/core/lib/support/sync.cc", + "src/core/lib/support/sync_posix.cc", + "src/core/lib/support/sync_windows.cc", + "src/core/lib/support/thd.cc", + "src/core/lib/support/thd_posix.cc", + "src/core/lib/support/thd_windows.cc", + "src/core/lib/support/time.cc", + "src/core/lib/support/time_posix.cc", + "src/core/lib/support/time_precise.cc", + "src/core/lib/support/time_windows.cc", + "src/core/lib/support/tls_pthread.cc", + "src/core/lib/support/tmpfile_msys.cc", + "src/core/lib/support/tmpfile_posix.cc", + "src/core/lib/support/tmpfile_windows.cc", + "src/core/lib/support/wrap_memcpy.cc" ], "third_party": false, "type": "filegroup" @@ -7997,137 +7997,135 @@ "language": "c", "name": "grpc_base", "src": [ - "src/core/lib/channel/channel_args.c", - "src/core/lib/channel/channel_stack.c", - "src/core/lib/channel/channel_stack_builder.c", - "src/core/lib/channel/connected_channel.c", - "src/core/lib/channel/handshaker.c", - "src/core/lib/channel/handshaker_factory.c", - "src/core/lib/channel/handshaker_registry.c", - "src/core/lib/compression/compression.c", - "src/core/lib/compression/message_compress.c", - "src/core/lib/compression/stream_compression.c", - "src/core/lib/compression/stream_compression_gzip.c", - "src/core/lib/compression/stream_compression_identity.c", - "src/core/lib/debug/stats.c", - "src/core/lib/debug/stats_data.c", - "src/core/lib/http/format_request.c", - "src/core/lib/http/httpcli.c", - "src/core/lib/http/parser.c", - "src/core/lib/iomgr/call_combiner.c", - "src/core/lib/iomgr/closure.c", - "src/core/lib/iomgr/combiner.c", - "src/core/lib/iomgr/endpoint.c", - "src/core/lib/iomgr/endpoint_pair_posix.c", - "src/core/lib/iomgr/endpoint_pair_uv.c", - "src/core/lib/iomgr/endpoint_pair_windows.c", - "src/core/lib/iomgr/error.c", - "src/core/lib/iomgr/ev_epoll1_linux.c", - "src/core/lib/iomgr/ev_epollex_linux.c", - "src/core/lib/iomgr/ev_epollsig_linux.c", - "src/core/lib/iomgr/ev_poll_posix.c", - "src/core/lib/iomgr/ev_posix.c", - "src/core/lib/iomgr/ev_windows.c", - "src/core/lib/iomgr/exec_ctx.c", - "src/core/lib/iomgr/executor.c", - "src/core/lib/iomgr/gethostname_fallback.c", - "src/core/lib/iomgr/gethostname_host_name_max.c", - "src/core/lib/iomgr/gethostname_sysconf.c", - "src/core/lib/iomgr/iocp_windows.c", - "src/core/lib/iomgr/iomgr.c", - "src/core/lib/iomgr/iomgr_posix.c", - "src/core/lib/iomgr/iomgr_uv.c", - "src/core/lib/iomgr/iomgr_windows.c", - "src/core/lib/iomgr/is_epollexclusive_available.c", - "src/core/lib/iomgr/load_file.c", - "src/core/lib/iomgr/lockfree_event.c", - "src/core/lib/iomgr/network_status_tracker.c", - "src/core/lib/iomgr/polling_entity.c", - "src/core/lib/iomgr/pollset_set_uv.c", - "src/core/lib/iomgr/pollset_set_windows.c", - "src/core/lib/iomgr/pollset_uv.c", - "src/core/lib/iomgr/pollset_windows.c", - "src/core/lib/iomgr/resolve_address_posix.c", - "src/core/lib/iomgr/resolve_address_uv.c", - "src/core/lib/iomgr/resolve_address_windows.c", - "src/core/lib/iomgr/resource_quota.c", - "src/core/lib/iomgr/sockaddr_utils.c", - "src/core/lib/iomgr/socket_factory_posix.c", - "src/core/lib/iomgr/socket_mutator.c", - "src/core/lib/iomgr/socket_utils_common_posix.c", - "src/core/lib/iomgr/socket_utils_linux.c", - "src/core/lib/iomgr/socket_utils_posix.c", - "src/core/lib/iomgr/socket_utils_uv.c", - "src/core/lib/iomgr/socket_utils_windows.c", - "src/core/lib/iomgr/socket_windows.c", - "src/core/lib/iomgr/tcp_client_posix.c", - "src/core/lib/iomgr/tcp_client_uv.c", - "src/core/lib/iomgr/tcp_client_windows.c", - "src/core/lib/iomgr/tcp_posix.c", - "src/core/lib/iomgr/tcp_server_posix.c", - "src/core/lib/iomgr/tcp_server_utils_posix_common.c", - "src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.c", - "src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.c", - "src/core/lib/iomgr/tcp_server_uv.c", - "src/core/lib/iomgr/tcp_server_windows.c", - "src/core/lib/iomgr/tcp_uv.c", - "src/core/lib/iomgr/tcp_windows.c", - "src/core/lib/iomgr/time_averaged_stats.c", - "src/core/lib/iomgr/timer_generic.c", - "src/core/lib/iomgr/timer_heap.c", - "src/core/lib/iomgr/timer_manager.c", - "src/core/lib/iomgr/timer_uv.c", - "src/core/lib/iomgr/udp_server.c", - "src/core/lib/iomgr/unix_sockets_posix.c", - "src/core/lib/iomgr/unix_sockets_posix_noop.c", - "src/core/lib/iomgr/wakeup_fd_cv.c", - "src/core/lib/iomgr/wakeup_fd_eventfd.c", - "src/core/lib/iomgr/wakeup_fd_nospecial.c", - "src/core/lib/iomgr/wakeup_fd_pipe.c", - "src/core/lib/iomgr/wakeup_fd_posix.c", - "src/core/lib/json/json.c", - "src/core/lib/json/json_reader.c", - "src/core/lib/json/json_string.c", - "src/core/lib/json/json_writer.c", - "src/core/lib/slice/b64.c", - "src/core/lib/slice/percent_encoding.c", - "src/core/lib/slice/slice.c", - "src/core/lib/slice/slice_buffer.c", - "src/core/lib/slice/slice_hash_table.c", - "src/core/lib/slice/slice_intern.c", - "src/core/lib/slice/slice_string_helpers.c", - "src/core/lib/surface/alarm.c", - "src/core/lib/surface/api_trace.c", - "src/core/lib/surface/byte_buffer.c", - "src/core/lib/surface/byte_buffer_reader.c", - "src/core/lib/surface/call.c", - "src/core/lib/surface/call_details.c", - "src/core/lib/surface/call_log_batch.c", - "src/core/lib/surface/channel.c", - "src/core/lib/surface/channel_init.c", - "src/core/lib/surface/channel_ping.c", - "src/core/lib/surface/channel_stack_type.c", - "src/core/lib/surface/completion_queue.c", - "src/core/lib/surface/completion_queue_factory.c", - "src/core/lib/surface/event_string.c", + "src/core/lib/channel/channel_args.cc", + "src/core/lib/channel/channel_stack.cc", + "src/core/lib/channel/channel_stack_builder.cc", + "src/core/lib/channel/connected_channel.cc", + "src/core/lib/channel/handshaker.cc", + "src/core/lib/channel/handshaker_factory.cc", + "src/core/lib/channel/handshaker_registry.cc", + "src/core/lib/compression/compression.cc", + "src/core/lib/compression/message_compress.cc", + "src/core/lib/compression/stream_compression.cc", + "src/core/lib/debug/stats.cc", + "src/core/lib/debug/stats_data.cc", + "src/core/lib/http/format_request.cc", + "src/core/lib/http/httpcli.cc", + "src/core/lib/http/parser.cc", + "src/core/lib/iomgr/call_combiner.cc", + "src/core/lib/iomgr/closure.cc", + "src/core/lib/iomgr/combiner.cc", + "src/core/lib/iomgr/endpoint.cc", + "src/core/lib/iomgr/endpoint_pair_posix.cc", + "src/core/lib/iomgr/endpoint_pair_uv.cc", + "src/core/lib/iomgr/endpoint_pair_windows.cc", + "src/core/lib/iomgr/error.cc", + "src/core/lib/iomgr/ev_epoll1_linux.cc", + "src/core/lib/iomgr/ev_epollex_linux.cc", + "src/core/lib/iomgr/ev_epollsig_linux.cc", + "src/core/lib/iomgr/ev_poll_posix.cc", + "src/core/lib/iomgr/ev_posix.cc", + "src/core/lib/iomgr/ev_windows.cc", + "src/core/lib/iomgr/exec_ctx.cc", + "src/core/lib/iomgr/executor.cc", + "src/core/lib/iomgr/gethostname_fallback.cc", + "src/core/lib/iomgr/gethostname_host_name_max.cc", + "src/core/lib/iomgr/gethostname_sysconf.cc", + "src/core/lib/iomgr/iocp_windows.cc", + "src/core/lib/iomgr/iomgr.cc", + "src/core/lib/iomgr/iomgr_posix.cc", + "src/core/lib/iomgr/iomgr_uv.cc", + "src/core/lib/iomgr/iomgr_windows.cc", + "src/core/lib/iomgr/is_epollexclusive_available.cc", + "src/core/lib/iomgr/load_file.cc", + "src/core/lib/iomgr/lockfree_event.cc", + "src/core/lib/iomgr/network_status_tracker.cc", + "src/core/lib/iomgr/polling_entity.cc", + "src/core/lib/iomgr/pollset_set_uv.cc", + "src/core/lib/iomgr/pollset_set_windows.cc", + "src/core/lib/iomgr/pollset_uv.cc", + "src/core/lib/iomgr/pollset_windows.cc", + "src/core/lib/iomgr/resolve_address_posix.cc", + "src/core/lib/iomgr/resolve_address_uv.cc", + "src/core/lib/iomgr/resolve_address_windows.cc", + "src/core/lib/iomgr/resource_quota.cc", + "src/core/lib/iomgr/sockaddr_utils.cc", + "src/core/lib/iomgr/socket_factory_posix.cc", + "src/core/lib/iomgr/socket_mutator.cc", + "src/core/lib/iomgr/socket_utils_common_posix.cc", + "src/core/lib/iomgr/socket_utils_linux.cc", + "src/core/lib/iomgr/socket_utils_posix.cc", + "src/core/lib/iomgr/socket_utils_uv.cc", + "src/core/lib/iomgr/socket_utils_windows.cc", + "src/core/lib/iomgr/socket_windows.cc", + "src/core/lib/iomgr/tcp_client_posix.cc", + "src/core/lib/iomgr/tcp_client_uv.cc", + "src/core/lib/iomgr/tcp_client_windows.cc", + "src/core/lib/iomgr/tcp_posix.cc", + "src/core/lib/iomgr/tcp_server_posix.cc", + "src/core/lib/iomgr/tcp_server_utils_posix_common.cc", + "src/core/lib/iomgr/tcp_server_utils_posix_ifaddrs.cc", + "src/core/lib/iomgr/tcp_server_utils_posix_noifaddrs.cc", + "src/core/lib/iomgr/tcp_server_uv.cc", + "src/core/lib/iomgr/tcp_server_windows.cc", + "src/core/lib/iomgr/tcp_uv.cc", + "src/core/lib/iomgr/tcp_windows.cc", + "src/core/lib/iomgr/time_averaged_stats.cc", + "src/core/lib/iomgr/timer_generic.cc", + "src/core/lib/iomgr/timer_heap.cc", + "src/core/lib/iomgr/timer_manager.cc", + "src/core/lib/iomgr/timer_uv.cc", + "src/core/lib/iomgr/udp_server.cc", + "src/core/lib/iomgr/unix_sockets_posix.cc", + "src/core/lib/iomgr/unix_sockets_posix_noop.cc", + "src/core/lib/iomgr/wakeup_fd_cv.cc", + "src/core/lib/iomgr/wakeup_fd_eventfd.cc", + "src/core/lib/iomgr/wakeup_fd_nospecial.cc", + "src/core/lib/iomgr/wakeup_fd_pipe.cc", + "src/core/lib/iomgr/wakeup_fd_posix.cc", + "src/core/lib/json/json.cc", + "src/core/lib/json/json_reader.cc", + "src/core/lib/json/json_string.cc", + "src/core/lib/json/json_writer.cc", + "src/core/lib/slice/b64.cc", + "src/core/lib/slice/percent_encoding.cc", + "src/core/lib/slice/slice.cc", + "src/core/lib/slice/slice_buffer.cc", + "src/core/lib/slice/slice_hash_table.cc", + "src/core/lib/slice/slice_intern.cc", + "src/core/lib/slice/slice_string_helpers.cc", + "src/core/lib/surface/alarm.cc", + "src/core/lib/surface/api_trace.cc", + "src/core/lib/surface/byte_buffer.cc", + "src/core/lib/surface/byte_buffer_reader.cc", + "src/core/lib/surface/call.cc", + "src/core/lib/surface/call_details.cc", + "src/core/lib/surface/call_log_batch.cc", + "src/core/lib/surface/channel.cc", + "src/core/lib/surface/channel_init.cc", + "src/core/lib/surface/channel_ping.cc", + "src/core/lib/surface/channel_stack_type.cc", + "src/core/lib/surface/completion_queue.cc", + "src/core/lib/surface/completion_queue_factory.cc", + "src/core/lib/surface/event_string.cc", "src/core/lib/surface/lame_client.cc", - "src/core/lib/surface/metadata_array.c", - "src/core/lib/surface/server.c", - "src/core/lib/surface/validate_metadata.c", - "src/core/lib/surface/version.c", - "src/core/lib/transport/bdp_estimator.c", - "src/core/lib/transport/byte_stream.c", - "src/core/lib/transport/connectivity_state.c", - "src/core/lib/transport/error_utils.c", - "src/core/lib/transport/metadata.c", - "src/core/lib/transport/metadata_batch.c", - "src/core/lib/transport/pid_controller.c", - "src/core/lib/transport/service_config.c", - "src/core/lib/transport/static_metadata.c", - "src/core/lib/transport/status_conversion.c", - "src/core/lib/transport/timeout_encoding.c", - "src/core/lib/transport/transport.c", - "src/core/lib/transport/transport_op_string.c" + "src/core/lib/surface/metadata_array.cc", + "src/core/lib/surface/server.cc", + "src/core/lib/surface/validate_metadata.cc", + "src/core/lib/surface/version.cc", + "src/core/lib/transport/bdp_estimator.cc", + "src/core/lib/transport/byte_stream.cc", + "src/core/lib/transport/connectivity_state.cc", + "src/core/lib/transport/error_utils.cc", + "src/core/lib/transport/metadata.cc", + "src/core/lib/transport/metadata_batch.cc", + "src/core/lib/transport/pid_controller.cc", + "src/core/lib/transport/service_config.cc", + "src/core/lib/transport/static_metadata.cc", + "src/core/lib/transport/status_conversion.cc", + "src/core/lib/transport/timeout_encoding.cc", + "src/core/lib/transport/transport.cc", + "src/core/lib/transport/transport_op_string.cc" ], "third_party": false, "type": "filegroup" @@ -8434,43 +8432,43 @@ "language": "c", "name": "grpc_client_channel", "src": [ - "src/core/ext/filters/client_channel/channel_connectivity.c", - "src/core/ext/filters/client_channel/client_channel.c", + "src/core/ext/filters/client_channel/channel_connectivity.cc", + "src/core/ext/filters/client_channel/client_channel.cc", "src/core/ext/filters/client_channel/client_channel.h", - "src/core/ext/filters/client_channel/client_channel_factory.c", + "src/core/ext/filters/client_channel/client_channel_factory.cc", "src/core/ext/filters/client_channel/client_channel_factory.h", - "src/core/ext/filters/client_channel/client_channel_plugin.c", - "src/core/ext/filters/client_channel/connector.c", + "src/core/ext/filters/client_channel/client_channel_plugin.cc", + "src/core/ext/filters/client_channel/connector.cc", "src/core/ext/filters/client_channel/connector.h", - "src/core/ext/filters/client_channel/http_connect_handshaker.c", + "src/core/ext/filters/client_channel/http_connect_handshaker.cc", "src/core/ext/filters/client_channel/http_connect_handshaker.h", - "src/core/ext/filters/client_channel/http_proxy.c", + "src/core/ext/filters/client_channel/http_proxy.cc", "src/core/ext/filters/client_channel/http_proxy.h", - "src/core/ext/filters/client_channel/lb_policy.c", + "src/core/ext/filters/client_channel/lb_policy.cc", "src/core/ext/filters/client_channel/lb_policy.h", - "src/core/ext/filters/client_channel/lb_policy_factory.c", + "src/core/ext/filters/client_channel/lb_policy_factory.cc", "src/core/ext/filters/client_channel/lb_policy_factory.h", - "src/core/ext/filters/client_channel/lb_policy_registry.c", + "src/core/ext/filters/client_channel/lb_policy_registry.cc", "src/core/ext/filters/client_channel/lb_policy_registry.h", - "src/core/ext/filters/client_channel/parse_address.c", + "src/core/ext/filters/client_channel/parse_address.cc", "src/core/ext/filters/client_channel/parse_address.h", - "src/core/ext/filters/client_channel/proxy_mapper.c", + "src/core/ext/filters/client_channel/proxy_mapper.cc", "src/core/ext/filters/client_channel/proxy_mapper.h", - "src/core/ext/filters/client_channel/proxy_mapper_registry.c", + "src/core/ext/filters/client_channel/proxy_mapper_registry.cc", "src/core/ext/filters/client_channel/proxy_mapper_registry.h", - "src/core/ext/filters/client_channel/resolver.c", + "src/core/ext/filters/client_channel/resolver.cc", "src/core/ext/filters/client_channel/resolver.h", - "src/core/ext/filters/client_channel/resolver_factory.c", + "src/core/ext/filters/client_channel/resolver_factory.cc", "src/core/ext/filters/client_channel/resolver_factory.h", - "src/core/ext/filters/client_channel/resolver_registry.c", + "src/core/ext/filters/client_channel/resolver_registry.cc", "src/core/ext/filters/client_channel/resolver_registry.h", - "src/core/ext/filters/client_channel/retry_throttle.c", + "src/core/ext/filters/client_channel/retry_throttle.cc", "src/core/ext/filters/client_channel/retry_throttle.h", - "src/core/ext/filters/client_channel/subchannel.c", + "src/core/ext/filters/client_channel/subchannel.cc", "src/core/ext/filters/client_channel/subchannel.h", - "src/core/ext/filters/client_channel/subchannel_index.c", + "src/core/ext/filters/client_channel/subchannel_index.cc", "src/core/ext/filters/client_channel/subchannel_index.h", - "src/core/ext/filters/client_channel/uri_parser.c", + "src/core/ext/filters/client_channel/uri_parser.cc", "src/core/ext/filters/client_channel/uri_parser.h" ], "third_party": false, @@ -8520,7 +8518,7 @@ "language": "c", "name": "grpc_deadline_filter", "src": [ - "src/core/ext/filters/deadline/deadline_filter.c", + "src/core/ext/filters/deadline/deadline_filter.cc", "src/core/ext/filters/deadline/deadline_filter.h" ], "third_party": false, @@ -8540,12 +8538,12 @@ "language": "c", "name": "grpc_http_filters", "src": [ - "src/core/ext/filters/http/client/http_client_filter.c", + "src/core/ext/filters/http/client/http_client_filter.cc", "src/core/ext/filters/http/client/http_client_filter.h", - "src/core/ext/filters/http/http_filters_plugin.c", - "src/core/ext/filters/http/message_compress/message_compress_filter.c", + "src/core/ext/filters/http/http_filters_plugin.cc", + "src/core/ext/filters/http/message_compress/message_compress_filter.cc", "src/core/ext/filters/http/message_compress/message_compress_filter.h", - "src/core/ext/filters/http/server/http_server_filter.c", + "src/core/ext/filters/http/server/http_server_filter.cc", "src/core/ext/filters/http/server/http_server_filter.h" ], "third_party": false, @@ -8571,17 +8569,17 @@ "language": "c", "name": "grpc_lb_policy_grpclb", "src": [ - "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" ], "third_party": false, @@ -8608,17 +8606,17 @@ "language": "c", "name": "grpc_lb_policy_grpclb_secure", "src": [ - "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/client_load_reporting_filter.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.h", "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.c", - "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" ], "third_party": false, @@ -8635,7 +8633,7 @@ "language": "c", "name": "grpc_lb_policy_pick_first", "src": [ - "src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.c" + "src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc" ], "third_party": false, "type": "filegroup" @@ -8651,7 +8649,7 @@ "language": "c", "name": "grpc_lb_policy_round_robin", "src": [ - "src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.c" + "src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc" ], "third_party": false, "type": "filegroup" @@ -8668,7 +8666,7 @@ "language": "c", "name": "grpc_max_age_filter", "src": [ - "src/core/ext/filters/max_age/max_age_filter.c", + "src/core/ext/filters/max_age/max_age_filter.cc", "src/core/ext/filters/max_age/max_age_filter.h" ], "third_party": false, @@ -8686,7 +8684,7 @@ "language": "c", "name": "grpc_message_size_filter", "src": [ - "src/core/ext/filters/message_size/message_size_filter.c", + "src/core/ext/filters/message_size/message_size_filter.cc", "src/core/ext/filters/message_size/message_size_filter.h" ], "third_party": false, @@ -8706,12 +8704,12 @@ "language": "c", "name": "grpc_resolver_dns_ares", "src": [ - "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.c", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/dns_resolver_ares.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver.h", - "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.c", - "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.c", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_ev_driver_posix.cc", + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.cc", "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper.h", - "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.c" + "src/core/ext/filters/client_channel/resolver/dns/c_ares/grpc_ares_wrapper_fallback.cc" ], "third_party": false, "type": "filegroup" @@ -8727,7 +8725,7 @@ "language": "c", "name": "grpc_resolver_dns_native", "src": [ - "src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.c" + "src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc" ], "third_party": false, "type": "filegroup" @@ -8745,7 +8743,7 @@ "language": "c", "name": "grpc_resolver_fake", "src": [ - "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c", + "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc", "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h" ], "third_party": false, @@ -8762,7 +8760,7 @@ "language": "c", "name": "grpc_resolver_sockaddr", "src": [ - "src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.c" + "src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc" ], "third_party": false, "type": "filegroup" @@ -8801,49 +8799,49 @@ "name": "grpc_secure", "src": [ "include/grpc/grpc_security.h", - "src/core/lib/http/httpcli_security_connector.c", - "src/core/lib/security/context/security_context.c", + "src/core/lib/http/httpcli_security_connector.cc", + "src/core/lib/security/context/security_context.cc", "src/core/lib/security/context/security_context.h", - "src/core/lib/security/credentials/composite/composite_credentials.c", + "src/core/lib/security/credentials/composite/composite_credentials.cc", "src/core/lib/security/credentials/composite/composite_credentials.h", - "src/core/lib/security/credentials/credentials.c", + "src/core/lib/security/credentials/credentials.cc", "src/core/lib/security/credentials/credentials.h", - "src/core/lib/security/credentials/credentials_metadata.c", - "src/core/lib/security/credentials/fake/fake_credentials.c", + "src/core/lib/security/credentials/credentials_metadata.cc", + "src/core/lib/security/credentials/fake/fake_credentials.cc", "src/core/lib/security/credentials/fake/fake_credentials.h", - "src/core/lib/security/credentials/google_default/credentials_generic.c", - "src/core/lib/security/credentials/google_default/google_default_credentials.c", + "src/core/lib/security/credentials/google_default/credentials_generic.cc", + "src/core/lib/security/credentials/google_default/google_default_credentials.cc", "src/core/lib/security/credentials/google_default/google_default_credentials.h", - "src/core/lib/security/credentials/iam/iam_credentials.c", + "src/core/lib/security/credentials/iam/iam_credentials.cc", "src/core/lib/security/credentials/iam/iam_credentials.h", - "src/core/lib/security/credentials/jwt/json_token.c", + "src/core/lib/security/credentials/jwt/json_token.cc", "src/core/lib/security/credentials/jwt/json_token.h", - "src/core/lib/security/credentials/jwt/jwt_credentials.c", + "src/core/lib/security/credentials/jwt/jwt_credentials.cc", "src/core/lib/security/credentials/jwt/jwt_credentials.h", - "src/core/lib/security/credentials/jwt/jwt_verifier.c", + "src/core/lib/security/credentials/jwt/jwt_verifier.cc", "src/core/lib/security/credentials/jwt/jwt_verifier.h", - "src/core/lib/security/credentials/oauth2/oauth2_credentials.c", + "src/core/lib/security/credentials/oauth2/oauth2_credentials.cc", "src/core/lib/security/credentials/oauth2/oauth2_credentials.h", - "src/core/lib/security/credentials/plugin/plugin_credentials.c", + "src/core/lib/security/credentials/plugin/plugin_credentials.cc", "src/core/lib/security/credentials/plugin/plugin_credentials.h", - "src/core/lib/security/credentials/ssl/ssl_credentials.c", + "src/core/lib/security/credentials/ssl/ssl_credentials.cc", "src/core/lib/security/credentials/ssl/ssl_credentials.h", "src/core/lib/security/transport/auth_filters.h", - "src/core/lib/security/transport/client_auth_filter.c", - "src/core/lib/security/transport/lb_targets_info.c", + "src/core/lib/security/transport/client_auth_filter.cc", + "src/core/lib/security/transport/lb_targets_info.cc", "src/core/lib/security/transport/lb_targets_info.h", - "src/core/lib/security/transport/secure_endpoint.c", + "src/core/lib/security/transport/secure_endpoint.cc", "src/core/lib/security/transport/secure_endpoint.h", - "src/core/lib/security/transport/security_connector.c", + "src/core/lib/security/transport/security_connector.cc", "src/core/lib/security/transport/security_connector.h", - "src/core/lib/security/transport/security_handshaker.c", + "src/core/lib/security/transport/security_handshaker.cc", "src/core/lib/security/transport/security_handshaker.h", - "src/core/lib/security/transport/server_auth_filter.c", - "src/core/lib/security/transport/tsi_error.c", + "src/core/lib/security/transport/server_auth_filter.cc", + "src/core/lib/security/transport/tsi_error.cc", "src/core/lib/security/transport/tsi_error.h", - "src/core/lib/security/util/json_util.c", + "src/core/lib/security/util/json_util.cc", "src/core/lib/security/util/json_util.h", - "src/core/lib/surface/init_secure.c" + "src/core/lib/surface/init_secure.cc" ], "third_party": false, "type": "filegroup" @@ -8860,7 +8858,7 @@ "language": "c", "name": "grpc_server_backward_compatibility", "src": [ - "src/core/ext/filters/workarounds/workaround_utils.c", + "src/core/ext/filters/workarounds/workaround_utils.cc", "src/core/ext/filters/workarounds/workaround_utils.h" ], "third_party": false, @@ -8879,9 +8877,9 @@ "language": "c", "name": "grpc_server_load_reporting", "src": [ - "src/core/ext/filters/load_reporting/server_load_reporting_filter.c", + "src/core/ext/filters/load_reporting/server_load_reporting_filter.cc", "src/core/ext/filters/load_reporting/server_load_reporting_filter.h", - "src/core/ext/filters/load_reporting/server_load_reporting_plugin.c", + "src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc", "src/core/ext/filters/load_reporting/server_load_reporting_plugin.h" ], "third_party": false, @@ -8916,35 +8914,35 @@ "language": "c", "name": "grpc_test_util_base", "src": [ - "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.c", + "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc", "src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h", - "test/core/end2end/cq_verifier.c", + "test/core/end2end/cq_verifier.cc", "test/core/end2end/cq_verifier.h", - "test/core/end2end/fixtures/http_proxy_fixture.c", + "test/core/end2end/fixtures/http_proxy_fixture.cc", "test/core/end2end/fixtures/http_proxy_fixture.h", - "test/core/end2end/fixtures/proxy.c", + "test/core/end2end/fixtures/proxy.cc", "test/core/end2end/fixtures/proxy.h", - "test/core/iomgr/endpoint_tests.c", + "test/core/iomgr/endpoint_tests.cc", "test/core/iomgr/endpoint_tests.h", - "test/core/util/debugger_macros.c", + "test/core/util/debugger_macros.cc", "test/core/util/debugger_macros.h", - "test/core/util/grpc_profiler.c", + "test/core/util/grpc_profiler.cc", "test/core/util/grpc_profiler.h", - "test/core/util/memory_counters.c", + "test/core/util/memory_counters.cc", "test/core/util/memory_counters.h", - "test/core/util/mock_endpoint.c", + "test/core/util/mock_endpoint.cc", "test/core/util/mock_endpoint.h", - "test/core/util/parse_hexstring.c", + "test/core/util/parse_hexstring.cc", "test/core/util/parse_hexstring.h", - "test/core/util/passthru_endpoint.c", + "test/core/util/passthru_endpoint.cc", "test/core/util/passthru_endpoint.h", - "test/core/util/port.c", + "test/core/util/port.cc", "test/core/util/port.h", - "test/core/util/port_server_client.c", + "test/core/util/port_server_client.cc", "test/core/util/port_server_client.h", - "test/core/util/slice_splitter.c", + "test/core/util/slice_splitter.cc", "test/core/util/slice_splitter.h", - "test/core/util/trickle_endpoint.c", + "test/core/util/trickle_endpoint.cc", "test/core/util/trickle_endpoint.h" ], "third_party": false, @@ -8960,7 +8958,7 @@ "language": "c", "name": "grpc_trace", "src": [ - "src/core/lib/debug/trace.c" + "src/core/lib/debug/trace.cc" ], "third_party": false, "type": "filegroup" @@ -9013,47 +9011,47 @@ "language": "c", "name": "grpc_transport_chttp2", "src": [ - "src/core/ext/transport/chttp2/transport/bin_decoder.c", + "src/core/ext/transport/chttp2/transport/bin_decoder.cc", "src/core/ext/transport/chttp2/transport/bin_decoder.h", - "src/core/ext/transport/chttp2/transport/bin_encoder.c", + "src/core/ext/transport/chttp2/transport/bin_encoder.cc", "src/core/ext/transport/chttp2/transport/bin_encoder.h", - "src/core/ext/transport/chttp2/transport/chttp2_plugin.c", - "src/core/ext/transport/chttp2/transport/chttp2_transport.c", + "src/core/ext/transport/chttp2/transport/chttp2_plugin.cc", + "src/core/ext/transport/chttp2/transport/chttp2_transport.cc", "src/core/ext/transport/chttp2/transport/chttp2_transport.h", - "src/core/ext/transport/chttp2/transport/flow_control.c", + "src/core/ext/transport/chttp2/transport/flow_control.cc", "src/core/ext/transport/chttp2/transport/frame.h", - "src/core/ext/transport/chttp2/transport/frame_data.c", + "src/core/ext/transport/chttp2/transport/frame_data.cc", "src/core/ext/transport/chttp2/transport/frame_data.h", - "src/core/ext/transport/chttp2/transport/frame_goaway.c", + "src/core/ext/transport/chttp2/transport/frame_goaway.cc", "src/core/ext/transport/chttp2/transport/frame_goaway.h", - "src/core/ext/transport/chttp2/transport/frame_ping.c", + "src/core/ext/transport/chttp2/transport/frame_ping.cc", "src/core/ext/transport/chttp2/transport/frame_ping.h", - "src/core/ext/transport/chttp2/transport/frame_rst_stream.c", + "src/core/ext/transport/chttp2/transport/frame_rst_stream.cc", "src/core/ext/transport/chttp2/transport/frame_rst_stream.h", - "src/core/ext/transport/chttp2/transport/frame_settings.c", + "src/core/ext/transport/chttp2/transport/frame_settings.cc", "src/core/ext/transport/chttp2/transport/frame_settings.h", - "src/core/ext/transport/chttp2/transport/frame_window_update.c", + "src/core/ext/transport/chttp2/transport/frame_window_update.cc", "src/core/ext/transport/chttp2/transport/frame_window_update.h", - "src/core/ext/transport/chttp2/transport/hpack_encoder.c", + "src/core/ext/transport/chttp2/transport/hpack_encoder.cc", "src/core/ext/transport/chttp2/transport/hpack_encoder.h", - "src/core/ext/transport/chttp2/transport/hpack_parser.c", + "src/core/ext/transport/chttp2/transport/hpack_parser.cc", "src/core/ext/transport/chttp2/transport/hpack_parser.h", - "src/core/ext/transport/chttp2/transport/hpack_table.c", + "src/core/ext/transport/chttp2/transport/hpack_table.cc", "src/core/ext/transport/chttp2/transport/hpack_table.h", - "src/core/ext/transport/chttp2/transport/http2_settings.c", + "src/core/ext/transport/chttp2/transport/http2_settings.cc", "src/core/ext/transport/chttp2/transport/http2_settings.h", - "src/core/ext/transport/chttp2/transport/huffsyms.c", + "src/core/ext/transport/chttp2/transport/huffsyms.cc", "src/core/ext/transport/chttp2/transport/huffsyms.h", - "src/core/ext/transport/chttp2/transport/incoming_metadata.c", + "src/core/ext/transport/chttp2/transport/incoming_metadata.cc", "src/core/ext/transport/chttp2/transport/incoming_metadata.h", "src/core/ext/transport/chttp2/transport/internal.h", - "src/core/ext/transport/chttp2/transport/parsing.c", - "src/core/ext/transport/chttp2/transport/stream_lists.c", - "src/core/ext/transport/chttp2/transport/stream_map.c", + "src/core/ext/transport/chttp2/transport/parsing.cc", + "src/core/ext/transport/chttp2/transport/stream_lists.cc", + "src/core/ext/transport/chttp2/transport/stream_map.cc", "src/core/ext/transport/chttp2/transport/stream_map.h", - "src/core/ext/transport/chttp2/transport/varint.c", + "src/core/ext/transport/chttp2/transport/varint.cc", "src/core/ext/transport/chttp2/transport/varint.h", - "src/core/ext/transport/chttp2/transport/writing.c" + "src/core/ext/transport/chttp2/transport/writing.cc" ], "third_party": false, "type": "filegroup" @@ -9069,7 +9067,7 @@ "language": "c", "name": "grpc_transport_chttp2_alpn", "src": [ - "src/core/ext/transport/chttp2/alpn/alpn.c", + "src/core/ext/transport/chttp2/alpn/alpn.cc", "src/core/ext/transport/chttp2/alpn/alpn.h" ], "third_party": false, @@ -9089,7 +9087,7 @@ "language": "c", "name": "grpc_transport_chttp2_client_connector", "src": [ - "src/core/ext/transport/chttp2/client/chttp2_connector.c", + "src/core/ext/transport/chttp2/client/chttp2_connector.cc", "src/core/ext/transport/chttp2/client/chttp2_connector.h" ], "third_party": false, @@ -9108,8 +9106,8 @@ "language": "c", "name": "grpc_transport_chttp2_client_insecure", "src": [ - "src/core/ext/transport/chttp2/client/insecure/channel_create.c", - "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.c" + "src/core/ext/transport/chttp2/client/insecure/channel_create.cc", + "src/core/ext/transport/chttp2/client/insecure/channel_create_posix.cc" ], "third_party": false, "type": "filegroup" @@ -9128,7 +9126,7 @@ "language": "c", "name": "grpc_transport_chttp2_client_secure", "src": [ - "src/core/ext/transport/chttp2/client/secure/secure_channel_create.c" + "src/core/ext/transport/chttp2/client/secure/secure_channel_create.cc" ], "third_party": false, "type": "filegroup" @@ -9146,7 +9144,7 @@ "language": "c", "name": "grpc_transport_chttp2_server", "src": [ - "src/core/ext/transport/chttp2/server/chttp2_server.c", + "src/core/ext/transport/chttp2/server/chttp2_server.cc", "src/core/ext/transport/chttp2/server/chttp2_server.h" ], "third_party": false, @@ -9164,8 +9162,8 @@ "language": "c", "name": "grpc_transport_chttp2_server_insecure", "src": [ - "src/core/ext/transport/chttp2/server/insecure/server_chttp2.c", - "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.c" + "src/core/ext/transport/chttp2/server/insecure/server_chttp2.cc", + "src/core/ext/transport/chttp2/server/insecure/server_chttp2_posix.cc" ], "third_party": false, "type": "filegroup" @@ -9183,7 +9181,7 @@ "language": "c", "name": "grpc_transport_chttp2_server_secure", "src": [ - "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.c" + "src/core/ext/transport/chttp2/server/secure/server_secure_chttp2.cc" ], "third_party": false, "type": "filegroup" @@ -9208,9 +9206,9 @@ "include/grpc/grpc_cronet.h", "include/grpc/grpc_security.h", "include/grpc/grpc_security_constants.h", - "src/core/ext/transport/cronet/client/secure/cronet_channel_create.c", - "src/core/ext/transport/cronet/transport/cronet_api_dummy.c", - "src/core/ext/transport/cronet/transport/cronet_transport.c", + "src/core/ext/transport/cronet/client/secure/cronet_channel_create.cc", + "src/core/ext/transport/cronet/transport/cronet_api_dummy.cc", + "src/core/ext/transport/cronet/transport/cronet_transport.cc", "src/core/ext/transport/cronet/transport/cronet_transport.h" ], "third_party": false, @@ -9227,8 +9225,8 @@ "language": "c", "name": "grpc_transport_inproc", "src": [ - "src/core/ext/transport/inproc/inproc_plugin.c", - "src/core/ext/transport/inproc/inproc_transport.c" + "src/core/ext/transport/inproc/inproc_plugin.cc", + "src/core/ext/transport/inproc/inproc_transport.cc" ], "third_party": false, "type": "filegroup" @@ -9263,7 +9261,7 @@ "language": "c", "name": "grpc_workaround_cronet_compression_filter", "src": [ - "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.c", + "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.cc", "src/core/ext/filters/workarounds/workaround_cronet_compression_filter.h" ], "third_party": false, @@ -9331,14 +9329,14 @@ "language": "c", "name": "tsi", "src": [ - "src/core/tsi/fake_transport_security.c", + "src/core/tsi/fake_transport_security.cc", "src/core/tsi/fake_transport_security.h", - "src/core/tsi/gts_transport_security.c", + "src/core/tsi/gts_transport_security.cc", "src/core/tsi/gts_transport_security.h", - "src/core/tsi/ssl_transport_security.c", + "src/core/tsi/ssl_transport_security.cc", "src/core/tsi/ssl_transport_security.h", "src/core/tsi/ssl_types.h", - "src/core/tsi/transport_security_grpc.c", + "src/core/tsi/transport_security_grpc.cc", "src/core/tsi/transport_security_grpc.h" ], "third_party": false, @@ -9358,9 +9356,9 @@ "language": "c", "name": "tsi_interface", "src": [ - "src/core/tsi/transport_security.c", + "src/core/tsi/transport_security.cc", "src/core/tsi/transport_security.h", - "src/core/tsi/transport_security_adapter.c", + "src/core/tsi/transport_security_adapter.cc", "src/core/tsi/transport_security_adapter.h", "src/core/tsi/transport_security_interface.h" ], -- cgit v1.2.3 From 4a91bf47d074a54d4405ba48d8799d458883ee82 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Fri, 22 Sep 2017 12:05:25 -0700 Subject: Fixing build.yaml. More pointer conversions and changes required by other builds --- build.yaml | 28 ++++++++++++++-------------- src/core/lib/support/env_windows.cc | 2 +- src/core/lib/support/log_posix.cc | 2 +- src/core/lib/surface/call.cc | 9 ++++++--- 4 files changed, 22 insertions(+), 19 deletions(-) (limited to 'src/core/lib/support') diff --git a/build.yaml b/build.yaml index 4fc7a85712..8d610bae74 100644 --- a/build.yaml +++ b/build.yaml @@ -735,20 +735,20 @@ filegroups: - test/core/util/trickle_endpoint.h src: - src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc - - test/core/end2end/cq_verifier.cc - - test/core/end2end/fixtures/http_proxy_fixture.cc - - test/core/end2end/fixtures/proxy.cc - - test/core/iomgr/endpoint_tests.cc - - test/core/util/debugger_macros.cc - - test/core/util/grpc_profiler.cc - - test/core/util/memory_counters.cc - - test/core/util/mock_endpoint.cc - - test/core/util/parse_hexstring.cc - - test/core/util/passthru_endpoint.cc - - test/core/util/port.cc - - test/core/util/port_server_client.cc - - test/core/util/slice_splitter.cc - - test/core/util/trickle_endpoint.cc + - test/core/end2end/cq_verifier.c + - test/core/end2end/fixtures/http_proxy_fixture.c + - test/core/end2end/fixtures/proxy.c + - test/core/iomgr/endpoint_tests.c + - test/core/util/debugger_macros.c + - test/core/util/grpc_profiler.c + - test/core/util/memory_counters.c + - test/core/util/mock_endpoint.c + - test/core/util/parse_hexstring.c + - test/core/util/passthru_endpoint.c + - test/core/util/port.c + - test/core/util/port_server_client.c + - test/core/util/slice_splitter.c + - test/core/util/trickle_endpoint.c deps: - gpr_test_util - gpr diff --git a/src/core/lib/support/env_windows.cc b/src/core/lib/support/env_windows.cc index 652eeb61c6..73c643c560 100644 --- a/src/core/lib/support/env_windows.cc +++ b/src/core/lib/support/env_windows.cc @@ -45,7 +45,7 @@ char *gpr_getenv(const char *name) { ret = GetEnvironmentVariable(tname, NULL, 0); if (ret == 0) return NULL; size = ret * (DWORD)sizeof(TCHAR); - tresult = gpr_malloc(size); + tresult = (LPTSTR)gpr_malloc(size); ret = GetEnvironmentVariable(tname, tresult, size); gpr_free(tname); if (ret == 0) { diff --git a/src/core/lib/support/log_posix.cc b/src/core/lib/support/log_posix.cc index 855e8e7107..38a136e646 100644 --- a/src/core/lib/support/log_posix.cc +++ b/src/core/lib/support/log_posix.cc @@ -48,7 +48,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, } else if ((size_t)ret <= sizeof(buf) - 1) { message = buf; } else { - message = allocated = gpr_malloc((size_t)ret + 1); + message = allocated = (char *)gpr_malloc((size_t)ret + 1); va_start(args, format); vsnprintf(message, (size_t)(ret + 1), format, args); va_end(args); diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 74e55d5741..173e800f0d 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -95,9 +95,12 @@ static gpr_atm pack_received_status(received_status r) { static received_status unpack_received_status(gpr_atm atm) { return (atm & 1) == 0 - ? (received_status){.is_set = false, .error = GRPC_ERROR_NONE} - : (received_status){.is_set = true, - .error = (grpc_error *)(atm & ~(gpr_atm)1)}; + ? (received_status){false, /* is_set */ + GRPC_ERROR_NONE /*error */ + } + : (received_status){true, /* is_set */ + (grpc_error *)(atm & ~(gpr_atm)1) /* error */ + }; } #define MAX_ERRORS_PER_BATCH 4 -- cgit v1.2.3 From 662d32444e7cad07d2026ddcbe5c659a6fa1a3bd Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Fri, 22 Sep 2017 13:39:26 -0700 Subject: Removing more build errors --- src/core/lib/iomgr/tcp_client_uv.cc | 10 +++++----- src/core/lib/support/thd_windows.cc | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc index 786c456b73..b1a301e378 100644 --- a/src/core/lib/iomgr/tcp_client_uv.cc +++ b/src/core/lib/iomgr/tcp_client_uv.cc @@ -58,7 +58,7 @@ static void tcp_close_callback(uv_handle_t *handle) { gpr_free(handle); } static void uv_tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { int done; - grpc_uv_tcp_connect *connect = acp; + grpc_uv_tcp_connect *connect = (grpc_uv_tcp_connect *)acp; if (GRPC_TRACER_ON(grpc_tcp_trace)) { const char *str = grpc_error_string(error); gpr_log(GPR_DEBUG, "CLIENT_CONNECT: %s: on_alarm: error=%s", @@ -77,7 +77,7 @@ static void uv_tc_on_alarm(grpc_exec_ctx *exec_ctx, void *acp, } static void uv_tc_on_connect(uv_connect_t *req, int status) { - grpc_uv_tcp_connect *connect = req->data; + grpc_uv_tcp_connect *connect = (grpc_uv_tcp_connect *)req->data; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; grpc_error *error = GRPC_ERROR_NONE; int done; @@ -131,16 +131,16 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, for (size_t i = 0; i < channel_args->num_args; i++) { if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_RESOURCE_QUOTA)) { grpc_resource_quota_unref_internal(exec_ctx, resource_quota); - resource_quota = grpc_resource_quota_ref_internal( + resource_quota = grpc_resource_quota_ref_internal((grpc_resource_quota *) channel_args->args[i].value.pointer.p); } } } - connect = gpr_zalloc(sizeof(grpc_uv_tcp_connect)); + connect = (grpc_uv_tcp_connect*)gpr_zalloc(sizeof(grpc_uv_tcp_connect)); connect->closure = closure; connect->endpoint = ep; - connect->tcp_handle = gpr_malloc(sizeof(uv_tcp_t)); + connect->tcp_handle = (uv_tcp_t*)gpr_malloc(sizeof(uv_tcp_t)); connect->addr_name = grpc_sockaddr_to_uri(resolved_addr); connect->resource_quota = resource_quota; uv_tcp_init(uv_default_loop(), connect->tcp_handle); diff --git a/src/core/lib/support/thd_windows.cc b/src/core/lib/support/thd_windows.cc index 54533e9412..1a82805dd9 100644 --- a/src/core/lib/support/thd_windows.cc +++ b/src/core/lib/support/thd_windows.cc @@ -66,7 +66,7 @@ static DWORD WINAPI thread_body(void *v) { int gpr_thd_new(gpr_thd_id *t, void (*thd_body)(void *arg), void *arg, const gpr_thd_options *options) { HANDLE handle; - struct thd_info *info = gpr_malloc(sizeof(*info)); + struct thd_info *info = (struct thd_info *)gpr_malloc(sizeof(*info)); info->body = thd_body; info->arg = arg; *t = 0; -- cgit v1.2.3 From 922bc8ae845f06702a05eedf99dec9eecb615430 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Fri, 22 Sep 2017 16:32:41 -0700 Subject: Some windows build errors --- src/core/lib/support/log_windows.cc | 4 ++-- src/core/lib/support/string_windows.cc | 2 +- src/core/lib/support/subprocess_windows.cc | 2 +- src/core/lib/support/sync_windows.cc | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/lib/support/log_windows.cc b/src/core/lib/support/log_windows.cc index b71dacd80a..ee52abea73 100644 --- a/src/core/lib/support/log_windows.cc +++ b/src/core/lib/support/log_windows.cc @@ -47,7 +47,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, } else { /* Allocate a new buffer, with space for the NUL terminator. */ size_t strp_buflen = (size_t)ret + 1; - message = gpr_malloc(strp_buflen); + message = (char *)gpr_malloc(strp_buflen); /* Print to the buffer. */ va_start(args, format); @@ -66,7 +66,7 @@ void gpr_log(const char *file, int line, gpr_log_severity severity, /* Simple starter implementation */ extern "C" void gpr_default_log(gpr_log_func_args *args) { - char *final_slash; + const char *final_slash; const char *display_file; char time_buffer[64]; time_t timer; diff --git a/src/core/lib/support/string_windows.cc b/src/core/lib/support/string_windows.cc index bae524d7cb..d37863c066 100644 --- a/src/core/lib/support/string_windows.cc +++ b/src/core/lib/support/string_windows.cc @@ -47,7 +47,7 @@ int gpr_asprintf(char **strp, const char *format, ...) { /* Allocate a new buffer, with space for the NUL terminator. */ strp_buflen = (size_t)ret + 1; - if ((*strp = gpr_malloc(strp_buflen)) == NULL) { + if ((*strp = (char *)gpr_malloc(strp_buflen)) == NULL) { /* This shouldn't happen, because gpr_malloc() calls abort(). */ return -1; } diff --git a/src/core/lib/support/subprocess_windows.cc b/src/core/lib/support/subprocess_windows.cc index 7412f8d344..6769f1d3a4 100644 --- a/src/core/lib/support/subprocess_windows.cc +++ b/src/core/lib/support/subprocess_windows.cc @@ -61,7 +61,7 @@ gpr_subprocess *gpr_subprocess_create(int argc, const char **argv) { } gpr_free(args_tchar); - r = gpr_malloc(sizeof(gpr_subprocess)); + r = (gpr_subprocess *)gpr_malloc(sizeof(gpr_subprocess)); memset(r, 0, sizeof(*r)); r->pi = pi; return r; diff --git a/src/core/lib/support/sync_windows.cc b/src/core/lib/support/sync_windows.cc index 008c5aecba..62fdd40af7 100644 --- a/src/core/lib/support/sync_windows.cc +++ b/src/core/lib/support/sync_windows.cc @@ -104,7 +104,7 @@ struct run_once_func_arg { void (*init_function)(void); }; static BOOL CALLBACK run_once_func(gpr_once *once, void *v, void **pv) { - struct run_once_func_arg *arg = v; + struct run_once_func_arg *arg = (struct run_once_func_arg *)v; (*arg->init_function)(); return 1; } -- cgit v1.2.3 From d8b84a249edd8d6f3f42ca40ec17668e67f38dff Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 25 Sep 2017 13:38:03 -0700 Subject: Removing few more build errors --- src/core/ext/census/base_resources.cc | 14 +++---- .../ext/filters/client_channel/client_channel.cc | 15 ++++---- .../client_channel/lb_policy/grpclb/grpclb.cc | 6 +-- src/core/ext/filters/client_channel/subchannel.cc | 31 +++++++-------- .../ext/filters/client_channel/subchannel_index.cc | 11 +++--- src/core/ext/filters/max_age/max_age_filter.cc | 3 +- .../transport/chttp2/transport/chttp2_transport.cc | 19 ++++----- src/core/ext/transport/chttp2/transport/writing.cc | 18 ++++----- src/core/lib/iomgr/resolve_address_windows.cc | 11 +++--- src/core/lib/support/spinlock.h | 5 +++ src/core/lib/surface/call.cc | 29 +++++++------- src/core/lib/surface/completion_queue.cc | 45 +++++++--------------- src/core/lib/transport/metadata.h | 5 +++ src/core/lib/transport/metadata_batch.h | 7 ++-- src/core/lib/transport/transport.cc | 9 +++-- src/core/tsi/fake_transport_security.cc | 3 +- 16 files changed, 111 insertions(+), 120 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/ext/census/base_resources.cc b/src/core/ext/census/base_resources.cc index 1f2bb39fe0..3697c6f0e0 100644 --- a/src/core/ext/census/base_resources.cc +++ b/src/core/ext/census/base_resources.cc @@ -45,12 +45,12 @@ void define_base_resources() { 0, // n_denominators NULL}; // denominators define_resource(&r); - r = (resource){(char *)"server_rpc_latency", // name - (char *)"Server RPC latency in seconds", // description - 0, // prefix - 1, // n_numerators - &numerator, // numerators - 0, // n_denominators - NULL}; // denominators + r = {(char *)"server_rpc_latency", // name + (char *)"Server RPC latency in seconds", // description + 0, // prefix + 1, // n_numerators + &numerator, // numerators + 0, // n_denominators + NULL}; // denominators define_resource(&r); } diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index 016199b1f4..3aded6b48e 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -995,13 +995,14 @@ static void create_subchannel_call_locked(grpc_exec_ctx *exec_ctx, channel_data *chand = (channel_data *)elem->channel_data; call_data *calld = (call_data *)elem->call_data; const grpc_connected_subchannel_call_args call_args = { - .pollent = calld->pollent, - .path = calld->path, - .start_time = calld->call_start_time, - .deadline = calld->deadline, - .arena = calld->arena, - .context = calld->subchannel_call_context, - .call_combiner = calld->call_combiner}; + calld->pollent, // pollent + calld->path, // path + calld->call_start_time, // start_time + calld->deadline, // deadline + calld->arena, // arena + calld->subchannel_call_context, // context + calld->call_combiner // call_combiner + }; grpc_error *new_error = grpc_connected_subchannel_create_call( exec_ctx, calld->connected_subchannel, &call_args, &calld->subchannel_call); diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc index b9e72da6be..bbebc3b643 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -456,11 +456,11 @@ static bool is_server_valid(const grpc_grpclb_server *server, size_t idx, static void *lb_token_copy(void *token) { return token == NULL ? NULL - : (void *)GRPC_MDELEM_REF((grpc_mdelem){(uintptr_t)token}).payload; + : (void *)GRPC_MDELEM_REF(grpc_mdelem{(uintptr_t)token}).payload; } static void lb_token_destroy(grpc_exec_ctx *exec_ctx, void *token) { if (token != NULL) { - GRPC_MDELEM_UNREF(exec_ctx, (grpc_mdelem){(uintptr_t)token}); + GRPC_MDELEM_UNREF(exec_ctx, grpc_mdelem{(uintptr_t)token}); } } static int lb_token_cmp(void *token1, void *token2) { @@ -1925,7 +1925,7 @@ static grpc_lb_policy *glb_create(grpc_exec_ctx *exec_ctx, arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_CALL_TIMEOUT_MS); glb_policy->lb_call_timeout_ms = - grpc_channel_arg_get_integer(arg, (grpc_integer_options){0, 0, INT_MAX}); + grpc_channel_arg_get_integer(arg, {0, 0, INT_MAX}); arg = grpc_channel_args_find(args->args, GRPC_ARG_GRPCLB_FALLBACK_TIMEOUT_MS); glb_policy->lb_fallback_timeout_ms = grpc_channel_arg_get_integer( diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc index 40a51c72d6..190f223ff0 100644 --- a/src/core/ext/filters/client_channel/subchannel.cc +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -343,27 +343,23 @@ grpc_subchannel *grpc_subchannel_create(grpc_exec_ctx *exec_ctx, "grpc.testing.fixed_reconnect_backoff_ms")) { fixed_reconnect_backoff = true; initial_backoff_ms = min_backoff_ms = max_backoff_ms = - grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){initial_backoff_ms, 100, INT_MAX}); + grpc_channel_arg_get_integer(&c->args->args[i], + {initial_backoff_ms, 100, INT_MAX}); } else if (0 == strcmp(c->args->args[i].key, GRPC_ARG_MIN_RECONNECT_BACKOFF_MS)) { fixed_reconnect_backoff = false; min_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){min_backoff_ms, 100, INT_MAX}); + &c->args->args[i], {min_backoff_ms, 100, INT_MAX}); } else if (0 == strcmp(c->args->args[i].key, GRPC_ARG_MAX_RECONNECT_BACKOFF_MS)) { fixed_reconnect_backoff = false; max_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){max_backoff_ms, 100, INT_MAX}); + &c->args->args[i], {max_backoff_ms, 100, INT_MAX}); } else if (0 == strcmp(c->args->args[i].key, GRPC_ARG_INITIAL_RECONNECT_BACKOFF_MS)) { fixed_reconnect_backoff = false; initial_backoff_ms = grpc_channel_arg_get_integer( - &c->args->args[i], - (grpc_integer_options){initial_backoff_ms, 100, INT_MAX}); + &c->args->args[i], {initial_backoff_ms, 100, INT_MAX}); } } } @@ -759,14 +755,15 @@ grpc_error *grpc_connected_subchannel_create_call( grpc_call_stack *callstk = SUBCHANNEL_CALL_TO_CALL_STACK(*call); (*call)->connection = GRPC_CONNECTED_SUBCHANNEL_REF(con, "subchannel_call"); const grpc_call_element_args call_args = { - .call_stack = callstk, - .server_transport_data = NULL, - .context = args->context, - .path = args->path, - .start_time = args->start_time, - .deadline = args->deadline, - .arena = args->arena, - .call_combiner = args->call_combiner}; + callstk, /* call_stack */ + NULL, /* server_transport_data */ + args->context, /* context */ + args->path, /* path */ + args->start_time, /* start_time */ + args->deadline, /* deadline */ + args->arena, /* arena */ + args->call_combiner /* call_combiner */ + }; grpc_error *error = grpc_call_stack_init( exec_ctx, chanstk, 1, subchannel_call_destroy, *call, &call_args); if (error != GRPC_ERROR_NONE) { diff --git a/src/core/ext/filters/client_channel/subchannel_index.cc b/src/core/ext/filters/client_channel/subchannel_index.cc index d7a51f3899..1f466ec0b8 100644 --- a/src/core/ext/filters/client_channel/subchannel_index.cc +++ b/src/core/ext/filters/client_channel/subchannel_index.cc @@ -114,11 +114,12 @@ static void *scv_avl_copy(void *p, void *unused) { } static const gpr_avl_vtable subchannel_avl_vtable = { - .destroy_key = sck_avl_destroy, - .copy_key = sck_avl_copy, - .compare_keys = sck_avl_compare, - .destroy_value = scv_avl_destroy, - .copy_value = scv_avl_copy}; + sck_avl_destroy, // destroy_key + sck_avl_copy, // copy_key + sck_avl_compare, // compare_keys + scv_avl_destroy, // destroy_value + scv_avl_copy // copy_value +}; void grpc_subchannel_index_init(void) { g_subchannel_index = gpr_avl_create(&subchannel_avl_vtable); diff --git a/src/core/ext/filters/max_age/max_age_filter.cc b/src/core/ext/filters/max_age/max_age_filter.cc index 88a70ba3e5..f4d5b1427e 100644 --- a/src/core/ext/filters/max_age/max_age_filter.cc +++ b/src/core/ext/filters/max_age/max_age_filter.cc @@ -315,8 +315,7 @@ static grpc_error* init_channel_elem(grpc_exec_ctx* exec_ctx, GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS)) { const int value = grpc_channel_arg_get_integer( &args->channel_args->args[i], - (grpc_integer_options){DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, - INT_MAX}); + {DEFAULT_MAX_CONNECTION_AGE_GRACE_MS, 0, INT_MAX}); chand->max_connection_age_grace = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_millis(value, GPR_TIMESPAN); diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index acf49632ff..9fc1b29138 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -315,15 +315,16 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, grpc_bdp_estimator_init(&t->flow_control.bdp_estimator, t->peer_string); t->flow_control.last_pid_update = gpr_now(GPR_CLOCK_MONOTONIC); - grpc_pid_controller_init( - &t->flow_control.pid_controller, - (grpc_pid_controller_args){.gain_p = 4, - .gain_i = 8, - .gain_d = 0, - .initial_control_value = log2(DEFAULT_WINDOW), - .min_control_value = -1, - .max_control_value = 25, - .integral_range = 10}); + grpc_pid_controller_init(&t->flow_control.pid_controller, + { + 4, /* gain_p */ + 8, /* gain_t */ + 0, /* gain_d */ + log2(DEFAULT_WINDOW), /* initial_control_value */ + -1, /* min_control_value */ + 25, /* max_control_value */ + 10 /* integral_range */ + }); grpc_chttp2_goaway_parser_init(&t->goaway_parser); grpc_chttp2_hpack_parser_init(exec_ctx, &t->hpack_parser); diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc index be1af16019..399b1608bd 100644 --- a/src/core/ext/transport/chttp2/transport/writing.cc +++ b/src/core/ext/transport/chttp2/transport/writing.cc @@ -257,15 +257,15 @@ grpc_chttp2_begin_write_result grpc_chttp2_begin_write( s->send_trailing_metadata == NULL || !is_default_initial_metadata(s->send_initial_metadata)) { grpc_encode_header_options hopt = { - .stream_id = s->id, - .is_eof = false, - .use_true_binary_metadata = - t->settings - [GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != 0, - .max_frame_size = t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], - .stats = &s->stats.outgoing}; + s->id, // stream_id + false, // is_eof + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != + 0, // use_true_binary_metadata + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], // max_frame_size + &s->stats.outgoing // stats + }; grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, NULL, 0, s->send_initial_metadata, &hopt, &t->outbuf); now_writing = true; diff --git a/src/core/lib/iomgr/resolve_address_windows.cc b/src/core/lib/iomgr/resolve_address_windows.cc index 0cb0029f4e..69e66de78d 100644 --- a/src/core/lib/iomgr/resolve_address_windows.cc +++ b/src/core/lib/iomgr/resolve_address_windows.cc @@ -93,13 +93,14 @@ static grpc_error *blocking_resolve_address_impl( } /* Success path: set addrs non-NULL, fill it in */ - (*addresses) = gpr_malloc(sizeof(grpc_resolved_addresses)); + (*addresses) = + (grpc_resolved_addresses *)gpr_malloc(sizeof(grpc_resolved_addresses)); (*addresses)->naddrs = 0; for (resp = result; resp != NULL; resp = resp->ai_next) { (*addresses)->naddrs++; } - (*addresses)->addrs = - gpr_malloc(sizeof(grpc_resolved_address) * (*addresses)->naddrs); + (*addresses)->addrs = (grpc_resolved_address *)gpr_malloc( + sizeof(grpc_resolved_address) * (*addresses)->naddrs); i = 0; for (resp = result; resp != NULL; resp = resp->ai_next) { memcpy(&(*addresses)->addrs[i].addr, resp->ai_addr, resp->ai_addrlen); @@ -132,7 +133,7 @@ grpc_error *(*grpc_blocking_resolve_address)( * grpc_blocking_resolve_address */ static void do_request_thread(grpc_exec_ctx *exec_ctx, void *rp, grpc_error *error) { - request *r = rp; + request *r = (request *)rp; if (error == GRPC_ERROR_NONE) { error = grpc_blocking_resolve_address(r->name, r->default_port, r->addresses); @@ -157,7 +158,7 @@ static void resolve_address_impl(grpc_exec_ctx *exec_ctx, const char *name, grpc_pollset_set *interested_parties, grpc_closure *on_done, grpc_resolved_addresses **addresses) { - request *r = gpr_malloc(sizeof(request)); + request *r = (request *)gpr_malloc(sizeof(request)); GRPC_CLOSURE_INIT(&r->request_closure, do_request_thread, r, grpc_executor_scheduler(GRPC_EXECUTOR_SHORT)); r->name = gpr_strdup(name); diff --git a/src/core/lib/support/spinlock.h b/src/core/lib/support/spinlock.h index 37adda11b0..47584f6279 100644 --- a/src/core/lib/support/spinlock.h +++ b/src/core/lib/support/spinlock.h @@ -25,9 +25,14 @@ a concurrency code smell. */ typedef struct { gpr_atm atm; } gpr_spinlock; +#ifdef __cplusplus +#define GPR_SPINLOCK_INITIALIZER (gpr_spinlock{0}) +#else #define GPR_SPINLOCK_INITIALIZER ((gpr_spinlock){0}) +#endif #define GPR_SPINLOCK_STATIC_INITIALIZER \ { 0 } + #define gpr_spinlock_trylock(lock) (gpr_atm_acq_cas(&(lock)->atm, 0, 1)) #define gpr_spinlock_unlock(lock) (gpr_atm_rel_store(&(lock)->atm, 0)) #define gpr_spinlock_lock(lock) \ diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 173e800f0d..4055d656bd 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -94,13 +94,11 @@ static gpr_atm pack_received_status(received_status r) { } static received_status unpack_received_status(gpr_atm atm) { - return (atm & 1) == 0 - ? (received_status){false, /* is_set */ - GRPC_ERROR_NONE /*error */ - } - : (received_status){true, /* is_set */ - (grpc_error *)(atm & ~(gpr_atm)1) /* error */ - }; + if ((atm & 1) == 0) { + return {false, GRPC_ERROR_NONE}; + } else { + return {true, (grpc_error *)(atm & ~(gpr_atm)1)}; + } } #define MAX_ERRORS_PER_BATCH 4 @@ -443,15 +441,14 @@ grpc_error *grpc_call_create(grpc_exec_ctx *exec_ctx, GRPC_CHANNEL_INTERNAL_REF(args->channel, "call"); /* initial refcount dropped by grpc_call_unref */ - grpc_call_element_args call_args = { - .call_stack = CALL_STACK_FROM_CALL(call), - .server_transport_data = args->server_transport_data, - .context = call->context, - .path = path, - .start_time = call->start_time, - .deadline = send_deadline, - .arena = call->arena, - .call_combiner = &call->call_combiner}; + grpc_call_element_args call_args = {CALL_STACK_FROM_CALL(call), + args->server_transport_data, + call->context, + path, + call->start_time, + send_deadline, + call->arena, + &call->call_combiner}; add_init_error(&error, grpc_call_stack_init(exec_ctx, channel_stack, 1, destroy_call, call, &call_args)); if (error != GRPC_ERROR_NONE) { diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc index fed66e3a20..d332e841a3 100644 --- a/src/core/lib/surface/completion_queue.cc +++ b/src/core/lib/surface/completion_queue.cc @@ -164,32 +164,15 @@ static void non_polling_poller_shutdown(grpc_exec_ctx *exec_ctx, static const cq_poller_vtable g_poller_vtable_by_poller_type[] = { /* GRPC_CQ_DEFAULT_POLLING */ - {.can_get_pollset = true, - .can_listen = true, - .size = grpc_pollset_size, - .init = grpc_pollset_init, - .kick = grpc_pollset_kick, - .work = grpc_pollset_work, - .shutdown = grpc_pollset_shutdown, - .destroy = grpc_pollset_destroy}, + {true, true, grpc_pollset_size, grpc_pollset_init, grpc_pollset_kick, + grpc_pollset_work, grpc_pollset_shutdown, grpc_pollset_destroy}, /* GRPC_CQ_NON_LISTENING */ - {.can_get_pollset = true, - .can_listen = false, - .size = grpc_pollset_size, - .init = grpc_pollset_init, - .kick = grpc_pollset_kick, - .work = grpc_pollset_work, - .shutdown = grpc_pollset_shutdown, - .destroy = grpc_pollset_destroy}, + {true, false, grpc_pollset_size, grpc_pollset_init, grpc_pollset_kick, + grpc_pollset_work, grpc_pollset_shutdown, grpc_pollset_destroy}, /* GRPC_CQ_NON_POLLING */ - {.can_get_pollset = false, - .can_listen = false, - .size = non_polling_poller_size, - .init = non_polling_poller_init, - .kick = non_polling_poller_kick, - .work = non_polling_poller_work, - .shutdown = non_polling_poller_shutdown, - .destroy = non_polling_poller_destroy}, + {false, false, non_polling_poller_size, non_polling_poller_init, + non_polling_poller_kick, non_polling_poller_work, + non_polling_poller_shutdown, non_polling_poller_destroy}, }; typedef struct cq_vtable { @@ -838,13 +821,13 @@ static grpc_event cq_next(grpc_completion_queue *cq, gpr_timespec deadline, GRPC_CQ_INTERNAL_REF(cq, "next"); cq_is_finished_arg is_finished_arg = { - .last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever), - .cq = cq, - .deadline = deadline, - .stolen_completion = NULL, - .tag = NULL, - .first_loop = true}; + + gpr_atm_no_barrier_load(&cqd->things_queued_ever), + cq, + deadline, + NULL, + NULL, + true}; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER(0, cq_is_next_finished, &is_finished_arg); diff --git a/src/core/lib/transport/metadata.h b/src/core/lib/transport/metadata.h index 974469e436..9f82225dc3 100644 --- a/src/core/lib/transport/metadata.h +++ b/src/core/lib/transport/metadata.h @@ -102,8 +102,13 @@ struct grpc_mdelem { ((grpc_mdelem_data *)((md).payload & ~(uintptr_t)3)) #define GRPC_MDELEM_STORAGE(md) \ ((grpc_mdelem_data_storage)((md).payload & (uintptr_t)3)) +#ifdef __cplusplus +#define GRPC_MAKE_MDELEM(data, storage) \ + (grpc_mdelem{((uintptr_t)(data)) | ((uintptr_t)storage)}) +#else #define GRPC_MAKE_MDELEM(data, storage) \ ((grpc_mdelem){((uintptr_t)(data)) | ((uintptr_t)storage)}) +#endif #define GRPC_MDELEM_IS_INTERNED(md) \ ((grpc_mdelem_data_storage)((md).payload & \ (uintptr_t)GRPC_MDELEM_STORAGE_INTERNED_BIT)) diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h index 57d298c75c..63f30a78d1 100644 --- a/src/core/lib/transport/metadata_batch.h +++ b/src/core/lib/transport/metadata_batch.h @@ -125,10 +125,11 @@ typedef struct { } grpc_filtered_mdelem; #define GRPC_FILTERED_ERROR(error) \ - ((grpc_filtered_mdelem){(error), GRPC_MDNULL}) -#define GRPC_FILTERED_MDELEM(md) ((grpc_filtered_mdelem){GRPC_ERROR_NONE, (md)}) + { (error), GRPC_MDNULL } +#define GRPC_FILTERED_MDELEM(md) \ + { GRPC_ERROR_NONE, (md) } #define GRPC_FILTERED_REMOVE() \ - ((grpc_filtered_mdelem){GRPC_ERROR_NONE, GRPC_MDNULL}) + { GRPC_ERROR_NONE, GRPC_MDNULL } typedef grpc_filtered_mdelem (*grpc_metadata_batch_filter_func)( grpc_exec_ctx *exec_ctx, void *user_data, grpc_mdelem elem); diff --git a/src/core/lib/transport/transport.cc b/src/core/lib/transport/transport.cc index 682a820b48..ab4f938e7b 100644 --- a/src/core/lib/transport/transport.cc +++ b/src/core/lib/transport/transport.cc @@ -110,10 +110,11 @@ grpc_slice grpc_slice_from_stream_owned_buffer(grpc_stream_refcount *refcount, } static const grpc_slice_refcount_vtable stream_ref_slice_vtable = { - .ref = slice_stream_ref, - .unref = slice_stream_unref, - .eq = grpc_slice_default_eq_impl, - .hash = grpc_slice_default_hash_impl}; + slice_stream_ref, /* ref */ + slice_stream_unref, /* unref */ + grpc_slice_default_eq_impl, /* eq */ + grpc_slice_default_hash_impl /* hash */ +}; #ifndef NDEBUG void grpc_stream_ref_init(grpc_stream_refcount *refcount, int initial_refs, diff --git a/src/core/tsi/fake_transport_security.cc b/src/core/tsi/fake_transport_security.cc index 0a992b5fd2..349dcf5cb8 100644 --- a/src/core/tsi/fake_transport_security.cc +++ b/src/core/tsi/fake_transport_security.cc @@ -98,8 +98,7 @@ static const char *tsi_fake_handshake_message_to_string(int msg) { static tsi_result tsi_fake_handshake_message_from_string( const char *msg_string, tsi_fake_handshake_message *msg) { - int i; - for (i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) { + for (int i = 0; i < TSI_FAKE_HANDSHAKE_MESSAGE_MAX; i++) { if (strncmp(msg_string, tsi_fake_handshake_message_strings[i], strlen(tsi_fake_handshake_message_strings[i])) == 0) { *msg = (tsi_fake_handshake_message)i; -- cgit v1.2.3 From fcd26bcd0f9091389915858ae0ac404b3c8748b9 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 25 Sep 2017 15:08:28 -0700 Subject: Removing more build errors --- src/core/ext/filters/client_channel/channel_connectivity.cc | 2 ++ src/core/ext/filters/client_channel/client_channel.cc | 1 + src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc | 1 + src/core/ext/filters/client_channel/subchannel.cc | 1 + src/core/ext/transport/chttp2/server/chttp2_server.cc | 1 + src/core/ext/transport/chttp2/transport/flow_control.cc | 1 + src/core/lib/iomgr/call_combiner.cc | 2 ++ src/core/lib/iomgr/combiner.cc | 1 + src/core/lib/iomgr/error.h | 2 +- src/core/lib/iomgr/resolve_address_windows.cc | 1 + src/core/lib/iomgr/resource_quota.cc | 1 + src/core/lib/iomgr/sockaddr_utils.cc | 1 + src/core/lib/iomgr/tcp_client_windows.cc | 2 ++ src/core/lib/iomgr/tcp_server_windows.cc | 1 + src/core/lib/iomgr/timer_generic.cc | 2 ++ src/core/lib/iomgr/timer_manager.cc | 2 ++ src/core/lib/iomgr/timer_uv.cc | 2 +- src/core/lib/security/credentials/jwt/jwt_credentials.cc | 1 + src/core/lib/slice/slice_intern.cc | 1 + src/core/lib/support/string_util_windows.cc | 1 + src/core/lib/surface/call_log_batch.cc | 2 ++ src/core/lib/surface/channel.cc | 1 + src/core/lib/surface/completion_queue.cc | 1 + src/core/lib/transport/bdp_estimator.cc | 1 + src/core/lib/transport/metadata.cc | 1 + 25 files changed, 31 insertions(+), 2 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/ext/filters/client_channel/channel_connectivity.cc b/src/core/ext/filters/client_channel/channel_connectivity.cc index 3844b98021..a05a11dad1 100644 --- a/src/core/ext/filters/client_channel/channel_connectivity.cc +++ b/src/core/ext/filters/client_channel/channel_connectivity.cc @@ -18,6 +18,8 @@ #include "src/core/lib/surface/channel.h" +#include + #include #include diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index 3aded6b48e..47ec5fe24a 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -18,6 +18,7 @@ #include "src/core/ext/filters/client_channel/client_channel.h" +#include #include #include #include diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc index bbebc3b643..621e2637b1 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc @@ -80,6 +80,7 @@ headers. Therefore, sockaddr.h must always be included first */ #include "src/core/lib/iomgr/sockaddr.h" +#include #include #include diff --git a/src/core/ext/filters/client_channel/subchannel.cc b/src/core/ext/filters/client_channel/subchannel.cc index 190f223ff0..bff5001d69 100644 --- a/src/core/ext/filters/client_channel/subchannel.cc +++ b/src/core/ext/filters/client_channel/subchannel.cc @@ -18,6 +18,7 @@ #include "src/core/ext/filters/client_channel/subchannel.h" +#include #include #include diff --git a/src/core/ext/transport/chttp2/server/chttp2_server.cc b/src/core/ext/transport/chttp2/server/chttp2_server.cc index 60244e163b..a51959bec7 100644 --- a/src/core/ext/transport/chttp2/server/chttp2_server.cc +++ b/src/core/ext/transport/chttp2/server/chttp2_server.cc @@ -20,6 +20,7 @@ #include +#include #include #include diff --git a/src/core/ext/transport/chttp2/transport/flow_control.cc b/src/core/ext/transport/chttp2/transport/flow_control.cc index 037813340d..75eae1f962 100644 --- a/src/core/ext/transport/chttp2/transport/flow_control.cc +++ b/src/core/ext/transport/chttp2/transport/flow_control.cc @@ -18,6 +18,7 @@ #include "src/core/ext/transport/chttp2/transport/internal.h" +#include #include #include #include diff --git a/src/core/lib/iomgr/call_combiner.cc b/src/core/lib/iomgr/call_combiner.cc index 48d8eaec18..bab3df021a 100644 --- a/src/core/lib/iomgr/call_combiner.cc +++ b/src/core/lib/iomgr/call_combiner.cc @@ -18,6 +18,8 @@ #include "src/core/lib/iomgr/call_combiner.h" +#include + #include grpc_tracer_flag grpc_call_combiner_trace = diff --git a/src/core/lib/iomgr/combiner.cc b/src/core/lib/iomgr/combiner.cc index f899b25f10..0e707ef839 100644 --- a/src/core/lib/iomgr/combiner.cc +++ b/src/core/lib/iomgr/combiner.cc @@ -19,6 +19,7 @@ #include "src/core/lib/iomgr/combiner.h" #include +#include #include #include diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h index b362948691..b36330a7ab 100644 --- a/src/core/lib/iomgr/error.h +++ b/src/core/lib/iomgr/error.h @@ -19,8 +19,8 @@ #ifndef GRPC_CORE_LIB_IOMGR_ERROR_H #define GRPC_CORE_LIB_IOMGR_ERROR_H +#include #include -#include #include #include diff --git a/src/core/lib/iomgr/resolve_address_windows.cc b/src/core/lib/iomgr/resolve_address_windows.cc index 69e66de78d..abcfc2114d 100644 --- a/src/core/lib/iomgr/resolve_address_windows.cc +++ b/src/core/lib/iomgr/resolve_address_windows.cc @@ -23,6 +23,7 @@ #include "src/core/lib/iomgr/resolve_address.h" +#include #include #include diff --git a/src/core/lib/iomgr/resource_quota.cc b/src/core/lib/iomgr/resource_quota.cc index 4d69986fbc..60262435b3 100644 --- a/src/core/lib/iomgr/resource_quota.cc +++ b/src/core/lib/iomgr/resource_quota.cc @@ -18,6 +18,7 @@ #include "src/core/lib/iomgr/resource_quota.h" +#include #include #include #include diff --git a/src/core/lib/iomgr/sockaddr_utils.cc b/src/core/lib/iomgr/sockaddr_utils.cc index 3f4145d104..8a2e6ed89b 100644 --- a/src/core/lib/iomgr/sockaddr_utils.cc +++ b/src/core/lib/iomgr/sockaddr_utils.cc @@ -19,6 +19,7 @@ #include "src/core/lib/iomgr/sockaddr_utils.h" #include +#include #include #include diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc index fc62105cc9..10f5594d56 100644 --- a/src/core/lib/iomgr/tcp_client_windows.cc +++ b/src/core/lib/iomgr/tcp_client_windows.cc @@ -18,6 +18,8 @@ #include "src/core/lib/iomgr/port.h" +#include + #ifdef GRPC_WINSOCK_SOCKET #include "src/core/lib/iomgr/sockaddr_windows.h" diff --git a/src/core/lib/iomgr/tcp_server_windows.cc b/src/core/lib/iomgr/tcp_server_windows.cc index 0162afc1ad..2f23c04306 100644 --- a/src/core/lib/iomgr/tcp_server_windows.cc +++ b/src/core/lib/iomgr/tcp_server_windows.cc @@ -22,6 +22,7 @@ #include "src/core/lib/iomgr/sockaddr.h" +#include #include #include diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc index 2472cf26be..91e227d241 100644 --- a/src/core/lib/iomgr/timer_generic.cc +++ b/src/core/lib/iomgr/timer_generic.cc @@ -18,6 +18,8 @@ #include "src/core/lib/iomgr/port.h" +#include + #ifdef GRPC_TIMER_USE_GENERIC #include "src/core/lib/iomgr/timer.h" diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc index 04ca44563d..9f67d8ef22 100644 --- a/src/core/lib/iomgr/timer_manager.cc +++ b/src/core/lib/iomgr/timer_manager.cc @@ -18,6 +18,8 @@ #include "src/core/lib/iomgr/timer_manager.h" +#include + #include #include #include diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc index adced41f53..d53fbf423c 100644 --- a/src/core/lib/iomgr/timer_uv.cc +++ b/src/core/lib/iomgr/timer_uv.cc @@ -66,7 +66,7 @@ void grpc_timer_init(grpc_exec_ctx *exec_ctx, grpc_timer *timer, } timer->pending = 1; timeout = (uint64_t)gpr_time_to_millis(gpr_time_sub(deadline, now)); - uv_timer = gpr_malloc(sizeof(uv_timer_t)); + uv_timer = (uv_timer_t *)gpr_malloc(sizeof(uv_timer_t)); uv_timer_init(uv_default_loop(), uv_timer); uv_timer->data = timer; timer->uv_timer = uv_timer; diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc index b361265a7b..5e4de4166d 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -18,6 +18,7 @@ #include "src/core/lib/security/credentials/jwt/jwt_credentials.h" +#include #include #include "src/core/lib/surface/api_trace.h" diff --git a/src/core/lib/slice/slice_intern.cc b/src/core/lib/slice/slice_intern.cc index ec71b3ca1d..1ea9a2aa67 100644 --- a/src/core/lib/slice/slice_intern.cc +++ b/src/core/lib/slice/slice_intern.cc @@ -18,6 +18,7 @@ #include "src/core/lib/slice/slice_internal.h" +#include #include #include diff --git a/src/core/lib/support/string_util_windows.cc b/src/core/lib/support/string_util_windows.cc index 2a03404448..d96f57a4b0 100644 --- a/src/core/lib/support/string_util_windows.cc +++ b/src/core/lib/support/string_util_windows.cc @@ -26,6 +26,7 @@ anything else, especially strsafe.h. */ #include +#include #include #include #include diff --git a/src/core/lib/surface/call_log_batch.cc b/src/core/lib/surface/call_log_batch.cc index 4a1c265817..5557927b7c 100644 --- a/src/core/lib/surface/call_log_batch.cc +++ b/src/core/lib/surface/call_log_batch.cc @@ -18,6 +18,8 @@ #include "src/core/lib/surface/call.h" +#include + #include #include #include "src/core/lib/slice/slice_string_helpers.h" diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index 48962e5e45..65333cf1af 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -18,6 +18,7 @@ #include "src/core/lib/surface/channel.h" +#include #include #include diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc index d332e841a3..91ad2cacd4 100644 --- a/src/core/lib/surface/completion_queue.cc +++ b/src/core/lib/surface/completion_queue.cc @@ -17,6 +17,7 @@ */ #include "src/core/lib/surface/completion_queue.h" +#include #include #include diff --git a/src/core/lib/transport/bdp_estimator.cc b/src/core/lib/transport/bdp_estimator.cc index 8b57693413..e7fa0eefe8 100644 --- a/src/core/lib/transport/bdp_estimator.cc +++ b/src/core/lib/transport/bdp_estimator.cc @@ -18,6 +18,7 @@ #include "src/core/lib/transport/bdp_estimator.h" +#include #include #include diff --git a/src/core/lib/transport/metadata.cc b/src/core/lib/transport/metadata.cc index 188b485625..5455b2481b 100644 --- a/src/core/lib/transport/metadata.cc +++ b/src/core/lib/transport/metadata.cc @@ -19,6 +19,7 @@ #include "src/core/lib/transport/metadata.h" #include +#include #include #include -- cgit v1.2.3 From 37fdb739607895335c748e42cd25170545a00d6c Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 25 Sep 2017 16:45:02 -0700 Subject: More build errors --- .../ext/filters/client_channel/client_channel.cc | 2 + .../resolver/dns/native/dns_resolver.cc | 3 + .../transport/chttp2/transport/chttp2_transport.cc | 65 +++++++++------------- src/core/ext/transport/chttp2/transport/writing.cc | 15 +++-- src/core/lib/compression/stream_compression_gzip.c | 7 +-- .../lib/compression/stream_compression_identity.c | 9 ++- src/core/lib/iomgr/error.cc | 2 + src/core/lib/iomgr/executor.cc | 4 +- src/core/lib/iomgr/iomgr.cc | 3 + src/core/lib/iomgr/tcp_client_windows.cc | 6 +- src/core/lib/iomgr/tcp_server_windows.cc | 17 +++--- src/core/lib/iomgr/timer_manager.cc | 5 +- .../security/credentials/jwt/jwt_credentials.cc | 2 + src/core/lib/support/string_util_windows.cc | 4 +- src/core/lib/surface/call.cc | 6 +- src/core/lib/surface/channel.cc | 20 +++---- src/core/lib/surface/completion_queue.cc | 15 ++--- src/core/lib/transport/transport_op_string.cc | 3 + 18 files changed, 93 insertions(+), 95 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index 47ec5fe24a..8bff7548ac 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -16,6 +16,8 @@ * */ +#include + #include "src/core/ext/filters/client_channel/client_channel.h" #include diff --git a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc index 1baf80b720..1c093d0ad2 100644 --- a/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc @@ -16,6 +16,9 @@ * */ +#include + +#include #include #include diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index 9fc1b29138..d6a9aab7d6 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -18,6 +18,9 @@ #include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" +#include + +#include #include #include #include @@ -434,13 +437,11 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { t->ping_policy.max_pings_without_data = grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){g_default_max_pings_without_data, 0, - INT_MAX}); + {g_default_max_pings_without_data, 0, INT_MAX}); } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { t->ping_policy.max_ping_strikes = grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){g_default_max_ping_strikes, 0, INT_MAX}); + &channel_args->args[i], {g_default_max_ping_strikes, 0, INT_MAX}); } else if (0 == strcmp( channel_args->args[i].key, @@ -449,9 +450,8 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, gpr_time_from_millis( grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){ - g_default_min_sent_ping_interval_without_data_ms, 0, - INT_MAX}), + {g_default_min_sent_ping_interval_without_data_ms, 0, + INT_MAX}), GPR_TIMESPAN); } else if (0 == strcmp( @@ -461,27 +461,24 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, gpr_time_from_millis( grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){ - g_default_min_recv_ping_interval_without_data_ms, 0, - INT_MAX}), + {g_default_min_recv_ping_interval_without_data_ms, 0, + INT_MAX}), GPR_TIMESPAN); } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE)) { t->write_buffer_size = (uint32_t)grpc_channel_arg_get_integer( - &channel_args->args[i], - (grpc_integer_options){0, 0, MAX_WRITE_BUFFER_SIZE}); + &channel_args->args[i], {0, 0, MAX_WRITE_BUFFER_SIZE}); } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_HTTP2_BDP_PROBE)) { - t->flow_control.enable_bdp_probe = grpc_channel_arg_get_integer( - &channel_args->args[i], (grpc_integer_options){1, 0, 1}); + t->flow_control.enable_bdp_probe = + grpc_channel_arg_get_integer(&channel_args->args[i], {1, 0, 1}); } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { const int value = grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){t->is_client - ? g_default_client_keepalive_time_ms - : g_default_server_keepalive_time_ms, - 1, INT_MAX}); + {t->is_client ? g_default_client_keepalive_time_ms + : g_default_server_keepalive_time_ms, + 1, INT_MAX}); t->keepalive_time = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_millis(value, GPR_TIMESPAN); @@ -489,18 +486,17 @@ static void init_transport(grpc_exec_ctx *exec_ctx, grpc_chttp2_transport *t, GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { const int value = grpc_channel_arg_get_integer( &channel_args->args[i], - (grpc_integer_options){t->is_client - ? g_default_client_keepalive_timeout_ms - : g_default_server_keepalive_timeout_ms, - 0, INT_MAX}); + {t->is_client ? g_default_client_keepalive_timeout_ms + : g_default_server_keepalive_timeout_ms, + 0, INT_MAX}); t->keepalive_timeout = value == INT_MAX ? gpr_inf_future(GPR_TIMESPAN) : gpr_time_from_millis(value, GPR_TIMESPAN); } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS)) { t->keepalive_permit_without_calls = - (uint32_t)grpc_channel_arg_get_integer( - &channel_args->args[i], (grpc_integer_options){0, 0, 1}); + (uint32_t)grpc_channel_arg_get_integer(&channel_args->args[i], + {0, 0, 1}); } else if (0 == strcmp(channel_args->args[i].key, GRPC_ARG_OPTIMIZATION_TARGET)) { if (channel_args->args[i].type != GRPC_ARG_STRING) { @@ -2627,9 +2623,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, for (i = 0; i < args->num_args; i++) { if (0 == strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIME_MS)) { const int value = grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){g_default_client_keepalive_time_ms, 1, - INT_MAX}); + &args->args[i], {g_default_client_keepalive_time_ms, 1, INT_MAX}); if (is_client) { g_default_client_keepalive_time_ms = value; } else { @@ -2639,8 +2633,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, strcmp(args->args[i].key, GRPC_ARG_KEEPALIVE_TIMEOUT_MS)) { const int value = grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){g_default_client_keepalive_timeout_ms, 0, - INT_MAX}); + {g_default_client_keepalive_timeout_ms, 0, INT_MAX}); if (is_client) { g_default_client_keepalive_timeout_ms = value; } else { @@ -2651,8 +2644,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, g_default_keepalive_permit_without_calls = (uint32_t)grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){g_default_keepalive_permit_without_calls, - 0, 1}); + {g_default_keepalive_permit_without_calls, 0, 1}); } else if (0 == strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { g_default_max_ping_strikes = grpc_channel_arg_get_integer( @@ -2661,8 +2653,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, } else if (0 == strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { g_default_max_pings_without_data = grpc_channel_arg_get_integer( - &args->args[i], (grpc_integer_options){ - g_default_max_pings_without_data, 0, INT_MAX}); + &args->args[i], {g_default_max_pings_without_data, 0, INT_MAX}); } else if (0 == strcmp( args->args[i].key, @@ -2670,9 +2661,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, g_default_min_sent_ping_interval_without_data_ms = grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){ - g_default_min_sent_ping_interval_without_data_ms, 0, - INT_MAX}); + {g_default_min_sent_ping_interval_without_data_ms, 0, INT_MAX}); } else if (0 == strcmp( args->args[i].key, @@ -2680,9 +2669,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, g_default_min_recv_ping_interval_without_data_ms = grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){ - g_default_min_recv_ping_interval_without_data_ms, 0, - INT_MAX}); + {g_default_min_recv_ping_interval_without_data_ms, 0, INT_MAX}); } } } diff --git a/src/core/ext/transport/chttp2/transport/writing.cc b/src/core/ext/transport/chttp2/transport/writing.cc index 399b1608bd..ba3d55abb3 100644 --- a/src/core/ext/transport/chttp2/transport/writing.cc +++ b/src/core/ext/transport/chttp2/transport/writing.cc @@ -431,17 +431,16 @@ grpc_chttp2_begin_write_result grpc_chttp2_begin_write( &s->stats.outgoing, &t->outbuf); } else { grpc_encode_header_options hopt = { - .stream_id = s->id, - .is_eof = true, - .use_true_binary_metadata = - t->settings + s->id, true, + + t->settings [GRPC_PEER_SETTINGS] [GRPC_CHTTP2_SETTINGS_GRPC_ALLOW_TRUE_BINARY_METADATA] != 0, - .max_frame_size = - t->settings[GRPC_PEER_SETTINGS] - [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], - .stats = &s->stats.outgoing}; + + t->settings[GRPC_PEER_SETTINGS] + [GRPC_CHTTP2_SETTINGS_MAX_FRAME_SIZE], + &s->stats.outgoing}; grpc_chttp2_encode_header(exec_ctx, &t->hpack_compressor, extra_headers_for_trailing_metadata, num_extra_headers_for_trailing_metadata, diff --git a/src/core/lib/compression/stream_compression_gzip.c b/src/core/lib/compression/stream_compression_gzip.c index abcbdb3a91..087b018be5 100644 --- a/src/core/lib/compression/stream_compression_gzip.c +++ b/src/core/lib/compression/stream_compression_gzip.c @@ -222,7 +222,6 @@ static void grpc_stream_compression_context_destroy_gzip( } const grpc_stream_compression_vtable grpc_stream_compression_gzip_vtable = { - .compress = grpc_stream_compress_gzip, - .decompress = grpc_stream_decompress_gzip, - .context_create = grpc_stream_compression_context_create_gzip, - .context_destroy = grpc_stream_compression_context_destroy_gzip}; + grpc_stream_compress_gzip, grpc_stream_decompress_gzip, + grpc_stream_compression_context_create_gzip, + grpc_stream_compression_context_destroy_gzip}; diff --git a/src/core/lib/compression/stream_compression_identity.c b/src/core/lib/compression/stream_compression_identity.c index 3dfcf53b85..9b2e6062e1 100644 --- a/src/core/lib/compression/stream_compression_identity.c +++ b/src/core/lib/compression/stream_compression_identity.c @@ -27,7 +27,7 @@ /* Singleton context used for all identity streams. */ static grpc_stream_compression_context identity_ctx = { - .vtable = &grpc_stream_compression_identity_vtable}; + &grpc_stream_compression_identity_vtable}; static void grpc_stream_compression_pass_through(grpc_slice_buffer *in, grpc_slice_buffer *out, @@ -88,7 +88,6 @@ static void grpc_stream_compression_context_destroy_identity( } const grpc_stream_compression_vtable grpc_stream_compression_identity_vtable = { - .compress = grpc_stream_compress_identity, - .decompress = grpc_stream_decompress_identity, - .context_create = grpc_stream_compression_context_create_identity, - .context_destroy = grpc_stream_compression_context_destroy_identity}; + grpc_stream_compress_identity, grpc_stream_decompress_identity, + grpc_stream_compression_context_create_identity, + grpc_stream_compression_context_destroy_identity}; diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc index aa05501537..2ea6cf1301 100644 --- a/src/core/lib/iomgr/error.cc +++ b/src/core/lib/iomgr/error.cc @@ -15,9 +15,11 @@ * limitations under the License. * */ +#include #include "src/core/lib/iomgr/error.h" +#include #include #include diff --git a/src/core/lib/iomgr/executor.cc b/src/core/lib/iomgr/executor.cc index 892385d7d7..ebe7f240b4 100644 --- a/src/core/lib/iomgr/executor.cc +++ b/src/core/lib/iomgr/executor.cc @@ -100,7 +100,7 @@ void grpc_executor_set_threading(grpc_exec_ctx *exec_ctx, bool threading) { for (size_t i = 0; i < g_max_threads; i++) { gpr_mu_init(&g_thread_state[i].mu); gpr_cv_init(&g_thread_state[i].cv); - g_thread_state[i].elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; + g_thread_state[i].elems = GRPC_CLOSURE_LIST_INIT; } gpr_thd_options opt = gpr_thd_options_default(); @@ -172,7 +172,7 @@ static void executor_thread(void *arg) { } GRPC_STATS_INC_EXECUTOR_QUEUE_DRAINED(&exec_ctx); grpc_closure_list exec = ts->elems; - ts->elems = (grpc_closure_list)GRPC_CLOSURE_LIST_INIT; + ts->elems = GRPC_CLOSURE_LIST_INIT; gpr_mu_unlock(&ts->mu); if (GRPC_TRACER_ON(executor_trace)) { gpr_log(GPR_DEBUG, "EXECUTOR[%d]: execute", (int)(ts - g_thread_state)); diff --git a/src/core/lib/iomgr/iomgr.cc b/src/core/lib/iomgr/iomgr.cc index f63f190155..3a0605833a 100644 --- a/src/core/lib/iomgr/iomgr.cc +++ b/src/core/lib/iomgr/iomgr.cc @@ -16,8 +16,11 @@ * */ +#include + #include "src/core/lib/iomgr/iomgr.h" +#include #include #include diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc index 10f5594d56..cb274ec771 100644 --- a/src/core/lib/iomgr/tcp_client_windows.cc +++ b/src/core/lib/iomgr/tcp_client_windows.cc @@ -68,7 +68,7 @@ static void async_connect_unlock_and_cleanup(grpc_exec_ctx *exec_ctx, } static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { - async_connect *ac = acp; + async_connect *ac = (async_connect *)acp; gpr_mu_lock(&ac->mu); grpc_winsocket *socket = ac->socket; ac->socket = NULL; @@ -79,7 +79,7 @@ static void on_alarm(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { } static void on_connect(grpc_exec_ctx *exec_ctx, void *acp, grpc_error *error) { - async_connect *ac = acp; + async_connect *ac = (async_connect *)acp; grpc_endpoint **ep = ac->endpoint; GPR_ASSERT(*ep == NULL); grpc_closure *on_done = ac->on_done; @@ -195,7 +195,7 @@ static void tcp_client_connect_impl( } } - ac = gpr_malloc(sizeof(async_connect)); + ac = (async_connect *)gpr_malloc(sizeof(async_connect)); ac->on_done = on_done; ac->socket = socket; gpr_mu_init(&ac->mu); diff --git a/src/core/lib/iomgr/tcp_server_windows.cc b/src/core/lib/iomgr/tcp_server_windows.cc index 2f23c04306..f198aaaa5b 100644 --- a/src/core/lib/iomgr/tcp_server_windows.cc +++ b/src/core/lib/iomgr/tcp_server_windows.cc @@ -98,7 +98,7 @@ grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, grpc_closure *shutdown_complete, const grpc_channel_args *args, grpc_tcp_server **server) { - grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server)); + grpc_tcp_server *s = (grpc_tcp_server *)gpr_malloc(sizeof(grpc_tcp_server)); s->channel_args = grpc_channel_args_copy(args); gpr_ref_init(&s->refs, 1); gpr_mu_init(&s->mu); @@ -116,7 +116,7 @@ grpc_error *grpc_tcp_server_create(grpc_exec_ctx *exec_ctx, static void destroy_server(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_tcp_server *s = arg; + grpc_tcp_server *s = (grpc_tcp_server *)arg; /* Now that the accepts have been aborted, we can destroy the sockets. The IOCP won't get notified on these, so we can flag them as already @@ -189,6 +189,7 @@ static grpc_error *prepare_socket(SOCKET sock, int *port) { grpc_resolved_address sockname_temp; grpc_error *error = GRPC_ERROR_NONE; + int sockname_temp_len; error = grpc_tcp_prepare_socket(sock); if (error != GRPC_ERROR_NONE) { @@ -206,7 +207,7 @@ static grpc_error *prepare_socket(SOCKET sock, goto failure; } - int sockname_temp_len = sizeof(struct sockaddr_storage); + sockname_temp_len = sizeof(struct sockaddr_storage); if (getsockname(sock, (struct sockaddr *)sockname_temp.addr, &sockname_temp_len) == SOCKET_ERROR) { error = GRPC_WSA_ERROR(WSAGetLastError(), "getsockname"); @@ -295,7 +296,7 @@ failure: /* Event manager callback when reads are ready. */ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_tcp_listener *sp = arg; + grpc_tcp_listener *sp = (grpc_tcp_listener *)arg; SOCKET sock = sp->new_socket; grpc_winsocket_callback_info *info = &sp->socket->read_info; grpc_endpoint *ep = NULL; @@ -369,7 +370,8 @@ static void on_accept(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { managed to accept a connection, and created an endpoint. */ if (ep) { // Create acceptor. - grpc_tcp_server_acceptor *acceptor = gpr_malloc(sizeof(*acceptor)); + grpc_tcp_server_acceptor *acceptor = + (grpc_tcp_server_acceptor *)gpr_malloc(sizeof(*acceptor)); acceptor->from_server = sp->server; acceptor->port_index = sp->port_index; acceptor->fd_index = 0; @@ -422,7 +424,7 @@ static grpc_error *add_socket_to_server(grpc_tcp_server *s, SOCKET sock, GPR_ASSERT(port >= 0); gpr_mu_lock(&s->mu); GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server"); - sp = gpr_malloc(sizeof(grpc_tcp_listener)); + sp = (grpc_tcp_listener *)gpr_malloc(sizeof(grpc_tcp_listener)); sp->next = NULL; if (s->head == NULL) { s->head = sp; @@ -473,7 +475,8 @@ grpc_error *grpc_tcp_server_add_port(grpc_tcp_server *s, sockname_temp.len = (size_t)sockname_temp_len; *port = grpc_sockaddr_get_port(&sockname_temp); if (*port > 0) { - allocated_addr = gpr_malloc(sizeof(grpc_resolved_address)); + allocated_addr = (grpc_resolved_address *)gpr_malloc( + sizeof(grpc_resolved_address)); memcpy(allocated_addr, addr, sizeof(grpc_resolved_address)); grpc_sockaddr_set_port(allocated_addr, *port); addr = allocated_addr; diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc index 9f67d8ef22..862eba19f4 100644 --- a/src/core/lib/iomgr/timer_manager.cc +++ b/src/core/lib/iomgr/timer_manager.cc @@ -18,12 +18,13 @@ #include "src/core/lib/iomgr/timer_manager.h" -#include - #include #include +#include #include +#include + #include "src/core/lib/debug/trace.h" #include "src/core/lib/iomgr/timer.h" diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.cc b/src/core/lib/security/credentials/jwt/jwt_credentials.cc index 5e4de4166d..835dd677ed 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.cc +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.cc @@ -16,6 +16,8 @@ * */ +#include + #include "src/core/lib/security/credentials/jwt/jwt_credentials.h" #include diff --git a/src/core/lib/support/string_util_windows.cc b/src/core/lib/support/string_util_windows.cc index d96f57a4b0..f8b9a06c31 100644 --- a/src/core/lib/support/string_util_windows.cc +++ b/src/core/lib/support/string_util_windows.cc @@ -43,7 +43,7 @@ gpr_char_to_tchar(LPCSTR input) { LPTSTR ret; int needed = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0); if (needed <= 0) return NULL; - ret = gpr_malloc((unsigned)needed * sizeof(TCHAR)); + ret = (LPTSTR)gpr_malloc((unsigned)needed * sizeof(TCHAR)); MultiByteToWideChar(CP_UTF8, 0, input, -1, ret, needed); return ret; } @@ -53,7 +53,7 @@ gpr_tchar_to_char(LPCTSTR input) { LPSTR ret; int needed = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL); if (needed <= 0) return NULL; - ret = gpr_malloc((unsigned)needed); + ret = (LPSTR)gpr_malloc((unsigned)needed); WideCharToMultiByte(CP_UTF8, 0, input, -1, ret, needed, NULL, NULL); return ret; } diff --git a/src/core/lib/surface/call.cc b/src/core/lib/surface/call.cc index 4055d656bd..6c97f5cc01 100644 --- a/src/core/lib/surface/call.cc +++ b/src/core/lib/surface/call.cc @@ -808,10 +808,8 @@ static void get_final_status(grpc_call *call, static void set_status_from_error(grpc_exec_ctx *exec_ctx, grpc_call *call, status_source source, grpc_error *error) { if (!gpr_atm_rel_cas(&call->status[source], - pack_received_status((received_status){ - .is_set = false, .error = GRPC_ERROR_NONE}), - pack_received_status((received_status){ - .is_set = true, .error = error}))) { + pack_received_status({false, GRPC_ERROR_NONE}), + pack_received_status({true, error}))) { GRPC_ERROR_UNREF(error); } } diff --git a/src/core/lib/surface/channel.cc b/src/core/lib/surface/channel.cc index 65333cf1af..59fced7bc4 100644 --- a/src/core/lib/surface/channel.cc +++ b/src/core/lib/surface/channel.cc @@ -145,9 +145,8 @@ grpc_channel *grpc_channel_create_with_builder( channel->compression_options.default_level.level = (grpc_compression_level)grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){GRPC_COMPRESS_LEVEL_NONE, - GRPC_COMPRESS_LEVEL_NONE, - GRPC_COMPRESS_LEVEL_COUNT - 1}); + {GRPC_COMPRESS_LEVEL_NONE, GRPC_COMPRESS_LEVEL_NONE, + GRPC_COMPRESS_LEVEL_COUNT - 1}); } else if (0 == strcmp(args->args[i].key, GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_LEVEL)) { channel->compression_options.default_stream_compression_level.is_set = @@ -155,17 +154,15 @@ grpc_channel *grpc_channel_create_with_builder( channel->compression_options.default_stream_compression_level.level = (grpc_stream_compression_level)grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){GRPC_STREAM_COMPRESS_LEVEL_NONE, - GRPC_STREAM_COMPRESS_LEVEL_NONE, - GRPC_STREAM_COMPRESS_LEVEL_COUNT - 1}); + {GRPC_STREAM_COMPRESS_LEVEL_NONE, GRPC_STREAM_COMPRESS_LEVEL_NONE, + GRPC_STREAM_COMPRESS_LEVEL_COUNT - 1}); } else if (0 == strcmp(args->args[i].key, GRPC_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { channel->compression_options.default_algorithm.is_set = true; channel->compression_options.default_algorithm.algorithm = (grpc_compression_algorithm)grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, - GRPC_COMPRESS_ALGORITHMS_COUNT - 1}); + &args->args[i], {GRPC_COMPRESS_NONE, GRPC_COMPRESS_NONE, + GRPC_COMPRESS_ALGORITHMS_COUNT - 1}); } else if (0 == strcmp(args->args[i].key, GRPC_STREAM_COMPRESSION_CHANNEL_DEFAULT_ALGORITHM)) { channel->compression_options.default_stream_compression_algorithm.is_set = @@ -174,9 +171,8 @@ grpc_channel *grpc_channel_create_with_builder( .algorithm = (grpc_stream_compression_algorithm)grpc_channel_arg_get_integer( &args->args[i], - (grpc_integer_options){ - GRPC_STREAM_COMPRESS_NONE, GRPC_STREAM_COMPRESS_NONE, - GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT - 1}); + {GRPC_STREAM_COMPRESS_NONE, GRPC_STREAM_COMPRESS_NONE, + GRPC_STREAM_COMPRESS_ALGORITHMS_COUNT - 1}); } else if (0 == strcmp(args->args[i].key, GRPC_COMPRESSION_CHANNEL_ENABLED_ALGORITHMS_BITSET)) { diff --git a/src/core/lib/surface/completion_queue.cc b/src/core/lib/surface/completion_queue.cc index 91ad2cacd4..453646bd49 100644 --- a/src/core/lib/surface/completion_queue.cc +++ b/src/core/lib/surface/completion_queue.cc @@ -15,6 +15,8 @@ * limitations under the License. * */ +#include + #include "src/core/lib/surface/completion_queue.h" #include @@ -1064,13 +1066,12 @@ static grpc_event cq_pluck(grpc_completion_queue *cq, void *tag, GRPC_CQ_INTERNAL_REF(cq, "pluck"); gpr_mu_lock(cq->mu); cq_is_finished_arg is_finished_arg = { - .last_seen_things_queued_ever = - gpr_atm_no_barrier_load(&cqd->things_queued_ever), - .cq = cq, - .deadline = deadline, - .stolen_completion = NULL, - .tag = tag, - .first_loop = true}; + gpr_atm_no_barrier_load(&cqd->things_queued_ever), + cq, + deadline, + NULL, + tag, + true}; grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER(0, cq_is_pluck_finished, &is_finished_arg); for (;;) { diff --git a/src/core/lib/transport/transport_op_string.cc b/src/core/lib/transport/transport_op_string.cc index 858664715c..87fdf72e29 100644 --- a/src/core/lib/transport/transport_op_string.cc +++ b/src/core/lib/transport/transport_op_string.cc @@ -16,8 +16,11 @@ * */ +#include + #include "src/core/lib/channel/channel_stack.h" +#include #include #include #include -- cgit v1.2.3 From 15ce142d65dc9f46c88457d9da692f5b5bd82a3c Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 25 Sep 2017 17:46:32 -0700 Subject: Reverting pb.c files for now. Also fixing few more build errors --- CMakeLists.txt | 16 ++++++++-------- Makefile | 16 ++++++++-------- binding.gyp | 6 +++--- build.yaml | 8 ++++---- config.m4 | 6 +++--- config.w32 | 6 +++--- gRPC-Core.podspec | 6 +++--- grpc.gemspec | 6 +++--- grpc.gyp | 12 ++++++------ package.xml | 6 +++--- .../ext/transport/chttp2/transport/chttp2_transport.cc | 3 +-- src/core/lib/security/credentials/jwt/json_token.cc | 2 ++ src/core/lib/security/credentials/jwt/json_token.h | 10 +++++----- src/core/lib/security/credentials/jwt/jwt_verifier.cc | 3 +++ src/core/lib/support/string_util_windows.cc | 1 + src/core/lib/surface/alarm.cc | 4 ++++ src/core/tsi/ssl_transport_security.cc | 2 ++ src/core/tsi/ssl_types.h | 8 ++++++++ src/python/grpcio/grpc_core_dependencies.py | 6 +++--- tools/doxygen/Doxyfile.core.internal | 6 +++--- tools/run_tests/generated/sources_and_headers.json | 8 ++++---- 21 files changed, 80 insertions(+), 61 deletions(-) (limited to 'src/core/lib/support') diff --git a/CMakeLists.txt b/CMakeLists.txt index 0475445447..e3a1da0665 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1178,7 +1178,7 @@ add_library(grpc src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c third_party/nanopb/pb_common.c third_party/nanopb/pb_decode.c third_party/nanopb/pb_encode.c @@ -1195,8 +1195,8 @@ add_library(grpc src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc src/core/ext/census/base_resources.cc src/core/ext/census/context.cc - src/core/ext/census/gen/census.pb.cc - src/core/ext/census/gen/trace_context.pb.cc + src/core/ext/census/gen/census.pb.c + src/core/ext/census/gen/trace_context.pb.c src/core/ext/census/grpc_context.cc src/core/ext/census/grpc_filter.cc src/core/ext/census/grpc_plugin.cc @@ -2329,7 +2329,7 @@ add_library(grpc_unsecure src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c third_party/nanopb/pb_common.c third_party/nanopb/pb_decode.c third_party/nanopb/pb_encode.c @@ -2337,8 +2337,8 @@ add_library(grpc_unsecure src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc src/core/ext/census/base_resources.cc src/core/ext/census/context.cc - src/core/ext/census/gen/census.pb.cc - src/core/ext/census/gen/trace_context.pb.cc + src/core/ext/census/gen/census.pb.c + src/core/ext/census/gen/trace_context.pb.c src/core/ext/census/grpc_context.cc src/core/ext/census/grpc_filter.cc src/core/ext/census/grpc_plugin.cc @@ -3045,8 +3045,8 @@ add_library(grpc++_cronet src/core/ext/transport/chttp2/server/chttp2_server.cc src/core/ext/census/base_resources.cc src/core/ext/census/context.cc - src/core/ext/census/gen/census.pb.cc - src/core/ext/census/gen/trace_context.pb.cc + src/core/ext/census/gen/census.pb.c + src/core/ext/census/gen/trace_context.pb.c src/core/ext/census/grpc_context.cc src/core/ext/census/grpc_filter.cc src/core/ext/census/grpc_plugin.cc diff --git a/Makefile b/Makefile index b15570d4c3..9a871b7702 100644 --- a/Makefile +++ b/Makefile @@ -3169,7 +3169,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ @@ -3186,8 +3186,8 @@ LIBGRPC_SRC = \ src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ src/core/ext/census/base_resources.cc \ src/core/ext/census/context.cc \ - src/core/ext/census/gen/census.pb.cc \ - src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/gen/census.pb.c \ + src/core/ext/census/gen/trace_context.pb.c \ src/core/ext/census/grpc_context.cc \ src/core/ext/census/grpc_filter.cc \ src/core/ext/census/grpc_plugin.cc \ @@ -4288,7 +4288,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ @@ -4296,8 +4296,8 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ src/core/ext/census/base_resources.cc \ src/core/ext/census/context.cc \ - src/core/ext/census/gen/census.pb.cc \ - src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/gen/census.pb.c \ + src/core/ext/census/gen/trace_context.pb.c \ src/core/ext/census/grpc_context.cc \ src/core/ext/census/grpc_filter.cc \ src/core/ext/census/grpc_plugin.cc \ @@ -4987,8 +4987,8 @@ LIBGRPC++_CRONET_SRC = \ src/core/ext/transport/chttp2/server/chttp2_server.cc \ src/core/ext/census/base_resources.cc \ src/core/ext/census/context.cc \ - src/core/ext/census/gen/census.pb.cc \ - src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/gen/census.pb.c \ + src/core/ext/census/gen/trace_context.pb.c \ src/core/ext/census/grpc_context.cc \ src/core/ext/census/grpc_filter.cc \ src/core/ext/census/grpc_plugin.cc \ diff --git a/binding.gyp b/binding.gyp index 2608fb1a7e..889b563e39 100644 --- a/binding.gyp +++ b/binding.gyp @@ -880,7 +880,7 @@ 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', @@ -897,8 +897,8 @@ 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', 'src/core/ext/census/base_resources.cc', 'src/core/ext/census/context.cc', - 'src/core/ext/census/gen/census.pb.cc', - 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/gen/census.pb.c', + 'src/core/ext/census/gen/trace_context.pb.c', 'src/core/ext/census/grpc_context.cc', 'src/core/ext/census/grpc_filter.cc', 'src/core/ext/census/grpc_plugin.cc', diff --git a/build.yaml b/build.yaml index 8d610bae74..06b1618f88 100644 --- a/build.yaml +++ b/build.yaml @@ -41,8 +41,8 @@ filegroups: src: - src/core/ext/census/base_resources.cc - src/core/ext/census/context.cc - - src/core/ext/census/gen/census.pb.cc - - src/core/ext/census/gen/trace_context.pb.cc + - src/core/ext/census/gen/census.pb.c + - src/core/ext/census/gen/trace_context.pb.c - src/core/ext/census/grpc_context.cc - src/core/ext/census/grpc_filter.cc - src/core/ext/census/grpc_plugin.cc @@ -550,7 +550,7 @@ filegroups: - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc - - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c plugin: grpc_lb_policy_grpclb uses: - grpc_base @@ -571,7 +571,7 @@ filegroups: - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc - src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc - src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc - - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc + - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c plugin: grpc_lb_policy_grpclb uses: - grpc_base diff --git a/config.m4 b/config.m4 index ff4dcec4a4..5450bbeb1d 100644 --- a/config.m4 +++ b/config.m4 @@ -309,7 +309,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ - src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ + src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ third_party/nanopb/pb_common.c \ third_party/nanopb/pb_decode.c \ third_party/nanopb/pb_encode.c \ @@ -326,8 +326,8 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc \ src/core/ext/census/base_resources.cc \ src/core/ext/census/context.cc \ - src/core/ext/census/gen/census.pb.cc \ - src/core/ext/census/gen/trace_context.pb.cc \ + src/core/ext/census/gen/census.pb.c \ + src/core/ext/census/gen/trace_context.pb.c \ src/core/ext/census/grpc_context.cc \ src/core/ext/census/grpc_filter.cc \ src/core/ext/census/grpc_plugin.cc \ diff --git a/config.w32 b/config.w32 index b710c88781..62b8dcdd76 100644 --- a/config.w32 +++ b/config.w32 @@ -286,7 +286,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_channel_secure.cc " + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\grpclb_client_stats.cc " + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\load_balancer_api.cc " + - "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\proto\\grpc\\lb\\v1\\load_balancer.pb.cc " + + "src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\proto\\grpc\\lb\\v1\\load_balancer.pb.c " + "third_party\\nanopb\\pb_common.c " + "third_party\\nanopb\\pb_decode.c " + "third_party\\nanopb\\pb_encode.c " + @@ -303,8 +303,8 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\load_reporting\\server_load_reporting_plugin.cc " + "src\\core\\ext\\census\\base_resources.cc " + "src\\core\\ext\\census\\context.cc " + - "src\\core\\ext\\census\\gen\\census.pb.cc " + - "src\\core\\ext\\census\\gen\\trace_context.pb.cc " + + "src\\core\\ext\\census\\gen\\census.pb.c " + + "src\\core\\ext\\census\\gen\\trace_context.pb.c " + "src\\core\\ext\\census\\grpc_context.cc " + "src\\core\\ext\\census\\grpc_filter.cc " + "src\\core\\ext\\census\\grpc_plugin.cc " + diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 4f7b00dcfd..e831058ee1 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -694,7 +694,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', 'src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc', 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', @@ -708,8 +708,8 @@ Pod::Spec.new do |s| 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', 'src/core/ext/census/base_resources.cc', 'src/core/ext/census/context.cc', - 'src/core/ext/census/gen/census.pb.cc', - 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/gen/census.pb.c', + 'src/core/ext/census/gen/trace_context.pb.c', 'src/core/ext/census/grpc_context.cc', 'src/core/ext/census/grpc_filter.cc', 'src/core/ext/census/grpc_plugin.cc', diff --git a/grpc.gemspec b/grpc.gemspec index dc441c7eda..98bdbe10e1 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -631,7 +631,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc ) s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc ) s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc ) - s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc ) + s.files += %w( src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c ) s.files += %w( third_party/nanopb/pb_common.c ) s.files += %w( third_party/nanopb/pb_decode.c ) s.files += %w( third_party/nanopb/pb_encode.c ) @@ -648,8 +648,8 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc ) s.files += %w( src/core/ext/census/base_resources.cc ) s.files += %w( src/core/ext/census/context.cc ) - s.files += %w( src/core/ext/census/gen/census.pb.cc ) - s.files += %w( src/core/ext/census/gen/trace_context.pb.cc ) + s.files += %w( src/core/ext/census/gen/census.pb.c ) + s.files += %w( src/core/ext/census/gen/trace_context.pb.c ) s.files += %w( src/core/ext/census/grpc_context.cc ) s.files += %w( src/core/ext/census/grpc_filter.cc ) s.files += %w( src/core/ext/census/grpc_plugin.cc ) diff --git a/grpc.gyp b/grpc.gyp index 264a1f424a..55853a65c9 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -446,7 +446,7 @@ 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', @@ -463,8 +463,8 @@ 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', 'src/core/ext/census/base_resources.cc', 'src/core/ext/census/context.cc', - 'src/core/ext/census/gen/census.pb.cc', - 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/gen/census.pb.c', + 'src/core/ext/census/gen/trace_context.pb.c', 'src/core/ext/census/grpc_context.cc', 'src/core/ext/census/grpc_filter.cc', 'src/core/ext/census/grpc_plugin.cc', @@ -1115,7 +1115,7 @@ 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', @@ -1123,8 +1123,8 @@ 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', 'src/core/ext/census/base_resources.cc', 'src/core/ext/census/context.cc', - 'src/core/ext/census/gen/census.pb.cc', - 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/gen/census.pb.c', + 'src/core/ext/census/gen/trace_context.pb.c', 'src/core/ext/census/grpc_context.cc', 'src/core/ext/census/grpc_filter.cc', 'src/core/ext/census/grpc_plugin.cc', diff --git a/package.xml b/package.xml index bbe2a10edd..0656c457d2 100644 --- a/package.xml +++ b/package.xml @@ -643,7 +643,7 @@ - + @@ -660,8 +660,8 @@ - - + + diff --git a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc index d6a9aab7d6..383da42bb4 100644 --- a/src/core/ext/transport/chttp2/transport/chttp2_transport.cc +++ b/src/core/ext/transport/chttp2/transport/chttp2_transport.cc @@ -2648,8 +2648,7 @@ void grpc_chttp2_config_default_keepalive_args(grpc_channel_args *args, } else if (0 == strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PING_STRIKES)) { g_default_max_ping_strikes = grpc_channel_arg_get_integer( - &args->args[i], - (grpc_integer_options){g_default_max_ping_strikes, 0, INT_MAX}); + &args->args[i], {g_default_max_ping_strikes, 0, INT_MAX}); } else if (0 == strcmp(args->args[i].key, GRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA)) { g_default_max_pings_without_data = grpc_channel_arg_get_integer( diff --git a/src/core/lib/security/credentials/jwt/json_token.cc b/src/core/lib/security/credentials/jwt/json_token.cc index 1f5cc7059d..8c30353470 100644 --- a/src/core/lib/security/credentials/jwt/json_token.cc +++ b/src/core/lib/security/credentials/jwt/json_token.cc @@ -30,9 +30,11 @@ #include "src/core/lib/slice/b64.h" #include "src/core/lib/support/string.h" +extern "C" { #include #include #include +} /* --- Constants. --- */ diff --git a/src/core/lib/security/credentials/jwt/json_token.h b/src/core/lib/security/credentials/jwt/json_token.h index 486fd981af..b923b02df6 100644 --- a/src/core/lib/security/credentials/jwt/json_token.h +++ b/src/core/lib/security/credentials/jwt/json_token.h @@ -19,6 +19,10 @@ #ifndef GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H #define GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include @@ -28,10 +32,6 @@ #define GRPC_JWT_OAUTH2_AUDIENCE "https://www.googleapis.com/oauth2/v3/token" -#ifdef __cplusplus -extern "C" { -#endif - /* --- auth_json_key parsing. --- */ typedef struct { @@ -78,4 +78,4 @@ void grpc_jwt_encode_and_sign_set_override( } #endif -#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */ \ No newline at end of file +#endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_JWT_JSON_TOKEN_H */ diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.cc b/src/core/lib/security/credentials/jwt/jwt_verifier.cc index 656d0920cd..aea16dee92 100644 --- a/src/core/lib/security/credentials/jwt/jwt_verifier.cc +++ b/src/core/lib/security/credentials/jwt/jwt_verifier.cc @@ -26,7 +26,10 @@ #include #include #include + +extern "C" { #include +} #include "src/core/lib/http/httpcli.h" #include "src/core/lib/iomgr/polling_entity.h" diff --git a/src/core/lib/support/string_util_windows.cc b/src/core/lib/support/string_util_windows.cc index f8b9a06c31..f170669391 100644 --- a/src/core/lib/support/string_util_windows.cc +++ b/src/core/lib/support/string_util_windows.cc @@ -36,6 +36,7 @@ #include #include "src/core/lib/support/string.h" +#include "src/core/lib/support/string_windows.h" #if defined UNICODE || defined _UNICODE LPTSTR diff --git a/src/core/lib/surface/alarm.cc b/src/core/lib/surface/alarm.cc index 7712f560b9..4e67543191 100644 --- a/src/core/lib/surface/alarm.cc +++ b/src/core/lib/surface/alarm.cc @@ -15,6 +15,10 @@ * limitations under the License. * */ +#include + +#include + #include "src/core/lib/surface/alarm_internal.h" #include diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc index dd59bccdae..b1c69e9c7b 100644 --- a/src/core/tsi/ssl_transport_security.cc +++ b/src/core/tsi/ssl_transport_security.cc @@ -39,12 +39,14 @@ #include #include +extern "C" { #include #include /* For OPENSSL_free */ #include #include #include #include +} #include "src/core/tsi/ssl_types.h" #include "src/core/tsi/transport_security.h" diff --git a/src/core/tsi/ssl_types.h b/src/core/tsi/ssl_types.h index 3788643355..e0e967034b 100644 --- a/src/core/tsi/ssl_types.h +++ b/src/core/tsi/ssl_types.h @@ -19,6 +19,10 @@ #ifndef GRPC_CORE_TSI_SSL_TYPES_H #define GRPC_CORE_TSI_SSL_TYPES_H +#ifdef __cplusplus +extern "C" { +#endif + /* A collection of macros to cast between various integer types that are * used differently between BoringSSL and OpenSSL: * TSI_INT_AS_SIZE(x): convert 'int x' to a length parameter for an OpenSSL @@ -37,4 +41,8 @@ #define TSI_SIZE_AS_SIZE(x) ((int)(x)) #endif +#ifdef __cplusplus +} +#endif + #endif /* GRPC_CORE_TSI_SSL_TYPES_H */ diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 5d3251c884..2955d16be6 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -285,7 +285,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_channel_secure.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc', 'src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc', - 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc', + 'src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c', 'third_party/nanopb/pb_common.c', 'third_party/nanopb/pb_decode.c', 'third_party/nanopb/pb_encode.c', @@ -302,8 +302,8 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/load_reporting/server_load_reporting_plugin.cc', 'src/core/ext/census/base_resources.cc', 'src/core/ext/census/context.cc', - 'src/core/ext/census/gen/census.pb.cc', - 'src/core/ext/census/gen/trace_context.pb.cc', + 'src/core/ext/census/gen/census.pb.c', + 'src/core/ext/census/gen/trace_context.pb.c', 'src/core/ext/census/grpc_context.cc', 'src/core/ext/census/grpc_filter.cc', 'src/core/ext/census/grpc_plugin.cc', diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index b6ea3c2eb7..d4c645e728 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -878,9 +878,9 @@ src/core/ext/census/census_interface.h \ src/core/ext/census/census_rpc_stats.h \ src/core/ext/census/context.cc \ src/core/ext/census/gen/README.md \ -src/core/ext/census/gen/census.pb.cc \ +src/core/ext/census/gen/census.pb.c \ src/core/ext/census/gen/census.pb.h \ -src/core/ext/census/gen/trace_context.pb.cc \ +src/core/ext/census/gen/trace_context.pb.c \ src/core/ext/census/gen/trace_context.pb.h \ src/core/ext/census/grpc_context.cc \ src/core/ext/census/grpc_filter.cc \ @@ -930,7 +930,7 @@ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h \ src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc \ src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h \ -src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc \ +src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c \ src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h \ src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index f9cb67917c..d4c09eeddf 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -7727,9 +7727,9 @@ "src/core/ext/census/census_interface.h", "src/core/ext/census/census_rpc_stats.h", "src/core/ext/census/context.cc", - "src/core/ext/census/gen/census.pb.cc", + "src/core/ext/census/gen/census.pb.c", "src/core/ext/census/gen/census.pb.h", - "src/core/ext/census/gen/trace_context.pb.cc", + "src/core/ext/census/gen/trace_context.pb.c", "src/core/ext/census/gen/trace_context.pb.h", "src/core/ext/census/grpc_context.cc", "src/core/ext/census/grpc_filter.cc", @@ -8579,7 +8579,7 @@ "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" ], "third_party": false, @@ -8616,7 +8616,7 @@ "src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb_client_stats.h", "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.cc", "src/core/ext/filters/client_channel/lb_policy/grpclb/load_balancer_api.h", - "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.cc", + "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c", "src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.h" ], "third_party": false, -- cgit v1.2.3 From 547653ebdb3cc16e3c0fb65383322bc7dacc9b90 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 25 Sep 2017 21:20:19 -0700 Subject: some build changes, some unresolved dependencies --- build.yaml | 1 + .../grpclb/proto/grpc/lb/v1/load_balancer.pb.c | 21 ++------------------- .../ext/transport/chttp2/transport/stream_lists.cc | 1 + src/core/lib/iomgr/tcp_windows.cc | 1 + src/core/lib/support/cpu_iphone.cc | 2 ++ src/core/lib/support/cpu_posix.cc | 1 + src/core/lib/support/cpu_windows.cc | 1 + src/core/lib/support/string_util_windows.cc | 1 + src/core/lib/support/wrap_memcpy.cc | 2 ++ src/core/lib/transport/static_metadata.h | 7 +++++++ tools/codegen/core/gen_static_metadata.py | 8 ++++++++ tools/run_tests/generated/sources_and_headers.json | 1 + 12 files changed, 28 insertions(+), 19 deletions(-) (limited to 'src/core/lib/support') diff --git a/build.yaml b/build.yaml index 06b1618f88..0f411f8645 100644 --- a/build.yaml +++ b/build.yaml @@ -763,6 +763,7 @@ filegroups: - gpr filegroups: - grpc_trace_headers + - grpc_base_headers - name: grpc_trace_headers headers: - src/core/lib/debug/trace.h diff --git a/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c b/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c index dd0ccc8476..6a5d54c82a 100644 --- a/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c +++ b/src/core/ext/filters/client_channel/lb_policy/grpclb/proto/grpc/lb/v1/load_balancer.pb.c @@ -1,20 +1,3 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ /* Automatically generated nanopb constant definitions */ /* Generated by nanopb-0.3.7-dev */ @@ -97,7 +80,7 @@ const pb_field_t grpc_lb_v1_Server_fields[5] = { #if !defined(PB_FIELD_32BIT) /* If you get an error here, it means that you need to define PB_FIELD_32BIT * compile-time option. You can do that in pb.h or on compiler command line. - * + * * The reason you need to do this is that some of your messages contain tag * numbers or field sizes that are larger than what can fit in 8 or 16 bit * field descriptors. @@ -108,7 +91,7 @@ PB_STATIC_ASSERT((pb_membersize(grpc_lb_v1_LoadBalanceRequest, initial_request) #if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) /* If you get an error here, it means that you need to define PB_FIELD_16BIT * compile-time option. You can do that in pb.h or on compiler command line. - * + * * The reason you need to do this is that some of your messages contain tag * numbers or field sizes that are larger than what can fit in the default * 8 bit descriptors. diff --git a/src/core/ext/transport/chttp2/transport/stream_lists.cc b/src/core/ext/transport/chttp2/transport/stream_lists.cc index 34f62aef84..9f731a397f 100644 --- a/src/core/ext/transport/chttp2/transport/stream_lists.cc +++ b/src/core/ext/transport/chttp2/transport/stream_lists.cc @@ -16,6 +16,7 @@ * */ +#include "src/core/ext/transport/chttp2/transport/chttp2_transport.h" #include "src/core/ext/transport/chttp2/transport/internal.h" #include diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc index d3659a077f..daf15b4944 100644 --- a/src/core/lib/iomgr/tcp_windows.cc +++ b/src/core/lib/iomgr/tcp_windows.cc @@ -37,6 +37,7 @@ #include "src/core/lib/iomgr/sockaddr_utils.h" #include "src/core/lib/iomgr/socket_windows.h" #include "src/core/lib/iomgr/tcp_client.h" +#include "src/core/lib/iomgr/tcp_windows.h" #include "src/core/lib/iomgr/timer.h" #include "src/core/lib/slice/slice_internal.h" diff --git a/src/core/lib/support/cpu_iphone.cc b/src/core/lib/support/cpu_iphone.cc index dfd69b9fd8..2847e03ba5 100644 --- a/src/core/lib/support/cpu_iphone.cc +++ b/src/core/lib/support/cpu_iphone.cc @@ -18,6 +18,8 @@ #include +#include + #ifdef GPR_CPU_IPHONE /* Probably 2 instead of 1, but see comment on gpr_cpu_current_cpu. */ diff --git a/src/core/lib/support/cpu_posix.cc b/src/core/lib/support/cpu_posix.cc index a1ba8202a8..503a96b4c8 100644 --- a/src/core/lib/support/cpu_posix.cc +++ b/src/core/lib/support/cpu_posix.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/src/core/lib/support/cpu_windows.cc b/src/core/lib/support/cpu_windows.cc index af26ff3679..8d89453403 100644 --- a/src/core/lib/support/cpu_windows.cc +++ b/src/core/lib/support/cpu_windows.cc @@ -19,6 +19,7 @@ #include #ifdef GPR_WINDOWS +#include #include unsigned gpr_cpu_num_cores(void) { diff --git a/src/core/lib/support/string_util_windows.cc b/src/core/lib/support/string_util_windows.cc index f170669391..86e19527a7 100644 --- a/src/core/lib/support/string_util_windows.cc +++ b/src/core/lib/support/string_util_windows.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include "src/core/lib/support/string.h" diff --git a/src/core/lib/support/wrap_memcpy.cc b/src/core/lib/support/wrap_memcpy.cc index cff056dc3b..c2362bf5b8 100644 --- a/src/core/lib/support/wrap_memcpy.cc +++ b/src/core/lib/support/wrap_memcpy.cc @@ -26,6 +26,7 @@ * Enable by setting LDFLAGS=-Wl,-wrap,memcpy when linking. */ +extern "C" { #ifdef __linux__ #if defined(__x86_64__) && !defined(GPR_MUSL_LIBC_COMPAT) __asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); @@ -38,3 +39,4 @@ void *__wrap_memcpy(void *destination, const void *source, size_t num) { } #endif #endif +} diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h index f03a9d23b1..299410f22c 100644 --- a/src/core/lib/transport/static_metadata.h +++ b/src/core/lib/transport/static_metadata.h @@ -27,6 +27,10 @@ #ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H #define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H +#ifdef __cplusplus +extern "C" { +#endif + #include "src/core/lib/transport/metadata.h" #define GRPC_STATIC_MDSTR_COUNT 100 @@ -584,4 +588,7 @@ extern const uint8_t grpc_static_accept_stream_encoding_metadata[4]; (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table \ [grpc_static_accept_stream_encoding_metadata[(algs)]], \ GRPC_MDELEM_STORAGE_STATIC)) +#ifdef __cplusplus +} +#endif #endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */ diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py index 0833204e19..355f3f4e23 100755 --- a/tools/codegen/core/gen_static_metadata.py +++ b/tools/codegen/core/gen_static_metadata.py @@ -354,6 +354,10 @@ an explanation of what's going on. print >> H, '#ifndef GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H' print >> H, '#define GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H' print >> H +print >> H, '#ifdef __cplusplus' +print >> H, 'extern "C" {' +print >> H, '#endif' +print >> H print >> H, '#include "src/core/lib/transport/metadata.h"' print >> H @@ -589,6 +593,10 @@ print >> C, '};' print >> H, '#define GRPC_MDELEM_ACCEPT_STREAM_ENCODING_FOR_ALGORITHMS(algs) (GRPC_MAKE_MDELEM(&grpc_static_mdelem_table[grpc_static_accept_stream_encoding_metadata[(algs)]], GRPC_MDELEM_STORAGE_STATIC))' +print >> H, '#ifdef __cplusplus' +print >> H, '}' +print >> H, '#endif' + print >> H, '#endif /* GRPC_CORE_LIB_TRANSPORT_STATIC_METADATA_H */' H.close() diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index d4c09eeddf..b23c5d7bc9 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -8951,6 +8951,7 @@ { "deps": [ "gpr", + "grpc_base_headers", "grpc_trace_headers" ], "headers": [], -- cgit v1.2.3 From 0362c60fde326856e95496091dc94b2d1815fec1 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Mon, 25 Sep 2017 22:35:21 -0700 Subject: Windows definition/declaration bugs --- src/core/ext/transport/chttp2/transport/hpack_encoder.cc | 2 +- src/core/ext/transport/chttp2/transport/hpack_table.cc | 2 +- src/core/lib/compression/stream_compression.cc | 2 +- src/core/lib/iomgr/tcp_windows.cc | 2 +- src/core/lib/support/string_util_windows.cc | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc index a404b664e3..5f1a2708a5 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_encoder.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_encoder.cc @@ -57,7 +57,7 @@ static const grpc_slice terminal_slice = { {{0, 0}} /* data.refcounted */ }; -extern grpc_tracer_flag grpc_http_trace; +extern "C" grpc_tracer_flag grpc_http_trace; typedef struct { int is_first_frame; diff --git a/src/core/ext/transport/chttp2/transport/hpack_table.cc b/src/core/ext/transport/chttp2/transport/hpack_table.cc index bbd135a318..82c284b36e 100644 --- a/src/core/ext/transport/chttp2/transport/hpack_table.cc +++ b/src/core/ext/transport/chttp2/transport/hpack_table.cc @@ -28,7 +28,7 @@ #include "src/core/lib/debug/trace.h" #include "src/core/lib/support/murmur_hash.h" -extern grpc_tracer_flag grpc_http_trace; +extern "C" grpc_tracer_flag grpc_http_trace; static struct { const char *key; diff --git a/src/core/lib/compression/stream_compression.cc b/src/core/lib/compression/stream_compression.cc index 411489f029..7faeb0d34f 100644 --- a/src/core/lib/compression/stream_compression.cc +++ b/src/core/lib/compression/stream_compression.cc @@ -21,7 +21,7 @@ #include "src/core/lib/compression/stream_compression.h" #include "src/core/lib/compression/stream_compression_gzip.h" -extern const grpc_stream_compression_vtable +extern "C" const grpc_stream_compression_vtable grpc_stream_compression_identity_vtable; bool grpc_stream_compress(grpc_stream_compression_context *ctx, diff --git a/src/core/lib/iomgr/tcp_windows.cc b/src/core/lib/iomgr/tcp_windows.cc index daf15b4944..dc84e564a9 100644 --- a/src/core/lib/iomgr/tcp_windows.cc +++ b/src/core/lib/iomgr/tcp_windows.cc @@ -419,7 +419,7 @@ static grpc_endpoint_vtable vtable = { grpc_endpoint *grpc_tcp_create(grpc_exec_ctx *exec_ctx, grpc_winsocket *socket, grpc_channel_args *channel_args, - char *peer_string) { + const char *peer_string) { grpc_resource_quota *resource_quota = grpc_resource_quota_create(NULL); if (channel_args != NULL) { for (size_t i = 0; i < channel_args->num_args; i++) { diff --git a/src/core/lib/support/string_util_windows.cc b/src/core/lib/support/string_util_windows.cc index 86e19527a7..b365512ee3 100644 --- a/src/core/lib/support/string_util_windows.cc +++ b/src/core/lib/support/string_util_windows.cc @@ -60,9 +60,9 @@ gpr_tchar_to_char(LPCTSTR input) { return ret; } #else -char *gpr_tchar_to_char(LPTSTR input) { return gpr_strdup(input); } +LPSTR gpr_tchar_to_char(LPCTSTR input) { return (LPSTR)gpr_strdup(input); } -char *gpr_char_to_tchar(LPTSTR input) { return gpr_strdup(input); } +LPTSTR gpr_char_to_tchar(LPCTSTR input) { return (LPTSTR)gpr_strdup(input); } #endif char *gpr_format_message(int messageid) { -- cgit v1.2.3 From f5b4f80e5cd5021bfbea3376b64ab54bbf8e4980 Mon Sep 17 00:00:00 2001 From: Yash Tibrewal Date: Tue, 26 Sep 2017 00:07:41 -0700 Subject: Windows build errors --- src/core/lib/iomgr/ev_epoll1_linux.cc | 4 ++-- src/core/lib/iomgr/tcp_client_posix.cc | 2 ++ src/core/lib/iomgr/tcp_client_uv.cc | 2 ++ src/core/lib/iomgr/tcp_client_windows.cc | 2 ++ src/core/lib/iomgr/timer_generic.cc | 2 ++ src/core/lib/iomgr/timer_manager.cc | 2 +- src/core/lib/iomgr/timer_uv.cc | 2 ++ src/core/lib/support/time_posix.cc | 2 ++ src/core/lib/support/time_windows.cc | 2 ++ 9 files changed, 17 insertions(+), 3 deletions(-) (limited to 'src/core/lib/support') diff --git a/src/core/lib/iomgr/ev_epoll1_linux.cc b/src/core/lib/iomgr/ev_epoll1_linux.cc index 3ac12ab56f..245fd950e2 100644 --- a/src/core/lib/iomgr/ev_epoll1_linux.cc +++ b/src/core/lib/iomgr/ev_epoll1_linux.cc @@ -18,11 +18,11 @@ #include "src/core/lib/iomgr/port.h" +#include "src/core/lib/iomgr/ev_epoll1_linux.h" + /* This polling engine is only relevant on linux kernels supporting epoll() */ #ifdef GRPC_LINUX_EPOLL -#include "src/core/lib/iomgr/ev_epoll1_linux.h" - #include #include #include diff --git a/src/core/lib/iomgr/tcp_client_posix.cc b/src/core/lib/iomgr/tcp_client_posix.cc index 39dbb506e2..7d9e9533fd 100644 --- a/src/core/lib/iomgr/tcp_client_posix.cc +++ b/src/core/lib/iomgr/tcp_client_posix.cc @@ -337,11 +337,13 @@ done: } // overridden by api_fuzzer.c +extern "C" { void (*grpc_tcp_client_connect_impl)( grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, const grpc_resolved_address *addr, gpr_timespec deadline) = tcp_client_connect_impl; +} void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, diff --git a/src/core/lib/iomgr/tcp_client_uv.cc b/src/core/lib/iomgr/tcp_client_uv.cc index b1a301e378..274366b356 100644 --- a/src/core/lib/iomgr/tcp_client_uv.cc +++ b/src/core/lib/iomgr/tcp_client_uv.cc @@ -164,11 +164,13 @@ static void tcp_client_connect_impl(grpc_exec_ctx *exec_ctx, } // overridden by api_fuzzer.c +extern "C" { void (*grpc_tcp_client_connect_impl)( grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, const grpc_resolved_address *addr, gpr_timespec deadline) = tcp_client_connect_impl; +} void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, diff --git a/src/core/lib/iomgr/tcp_client_windows.cc b/src/core/lib/iomgr/tcp_client_windows.cc index cb274ec771..1154965c82 100644 --- a/src/core/lib/iomgr/tcp_client_windows.cc +++ b/src/core/lib/iomgr/tcp_client_windows.cc @@ -228,11 +228,13 @@ failure: } // overridden by api_fuzzer.c +extern "C" { void (*grpc_tcp_client_connect_impl)( grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, grpc_pollset_set *interested_parties, const grpc_channel_args *channel_args, const grpc_resolved_address *addr, gpr_timespec deadline) = tcp_client_connect_impl; +} void grpc_tcp_client_connect(grpc_exec_ctx *exec_ctx, grpc_closure *closure, grpc_endpoint **ep, diff --git a/src/core/lib/iomgr/timer_generic.cc b/src/core/lib/iomgr/timer_generic.cc index 91e227d241..971d80d8bc 100644 --- a/src/core/lib/iomgr/timer_generic.cc +++ b/src/core/lib/iomgr/timer_generic.cc @@ -43,9 +43,11 @@ #define MIN_QUEUE_WINDOW_DURATION 0.01 #define MAX_QUEUE_WINDOW_DURATION 1 +extern "C" { grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer"); grpc_tracer_flag grpc_timer_check_trace = GRPC_TRACER_INITIALIZER(false, "timer_check"); +} /* A "timer shard". Contains a 'heap' and a 'list' of timers. All timers with * deadlines earlier than 'queue_deadline" cap are maintained in the heap and diff --git a/src/core/lib/iomgr/timer_manager.cc b/src/core/lib/iomgr/timer_manager.cc index 862eba19f4..9b54fab898 100644 --- a/src/core/lib/iomgr/timer_manager.cc +++ b/src/core/lib/iomgr/timer_manager.cc @@ -33,7 +33,7 @@ typedef struct completed_thread { struct completed_thread *next; } completed_thread; -extern grpc_tracer_flag grpc_timer_check_trace; +extern "C" grpc_tracer_flag grpc_timer_check_trace; // global mutex static gpr_mu g_mu; diff --git a/src/core/lib/iomgr/timer_uv.cc b/src/core/lib/iomgr/timer_uv.cc index d53fbf423c..53f79b545a 100644 --- a/src/core/lib/iomgr/timer_uv.cc +++ b/src/core/lib/iomgr/timer_uv.cc @@ -29,9 +29,11 @@ #include +extern "C" { grpc_tracer_flag grpc_timer_trace = GRPC_TRACER_INITIALIZER(false, "timer"); grpc_tracer_flag grpc_timer_check_trace = GRPC_TRACER_INITIALIZER(false, "timer_check"); +} static void timer_close_callback(uv_handle_t *handle) { gpr_free(handle); } diff --git a/src/core/lib/support/time_posix.cc b/src/core/lib/support/time_posix.cc index 3ead40d807..deccb50975 100644 --- a/src/core/lib/support/time_posix.cc +++ b/src/core/lib/support/time_posix.cc @@ -128,7 +128,9 @@ static gpr_timespec now_impl(gpr_clock_type clock) { } #endif +extern "C" { gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; +} #ifdef GPR_LOW_LEVEL_COUNTERS gpr_atm gpr_now_call_count; diff --git a/src/core/lib/support/time_windows.cc b/src/core/lib/support/time_windows.cc index 40df3761c0..dda7566cd8 100644 --- a/src/core/lib/support/time_windows.cc +++ b/src/core/lib/support/time_windows.cc @@ -69,7 +69,9 @@ static gpr_timespec now_impl(gpr_clock_type clock) { return now_tv; } +extern "C" { gpr_timespec (*gpr_now_impl)(gpr_clock_type clock_type) = now_impl; +} gpr_timespec gpr_now(gpr_clock_type clock_type) { return gpr_now_impl(clock_type); -- cgit v1.2.3